Working with NHibernate without default constructors

In the previous post we established that the usage of default constructor required by NHibernate is theoretically not required. The fact that NHibernate does use them though has to do with technical limitations of CLR. Turns out in most cases there is a workaround, which is not perfect but was a fun experiment to implement.

The code presented here is just an experiment. It is not of production quality – it is merely a proof of concept so if you use it in production and the world blows up, don’t blame me.

Interceptor

The place that NHibernate lets you plug into to instantiate entities and components as they are pulled from database is in NHibernate’s IInterceptor (not to be confused with Castle DynamicProxy IInerceptor) interface and its instantiate method. NHibernate has also a IObjectsFactory interface but that one is used to instantiate everything including NHibernate’s own infrastructure (like IUserTypes for example) and we want to override only activation of entities and components.

public class ExtensibleInterceptor : EmptyInterceptor
{
    private readonly INHibernateActivator activator;
 
    public ISessionFactory SessionFactory { get; set; }
 
    public ExtensibleInterceptor(INHibernateActivator activator)
    {
        this.activator = activator;
    }
 
    public override object Instantiate(string clazz, EntityMode entityMode, object id)
    {
        if (entityMode == EntityMode.Poco)
        {
            var type = Type.GetType(clazz);
            if (type != null && activator.CanInstantiate(type))
            {
                var instance = activator.Instantiate(type);
                SessionFactory.GetClassMetadata(clazz).SetIdentifier(instance, id, entityMode);
                return instance;
            }
        }
        return base.Instantiate(clazz, entityMode, id);
    }
}

The code here is straightforward. The INHibernateActivator is a custom interface that we’ll discuss in details below. That’s where the actual activation happens. We’re also using ISessionFactory so that we properly set the id of retrieved instance.

Proxy validator

Turns out that is not enough. In the interceptor we activate objects we fully obtain from the database. However big part of NHibernate performance optimisation is to ensure we don’t load more data that we need (a.k.a. lazy loading) and to do it transparently NHibernate uses proxies.

For every persistent type NHibernate uses object called proxy validator to ensure object meets all the requirements it has to be able to properly proxy a type and that work is done by IProxyValidator.

Part of the work of proxy validator is to check for default constructor so we need to override that logic to reassure NHibernate we have things under control.

public class CustomProxyValidator : DynProxyTypeValidator
{
    private const bool iDontCare = true;
 
    protected override bool HasVisibleDefaultConstructor(Type type)
    {
        return iDontCare;
    }
}

Proxy factory

Now that we told NHibernate we can handle proxying types without default constructor it’s time to write code that actually does it. That’s the task of ProxyFactory which uses Castle DynamicProxy under the cover.

public class CustomProxyFactory : AbstractProxyFactory
{
    protected static readonly IInternalLogger log = LoggerProvider.LoggerFor(typeof (CustomProxyFactory));
    private static readonly DefaultProxyBuilder proxyBuilder = new DefaultProxyBuilder();
    private readonly INHibernateActivator activator;
 
    public CustomProxyFactory(INHibernateActivator activator)
    {
        this.activator = activator;
    }
 
    public override INHibernateProxy GetProxy(object id, ISessionImplementor session)
    {
        try
        {
            var proxyType = IsClassProxy
                                ? proxyBuilder.CreateClassProxyType(
                                    PersistentClass,
                                    Interfaces,
                                    ProxyGenerationOptions.Default)
                                : proxyBuilder.CreateInterfaceProxyTypeWithoutTarget(
                                    Interfaces[0],
                                    Interfaces,
                                    ProxyGenerationOptions.Default);
 
            var proxy = activator.Instantiate(proxyType);
 
            var initializer = new LazyInitializer(EntityName, PersistentClass, id, GetIdentifierMethod, SetIdentifierMethod,
                                                    ComponentIdType, session);
            SetInterceptors(proxy, initializer);
            initializer._constructed = true;
            return (INHibernateProxy) proxy;
        }
        catch (Exception e)
        {
            log.Error("Creating a proxy instance failed", e);
            throw new HibernateException("Creating a proxy instance failed", e);
        }
    }
 
    public override object GetFieldInterceptionProxy()
    {
        var proxyGenerationOptions = new ProxyGenerationOptions();
        var interceptor = new LazyFieldInterceptor();
        proxyGenerationOptions.AddMixinInstance(interceptor);
        var proxyType = proxyBuilder.CreateClassProxyType(PersistentClass, Interfaces, proxyGenerationOptions);
        var proxy = activator.Instantiate(proxyType);
        SetInterceptors(proxy, interceptor);
        SetMixin(proxy, interceptor);
 
        return proxy;
    }
 
    private void SetInterceptors(object proxy, params IInterceptor[] interceptors)
    {
        var field = proxy.GetType().GetField("__interceptors");
        field.SetValue(proxy, interceptors);
    }
 
    private void SetMixin(object proxy, LazyFieldInterceptor interceptor)
    {
        var fields = proxy.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance);
        var mixin = fields.Where(f => f.Name.StartsWith("__mixin")).Single();
        mixin.SetValue(proxy, interceptor);
    }
}

It is pretty standard and not very interesting. We’re again using the INHibernateActivator, we’ve yet to discuss, to create instances. Also since we’re not calling any constructors to do it, we have to set the proxy dependencies that would otherwise by provided via constructor inline. For that we’re using some internal knowledge about how DynamicProxy lays out its proxy types.

Good news is – we’re almost there. Actually we might want to also override NHibernate reflection optimizer. As its name implies replaces some actions that are performed via reflection with faster, compiled versions. The default one works with assumption that default constructor is available and may fail otherwise but we can disable that feature for our simple proof of concept application.

Proxy factory factory

There’s one more step actually – we need to attach out proxy factory and proxy validator to session factory and we do that via ridiculously named IProxyFactoryFactory

public class CustomProxyFactoryFactory : IProxyFactoryFactory
{
    public IProxyFactory BuildProxyFactory()
    {
        return new CustomProxyFactory(new NHibernateActivator());
    }
 
    public bool IsInstrumented(Type entityClass)
    {
        return true;
    }
 
    public bool IsProxy(object entity)
    {
        return (entity is INHibernateProxy);
    }
 
    public IProxyValidator ProxyValidator
    {
        get { return new CustomProxyValidator(); }
    }
}

Putting it all together

Now that we have all these pieces let’s see how we put them together (with some help from FluentNHibernate)

var config = Fluently.Configure()
    .Mappings(c => c.FluentMappings.AddFromAssemblyOf<Program>())
    .Database(MsSqlConfiguration.MsSql2008
                .ConnectionString(s => s.Database("NHExperiment").Server(".").TrustedConnection())
                .ProxyFactoryFactory<CustomProxyFactoryFactory>())
    .BuildConfiguration();
var interceptor = new ExtensibleInterceptor(new NHibernateActivator());
config.Interceptor = interceptor;
NHibernate.Cfg.Environment.UseReflectionOptimizer = false;
var factory = config.BuildSessionFactory();
interceptor.SessionFactory = factory;

We set up our standard stuff – mapping and connection string. Then we need to tell NHibernate to use our custom ProxyFactoryFactory. Then we build configuration object and set our custom interceptor to it. Having done that we can build session factory and give the interceptor reference to the session factory. Certainly not the smoothest ride but gets the job done. Oh – and as I mentioned we disable reflection optimizer so that we don’t have to override yet another class (two actually).

That is all it takes to be able to get rid of all the seemingly superfluous constructors and have a much simpler model.

Well not quite – what about the activator

Right – the activator. How do we make that happen? Here’s all the code in this class.

public class NHibernateActivator : INHibernateActivator
{
    public bool CanInstantiate(Type type)
    {
        return true;
    }
 
    public object Instantiate(Type type)
    {
        return FormatterServices.GetUninitializedObject(type);
    }
}

Remember how I said that NHibernate can be viewed as fancy serializer? Here we make it obvious by using System.Runtime.Serialization.FormatterServices class to give us an uninitialized instance of given type. Uninitialized means that all the fields will be null or 0 and no code will be ran. This however is precisely what we want for the reasons outlined in previous post. We then throw the object to NHibernate machinery to perform all the necessary initialization for us, so that when the object is returned from the session it is fully created and usable. We could also implement a mechanism similar to one of the options that standard .NET serialization provides to allow the object to initialize its volatile state.

Final words

That is all it takes to make the model a bit more persistence ignorant. Like I said this approach won’t work always. It requires full trust environment and probably if you have complex volatile state the solution will be too simplistic. I would be interested to hear what do you think about the approach in general. Can you spot any severe shortcomings or flaws? Do you like it? Would you use it?


Posted 03-20-2011 7:58 AM by Krzysztof Koźmic
Filed under:

[Advertisement]

Comments

Eber I wrote re: Working with NHibernate without default constructors
on 03-21-2011 5:19 PM

enterprisey!

jgauffin wrote re: Working with NHibernate without default constructors
on 02-26-2012 7:17 AM

Excellent article. Just what I was looking for.

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)