Monday, August 15, 2011

Ensuring the the entities object graph returned by Entity Framework is not of proxy type

I got a nasty error in my repository interface that I debugged for about three days. I noticed that the cloning procedure sometimes get proxy objects when the unit tests are run together (the cloning procedure failed), though when run individually each of them passed the unit test.

It turns out the cause of the problem is when the repository interface get objects from GetCascade repository interface, sometimes it return object proxies. Though the DbContext query in GetCascade returns the real object type when the unit test is run individually(I have this.Configuration.ProxyCreationEnabled = false; on OnModelCreating), some of the objects that are returned when the unit tests are run together are of the proxy type. And it turns out that ProxyCreationEnabled are being reset back to true when the unit tests are run together. How this quirk happens is beyond me, so the interim solution is to set again the ProxyCreationEnabled to false to ensure the objects returned by EF's Linq are not of proxy type.

public TEnt GetCascade(object pkValue)
{
    // don't want proxy objects here :-)
    
    bool oldValue = _ctx.Configuration.ProxyCreationEnabled;
    _ctx.Configuration.ProxyCreationEnabled = false;
     

    Type entType = typeof(TEnt);
    var query = _ctx.Set<TEnt>().AsNoTracking().Where(string.Format("{0} = @0", PrimaryKeyName), pkValue);
    if (!_ctx.Configuration.ProxyCreationEnabled)
    {
        /* e.g.
        var query = new EfRepository<Question>().All.Where(x => x.QuestionId == id);
        query = query.Include("Answers");
        query = query.Include("Answers.Comments");
        query = query.Include("Comments");*/

        foreach (string path in GetCollectionPaths(entType))
            query = query.Include(path);

    }


    var r = query.Single();


    _ctx.Configuration.ProxyCreationEnabled = oldValue;
    

    return r;
}

IEnumerable<string> GetCollectionPaths(Type root)
{

    foreach (PropertyInfo pi in root.GetProperties().Where(x => x.PropertyType.IsGenericType && typeof(IEnumerable).IsAssignableFrom(x.PropertyType)))
    {

        yield return pi.Name;

        // ICollection<listElemType> p; IList derives from ICollection, just use the base interface
        Type listElemType =
            pi.PropertyType.GetInterfaces().Where(x =>
            x.IsGenericType && x.GetGenericTypeDefinition() == typeof(ICollection<>)).Single().GetGenericArguments()[0];


        foreach (string subPath in GetCollectionPaths(listElemType))
        {
            yield return pi.Name + "." + subPath;
        }

    }
}



UPDATE: June 3, 2012

Permanent solution: http://www.ienablemuch.com/2011/08/entity-framework-troubles-on-wcf.html



1 comment:

  1. Hi Michael,

    Please let me know if you'd be interested in working on a custom orm/development framework using C#,nhibernate/fluent/Silverlight & ASP.NET.

    You can reach me at ashby builders (one word) at gmail.com

    Thanks & Regards

    ReplyDelete