Updates to – Putting Entity framework 4 to use in a business architecture

This post only reflects some updates to my previous article: Putting Entity framework 4 to use in a business architecture.

I made a “minor” error in my EfDynamicProxyAssemblies that is consumed by my EfDataContractSerializer. The error which I know have corrected arised when I added an example to the client application, where I intended to read back the created wishlist(s) for a certain username. What happened was that the service used the overload of the EfEntityStore’s Query method that lets you specify string’s for memebers in the objectgraph to include in the select. The generated proxie than contained three entities: Wishlist, UserAccount and Wish, but my EfDynamicProxyAssemblies cleared the cached assemblies once the types were extracted. And the first time the dynamic proxie assembly was executed, the only contained type was UserAccount, hence WishList and Wish didn’t get extracted and registrered as a known type.

I have corrected the code and added some information about the custom EfDataContractSerializer attribute in the article.

Download the pdf

The complete code example can be found here.

I have also added some information about “eager loading”. So there is some new code and two new sub chapters.

Enjoy!

//Daniel

Putting Entity framework 4 to use in a business architecture

Updates have been made since version 1 of this article!

“>Read more about it here.

Finally I’m finished. I have been struggling with an intended sum-up of my latest post using Entity framework 4. It ended up with an document of about 40 pages. There is a lot of code in there so the number of pages could be a bit missleading (which of course depends on what you are seeking).

Download the pdf

The complete code example can be found here.

As always….. Have fun with it!

//Daniel

WCF DataContractSerializer problems with dynamic proxies in Entity framework 4

This article was produced for Beta 2 & CTP2

Download the code

I were having trouble with serialization of my entities over WCF using net.tcp binding when letting the ObjectContext create dynamic proxies. To solve the problem I hade to extend the DataContractSerializerOperationBehavior and provide it with the dynamic proxies as “Known types”.

I also had to create a custom attribute that I could mark my methods (service operations) so that my custom DataContractSerializerOperationBehavior is invoked “only when needed”.

It was as simple as that. I also created an class that listens for the AppDomain.AssemblyLoad event and keeps track of the dynamic proxy types.

So if you are having these problems, feel free to use the code.

You also have to decorate your entities with the Serializable- and DataContract attribute with IsReference=true. Also you have to mark the members that should be serialized in your entites with the DataMember attribute.

The Interface that defines my service
I only mark the methods that uses entities that has a dynamic proxy with my custom EfDataContractSerializer attribute.

[ServiceContract]
public interface ISecurityService : IService
{
    [OperationContract]
    [EfDataContractSerializer]
    ServiceResponse<UserAccount> SetupNewUserAccount(UserAccount userAccount);

    [OperationContract]
    [EfDataContractSerializer]
    ServiceResponse<UserAccount> GetUserAccountByCredentials(string username, string password);

    [OperationContract]
    [EfDataContractSerializer]
    ServiceResponse UpdateUserAccount(UserAccount userAccount);

    [OperationContract]
    ServiceResponse SetNewPassword(string username, string currentPassword, string newPassword);
}

EfDataContractSerializerAttribute – For decorationg methods in your service

[Serializable]
public class EfDataContractSerializerAttribute : Attribute, IOperationBehavior
{
    private readonly EfDataContractSerializerOperationBehavior _innerOperationBehavior;
    
    public EfDataContractSerializerAttribute()
    {
    }

    public EfDataContractSerializerAttribute(OperationDescription operation)
    {
        _innerOperationBehavior = new EfDataContractSerializerOperationBehavior(operation);
    }

    public EfDataContractSerializerAttribute(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
    {
        _innerOperationBehavior = new EfDataContractSerializerOperationBehavior(operation, dataContractFormatAttribute);
    }

    public void Validate(OperationDescription operationDescription)
    {
        if (_innerOperationBehavior == null)
            return;

        (_innerOperationBehavior as IOperationBehavior).Validate(operationDescription);
    }

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
    {
        ReplaceDataContractSerializerOperationBehavior(operationDescription);
    }

    public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
    {
        ReplaceDataContractSerializerOperationBehavior(operationDescription);
    }

    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
    {
        if (_innerOperationBehavior == null)
            return;

        (_innerOperationBehavior as IOperationBehavior).AddBindingParameters(operationDescription, bindingParameters);
    }

    private void ReplaceDataContractSerializerOperationBehavior(OperationDescription description)
    {
        var operationBehavior = description.Behaviors.Find<DataContractSerializerOperationBehavior>();

        if (operationBehavior == null)
            return;

        description.Behaviors.Remove(operationBehavior);
        description.Behaviors.Add(new EfDataContractSerializerOperationBehavior(description));
    }
}

EfDataContractSerializerOperationBehavior – For populating the DataContractSerializer with known types

public class EfDataContractSerializerOperationBehavior : DataContractSerializerOperationBehavior
{
    public EfDataContractSerializerOperationBehavior(OperationDescription operation)
        : base(operation)
    {
    }

    public EfDataContractSerializerOperationBehavior(OperationDescription operation, DataContractFormatAttribute dataContractFormatAttribute)
        : base(operation, dataContractFormatAttribute)
    {
    }

    public override XmlObjectSerializer CreateSerializer(Type type, string name, string ns, IList<Type> knownTypes)
    {
        return new DataContractSerializer(type, name, ns, MergeKnownTypesWithDynamicProxyTypes(knownTypes));
    }

    public override XmlObjectSerializer CreateSerializer(Type type, XmlDictionaryString name, XmlDictionaryString ns, IList<Type> knownTypes)
    {
        return new DataContractSerializer(type, name, ns, MergeKnownTypesWithDynamicProxyTypes(knownTypes));
    }

    private static IEnumerable<Type> MergeKnownTypesWithDynamicProxyTypes(IEnumerable<Type> knownTypes)
    {
        var tmp = new HashSet<Type>();

        if (knownTypes != null)
            tmp.UnionWith(knownTypes);

        tmp.UnionWith(EfDynamicProxyAssemblies.GetTypes());

        return tmp;
    }
}

EfDynamicProxyAssemblies – For keeping track of currently generated dynamic proxies

public static class EfDynamicProxyAssemblies
{
    private static readonly object _lock = new object();
    private static readonly Dictionary<string, Assembly> _loadedAssemblies = new Dictionary<string, Assembly>();
    private static readonly HashSet<Assembly> _assembliesToExtract = new HashSet<Assembly>();
    private static readonly List<Type> _dynamixProxies = new List<Type>();

    static EfDynamicProxyAssemblies()
    {
        AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
    }

    private static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
    {
        if (args == null || args.LoadedAssembly == null || string.IsNullOrEmpty(args.LoadedAssembly.FullName))
            return;

        lock (_lock)
        {
            if ((args.LoadedAssembly.FullName).StartsWith("EntityFrameworkDynamicProxies") &&
                !_loadedAssemblies.ContainsKey(args.LoadedAssembly.FullName))
            {
                _loadedAssemblies.Add(args.LoadedAssembly.FullName, args.LoadedAssembly);
                _assembliesToExtract.Add(args.LoadedAssembly);
                //Types aren't generated yet, so we can't add them to _dynamixEntityProxies
            }
        }
    }

    public static IEnumerable<Type> GetTypes()
    {
        lock(_lock)
        {
            var types = _assembliesToExtract.SelectMany(a => a.GetTypes()).ToList();
            _dynamixProxies.AddRange(types);

            _assembliesToExtract.Clear();
        }

        return _dynamixProxies;
    }
}

Enjoy!

//Daniel