Kamil Kliczbor @ asptip.net

7Nov/130

NHibernate Internals: Exploring NHibernate Proxy Part 2

NhLogoWhite64_med

Introduction

In the previous post I showed how simple proxy class was generated by proxy factory. Now it is time to find some answers to the questions I asked.

The suspicious name of the proxy class

We have a mapped class called MyProxyImpl and it implements an interface IMyProxy. The implementing proxy class name is INHibernateProxyProxy. This is strange and questionable. Let's investigate this and I will try answer the first question: Why it has such a strange name?

One thing that I should expand in the previous post is a mapping definition that is really important in understanding the way NHibernate maps a persistent class. In this particular case we have following mapping file defined:

<?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>

The most interesting part is the line with proxy="IMyProxy". This line tells NHibernate that generated proxy will have to implement IMyProxy interface instead of inheriting from MyProxyImpl class.

Note that the MyProxyImpl has not a virtual members and the proxy can be instantiated without errors! The reason it still works is because Proxy type is not being generated on class basis but by explicit proxy interface implementation.

The definition of this mapping file is available under the http://nhforge.org/doc/nh/en/index.html#mapping-declaration-class. We have there an explanation:

proxy (optional): Specifies an interface to use for lazy initializing proxies. You may specify the name of the class itself.

Description tells nothing about the generated class name, so we need to go debug through the code to see how the class name is resolved.
The stack trace pointed at PocoEntityTuplizer and its behaviour during the building the proxy factory for specific class. The clue is that we have some set of the types (interfaces) that are used in the resulting proxy with the first type set to INhibernateProxy.

// determine all interfaces needed by the resulting proxy
var proxyInterfaces = new HashedSet {typeof (INHibernateProxy)};

So the resulting proxyInterfaces set will finally have two types of the interfaces: INHibernateProxy and the IMyProxy. This will affect of course the DefaultProxyFactory PostInstantiate method. As a result when calling GetProxy we will have to take the first interface type when the class is based on the explicit proxy interface:

object proxyInstance = IsClassProxy
    ? factory.CreateProxy(PersistentClass, initializer, Interfaces)
    : factory.CreateProxy(Interfaces[0], initializer, Interfaces);

The IsClassProxy tells if the mapped class should be used for generating the name of the proxy or we should use the first interface type that is always INHibernateProxy. So far so good. The mystery solved!

My opinion about the way we access the interface by getting Interfaces[0] is a little bit clumsy. Finally this is always INhibernateProxy, so why we are using here a reference to the first item in the interfaces array ? I should ask this question in the nh-dev group.

But this leads to other additional question. How this behaviour is handled if I have many similar mappings? So let's say that I 'm going to declare the similar types: MyProxyImpl2 class that implements IMyProxy2 interface. How this will work if I have the same type, assembly and module names ?
After some investigations I found that NHibernate is creating the same INHibernateProxyProxy type within the INHibernateProxyAssembly in current AppDomain for IMyProxy and IMyProxy2. It would not be anything special but we have really strange fact. According to DefineDynamicAssembly signature description the assembly identity should be unique among AppDomain. As I expected this is just simply hint not requirement, so here we can have two types with the same names in the assembly with the same name and with the module as well. When we check the currentDomain.GetAssemblies() in CreateUncachedProxyType of the ProxyFactory we get the types in one app domain:

[27]: {INHibernateProxyProxyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null}
[28]: {ISymWrapper, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a}
[29]: {INHibernateProxyProxyAssembly, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null}

 It is worth to remember that NHibernate generates separate dynamic assembly with the generated type for each proxy type within one AppDomain.

Ok, let's have a cold beer for that, and see you soon in the next post.

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.