Kamil Kliczbor @ asptip.net

3Nov/130

NHibernate Internals: Exploring NHibernate Proxy Part 1

NhLogoWhite64_med

Introduction

This is a first post from series I plan to write about proxy feature in NHibernate. I would treat this as a notes and observation rather than a regular tutorial. I hope that you can find these quasi-posts series useful. I encourage you to make experiments with NHibernate!
The version I'am working with is 3.3.1.4000. The names here or in the next posts will refer to the test case names or types used there.

NHibernate Proxy at a glance

NHibernate provides enabled by default feature called lazy-loading. In a one word: this is a feature that allows to keep a non-loaded referenced entity or collection of entities attached to the entity you are working with and loading them on demand when it is necessary.


NHibernate was using separate dll for the proxy factory, but now it is included in the codebase . Default proxy factory is based on the LinFu.DynamicProxy. You can see it's customized implementation under NHibernate.Proxy.DynamicProxy namespace. It is still possible of course to use your own ProxyFactory implementation. This can be done via configuration.

Proxy

As I mentioned above the default implementation of the  proxy factory is based on LinFu.DynamicProxy. More about this proxy generator you can find on codeproject.  I would not like to dig in the implementation of the LinFu. Instead let's see the experimentation with the simple interface and it's implementation as a proxy class.

So, we have IMyProxy interface with Id and name properties and MyProxyImpl implementation. Let's see what is the output of dynamically generated type. In order to do so, go to ProxyFixture test and run the Proxy test case. You can define DEBUG constant in the NHibernate test project. Additionally change Save method body in the DefaultProxyAssemblyBuilder and choose right assembly name. The output was taken from .NET Reflector and some code was removed from the output for clarity.

public interface IMyProxy
{
	int Id { get; set; }
	string Name { get; set; }
}
public class MyProxyImpl: IMyProxy
{
	public int Id { get; set; }
	public string Id { get; set; }
}

And the mapping file that is the essential here is listed below as an *.hbm.xml mapping:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
				   assembly="NHibernate.Test"
				   namespace="NHibernate.Test.DynamicProxyTests.InterfaceProxySerializationTests">
	<class name="MyProxyImpl" proxy="IMyProxy">
		<id	name="Id">
			<generator class="assigned" />
		</id>

		<property name="Name" />
	</class>
</hibernate-mapping>
[Serializable]
public class INHibernateProxyProxy : ProxyDummy, INHibernateProxy, IMyProxy, ISerializable, IProxy
{
    // Fields
    private IInterceptor __interceptor;

    // Methods
    public INHibernateProxyProxy()
    {
    }

    public INHibernateProxyProxy(SerializationInfo info1, StreamingContext context) : this()
    {
        System.Type type = typeof(IInterceptor);
        this.__interceptor = (IInterceptor) info1.GetValue("__interceptor", type);
    }

    public override ILazyInitializer get_HibernateLazyInitializer()
    {
        if (this.Interceptor == null)
        {
            return this.HibernateLazyInitializer;
        }
        object[] args = new object[0];
        InvocationInfo info = new InvocationInfo(this, (MethodInfo) methodof(INHibernateProxy.get_HibernateLazyInitializer), null, new Type[0], args);
        args = info.Arguments;
        return (ILazyInitializer) this.Interceptor.Intercept(info);
    }

    public override int get_Id()
    {
        if (this.Interceptor == null)
        {
            return this.Id; // refers to IMyProxy.Id property
        }
        object[] args = new object[0];
        InvocationInfo info = new InvocationInfo(this, (MethodInfo) methodof(IMyProxy.get_Id), null, new Type[0], args);
        args = info.Arguments;
        return (int) this.Interceptor.Intercept(info);
    }

    public override void set_Id(int num1)
    {
        if (this.Interceptor == null)
        {
            this.Id = num1;
        }
        else
        {
            InvocationInfo info = new InvocationInfo(this, (MethodInfo) methodof(IMyProxy.set_Id), null, new Type[0], new object[] { num1 });
            this.Interceptor.Intercept(info);
            object[] arguments = info.Arguments;
        }
    }

    public override void GetObjectData(SerializationInfo info1, StreamingContext context)
    {
        info1.SetType(typeof(ProxyObjectReference));
        info1.AddValue("__interceptor", this.__interceptor);
        info1.AddValue("__baseType", "NHibernate.Proxy.INHibernateProxy, NHibernate, Version=3.3.1.4000, Culture=neutral, PublicKeyToken=null");
        info1.AddValue("__baseInterfaceCount", 2);
        info1.AddValue("__baseInterface0", "NHibernate.Proxy.INHibernateProxy, NHibernate, Version=3.3.1.4000, Culture=neutral, PublicKeyToken=null");
        info1.AddValue("__baseInterface1", "NHibernate.Test.DynamicProxyTests.InterfaceProxySerializationTests.IMyProxy, NHibernate.Test, Version=3.3.1.4000, Culture=neutral, PublicKeyToken=null");
    }

    public virtual IInterceptor NHibernate.Proxy.DynamicProxy.IProxy.Interceptor
    {
        get
        {
            return this.__interceptor;
        }
        set
        {
            this.__interceptor = value;
        }
    }
}

Let's list interesting things here that I will try to explain in the next posts:

  • The name INHibernateProxyProxy is really controversial. Why proxy class has such a strange name ?
  • The proxy inherits from ProxyDummy. What the heck is that ?
  • Here are two interfaces IProxy and INHibernateProxy. What they are here for ?
  • What is IInterceptor and how it is used in NHibernate?
  • What is ILazyInitializer and how it works ?
  • Finally, how this magic works that when I try to access Id the entity is not loaded but when I access other property, proxy tries to get value from DB (if there is no entity stored in session cache)?
Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.