Viewing Generated Proxy Code in the Entity Framework

This is one post that’s been on my to-do list for a while, and since I’ve seen some questions relating to it in our forums, I thought it appropriate to get it out the door before .NET 4 officially releases.

As you may know, the Entity Framework supports mapping database information to POCO (Plain Old CLR Objects) classes in .NET 4. Normally, giving you control over your classes would mean we lose out on a few benefits on controlling the classes ourselves, such as lazy loading and change tracking capabilities. However, if your classes meet a few requirements, then we can dynamically create an assembly at runtime which contains classes that inherit from your POCO types. Today these classes add additional behavior such as lazy loading and change tracking, but in future releases we could potentially augment them to add more features. Typically we refer to these dynamic classes as POCO proxies.

The Entity Framework uses the features in the Reflection.Emit namespace to generate these classes. If you check out Reflector, you can see that the System.Data.Objects.Internal.EntityProxyFactory.GetDynamicModule starts this process by calling AppDomain.CurrentDomain.DefineDynamicAssembly with the AssemblyBuilderAccess specified in the s_ProxyAssemblyBuilderAccess field. During normal execution, s_ProxyAssemblyBuilderAccess is AssemblyBuilderAccess.Run and will never change, but we added this field as a test hook for other purposes. As a result, you can also save the assembly to disk by setting the s_ProxyAssemblyBuilderAccess field to AssemblyBuilderAccess.RunAndSave with reflection.

You can’t just save the assembly right away, since we lazily emit new types into the dynamic assembly as they’re requested. You’ll need to force the Entity Framework to create proxy types by calling ObjectContext.CreateProxyTypes with a list of all POCO types for which you want proxies generated. Then you can save the assembly to disk by calling AssemblyBuilder.Save on the dynamic assembly. If that sounds complicated, don’t worry. We’ll walk through some code to make these steps more concrete. Please note that you’ll need to run in full trust or at least have ReflectionPermission with ReflectionPermissionFlag.MemberAccess to run the code below.

I’ve attached a solution to this post that shows the code in more detail, but let’s walk through the major parts. To set up, I’ve created an Entity Data Model based on the Northwind database, and I’ve used our POCO templates to create classes that the Entity Framework will create proxy types for. The first step is to set the s_ProxyAssemblyBuilderAccess field via reflection.

    C#

    Type entityProxyFactoryType = Type.GetType(

        "System.Data.Objects.Internal.EntityProxyFactory, " + typeof(ObjectContext).Assembly.FullName);

     

    const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Static;

    entityProxyFactoryType.GetField("s_ProxyAssemblyBuilderAccess", bindingFlags)

        .SetValue(null, AssemblyBuilderAccess.RunAndSave);

    VB

    Dim entityProxyFactoryType As Type = Type.GetType(

            "System.Data.Objects.Internal.EntityProxyFactory, " + GetType(ObjectContext).Assembly.FullName)

     

    Const bindingFlags As BindingFlags = BindingFlags.NonPublic Or BindingFlags.Static

    entityProxyFactoryType.GetField("s_ProxyAssemblyBuilderAccess", bindingFlags) _

        .SetValue(Nothing, AssemblyBuilderAccess.RunAndSave)

Next, we need to force the creation of proxy types. Here we’re using a variable context that represents an ObjectContext we’re interested in generating proxies for. (Its instantiation is not shown.) Fortunately, the CreateProxyTypes method ignores any types that are not represented by the model, so we can call the method passing all types in the current assembly.

    C#

    context.CreateProxyTypes(Assembly.GetExecutingAssembly().GetTypes());

    VB

    context.CreateProxyTypes(Assembly.GetExecutingAssembly().GetTypes())

Finally, we need to access the AssemblyBuilder using a bit of reflection and call its Save method.

    C#

    var moduleBuilders = (IDictionary<Assembly, ModuleBuilder>)

        entityProxyFactoryType.GetField("s_ModuleBuilders", bindingFlags).GetValue(null);

     

    var pocoProxyModule = moduleBuilders[typeof(NorthwindEntities).Assembly];

    var pocoProxyAssembly = (AssemblyBuilder)pocoProxyModule.Assembly;

     

    pocoProxyAssembly.Save("EntityProxyModule.dll");

    VB

    Dim moduleBuilders = DirectCast(

            entityProxyFactoryType.GetField("s_ModuleBuilders", bindingFlags).GetValue(Nothing),  _

            IDictionary(Of Assembly, ModuleBuilder))

     

    Dim pocoProxyModule = moduleBuilders(GetType(NorthwindEntities).Assembly)

    Dim pocoProxyAssembly = DirectCast(pocoProxyModule.Assembly, AssemblyBuilder)

     

    pocoProxyAssembly.Save("EntityProxyModule.dll")

After the preceding code runs, you’ll be left with a file named EntityProxyModule.dll in the current directory, which you can easily pop into Reflector to view the code for the proxies. As you can see the names of the generated types are quite long. :) Since proxies today are tied to the metadata of the ObjectContext by which they were created, we use a hash of the metadata in the proxy’s type name to correlate the proxy type with that ObjectContext. If we need to use the same CLR type for an ObjectContext with different metadata, we will create a new proxy type.

Proxy assembly in Reflector

I’m not going to dive deep into the proxy code, since it resembles code from the default code generation in some ways (e.g. change tracking, relationship management). There is one interesting piece that I’ll point out and that is how we override navigation properties.

image

Our overrides are very simple—usually we delegate to the base implementation, but if lazy loading is enabled we will call into a delegate (ef_proxy_interceptor…), which is where we will load the navigation property into memory if it’s not there already. Of course, in the proxy code there is nothing specific to lazy loading; we just call a delegate that could do any number of things in future.

I encourage you to download the solution and play around with the code. Let me know here or on our forums if you have any additional questions!

6 thoughts on “Viewing Generated Proxy Code in the Entity Framework

  1. Hi, I tried this solution (took the code from here pretty much as is) but keep getting an InvalidOperationException with the message “Cannot save a transient assembly” on the pocoProxyAssembly.Save(…) line. I am running it through a Visual Studio Unit Test though.

    • Dinesh, did you use the first code snippet to set the AssemblyBuilderAccess to AssemblyBuilderAccess.RunAndSave instead of AssemblyBuilderAccess.Run?

  2. Hi, we have problems that our navigation propterties are not not reloaded even if we have set MergeOption.OverwriteChanges on the ObjectSet. The fist time we fetch a entity and then access its navigation propterties we get the data (and we can see this in EFProf). I we then make changes to the database and fetch the the entity again we see that the actual entity is fetched from the database with its new data. But when we access the entity’s navigation property the already loaded data is returned and not the updated data in the database.
    You state above that “we will load the navigation property into memory if it’s not there already.” How does it handle the case that the data is memory but the data in the database is updated and thereby the data should be fetched again into memory?

    BR,

    /Peter

    • Hi Peter, sorry for the delay in replying. The underlying problem is that there is no way for the EF to know there is new data in the database unless you tell it (e.g. with OverwriteChanges). It’s an interesting situation that you bring up because I can see advantages to both sides. On the one hand, you could expect that reloading an object with MergeOption.OverwriteChanges would cause navigation properties to be invalidated and thus lazy loading would trigger a new query against the database, even if the navigation property was already loaded. On the other hand, this behavior could be surprising because you are only loading the object with MergeOption.OverwriteChanges and not the association.

      You do have a way to do explicit loading of associations with MergeOption.OverwriteChanges (by using ObjectContext.LoadProperty). But I don’t think there is a way to tell lazy loading what MergeOption to use at this time.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>