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?

NHibernate and default constructors

One of the first things you learn about NHibernate is that in order for it to be able to construct your instances and take advantage of lazy loading every persistent class must have the default, parameterless constructor. This leads to having entities looking like this (standard blog with posts and comments example).

 public class Post { private readonly IList<comment> comments = new List<comment>();
    private Blog blog;

    [Obsolete("For NHibernate")]
    protected Post()
    {
       
    }

    public Post(string title, string body, Blog blog)
    {
        Blog = blog;
        Title = title;
        Body = body;
        Published = DateTimeOffset.Now;
    }

    public virtual int Id { get; private set; }
    public virtual DateTimeOffset Published { get; private set; }

    public virtual string Title { get; set; }
    public virtual string Body { get; set; }


    public virtual IEnumerable<comment> Comments
    {
        get { return comments; }
    }

    public virtual Blog Blog
    {
        get { return blog; }
        set
        {
            if (blog != null)
            {
                throw new InvalidOperationException("already set");
            }
            blog = value;
            if (blog != null)
            {
                blog.AddPost(this);
            }
        }
    }

    [EditorBrowsable(EditorBrowsableState.Never)]
    protected internal virtual void AddComment(Comment comment)
    {
        comments.Add(comment);
    }
}

Notice the first constructor. It doesn’t do anything. As the obsolete message (which is out there to get compilation warning in case some developer accidently calls this constructor in their code) points out – this constructor is there only so that NHibernate can do its magic. Some people do put there initialization logic for collections, (especially when you use automatic properties) but I use fields and initialize them inline. In that case the constructor is pretty much useless. I started thinking why is it even there and that perhaps it doesn’t really belong to the class. But let’s start at the beginning.

What is a constructor

As basic as the question may seem, it is useful to remind ourselves why we need constructors at all. The best book about C# and .NET defines them as follows:

Constructors are special methods that allow an instance of a type to be initialized to a good state.

Notice two important things about this definition. First, it doesn’t say that constructor creates the instance or that constructors are the only way to create the instance. Second, constructors initialize newly created object to their initial state so that anything that uses the object afterwards deals with fully constructed, valid object.

Constructors and persistence

The above definition very well applies to the other constructor we have on the Post class. That constructor initializes the Post to a valid state. In this case valid means the following.

  • Post is part of a blog – we can’t have a post that lives on its own. Our posts need to be part of a blog and we make this requirement explicit by requiring a Blog instance to be provided when constructing Post.
  • Post requires a title and a body and that’s why we also require those two properties to be provided when constructing a post.
  • Posts are usually displayed in a inverse chronological order hence we set the Published timestamp.

We do none of the above in the other, “nhibernate” constructor. That means that according to the definition of a constructor it is not really doing what a constructor is supposed to be doing. It is never used to construct an object.

Hydration

Let’s take a step back now. What NHibernate is doing with objects in nutshell is serialization. You create an object in your code and initialize it using constructor, do some stuff with it and then you save the object away, so that it can be retrieved later, after your app has been closed, or perhaps on another server instance. You save away the state of the object so that the state representation of the object can live longer than volatile, in-memory representation of the object. If you follow this path of though the next obvious conclusion is that if you have a load-balanced system and two server instances work with Post#123 they both are dealing with the same object, even though they are two separate machines.

The conclusion of that is that when NHibernate is retrieving an object from the persistent store it is not constructing it. It is recreating an in-memory representation of an object that had been created and persisted previously. Hence we are merely recreating object that already has a well known state and had been initialized and just providing different representation for it. This process is called hydration.

Persistent and Volatile state

The full picture is a bit more complicated than what I painted so far. The database and in-memory object are two representation of the same entity but they don’t have to be fully one to one. Specifically it is possible for the in-memory representation to have state beyond the persistent state. In other words the in-memory object may have some properties that are specific to it, and not relevant to the in-database representation. A convenient example that most people will be able to relate to would be a logger. Please don’t quote me as advocating using logging in your entities but logger is one of the things you may want to have on your in-memory object and use it while executing code in your application but then let them go once you no longer need the object and not persist them. If we had one in the Post class the empty constructor would change to the following:

[Obsolete("For NHibernate")]
protected Post()
{
    logger = LoggerProvider.LoggerFor(typeof(Post));
}

If we don’t use constructor for recreation of the object, how can we get the logger in? How do we make NHibernate hold the contructor semantics and give us fully initialized object? Remember I said one way of looking at NHibernate from the object’s perspective is that’s just a fancy serializer/deserializer. Turns out serialization mechanism in .NET offers us four(that I know of, possibly more) ways of tacking this issue

  • you can use serialization surrogate that knows how to recreate the full state of the object
  • you can use deserialization callback interface to be notified when the object has been fully deserialized and then initialize the volatile state.
  • you can use serialization callback attributes to be notified when various steps in the serialization/deserialization process happen and initialize the volatile state.
  • you can use ISerializable interface and implement serialization constructor which is used to deserialize the object.

Notice that only one of those approaches uses special constructor. Since as we discussed NHibernate doesn’t really need the default constructor (in theory that is), can we really get rid of it? Turns one we can (in most cases), and we’ll look at how to do it in the next post.

Testing with NHibernate and SQLite

warninglabel

There does not seem to be too much details on how to set up a test environment for NHibernate testing using SQLite. Ayende has a nice post on this, but he does not go into details of how, what and where, so I decided to fill in the blanks, and provide an up to date sample for NHibernate 2.1.

Let’s first gather all the things we need:

Now we need to add references to all these projects. Make sure you add correct reference to System.Data.SQLite (either x86 version from the main directory, or x64 version from x64 folder if you’re running on 64bits).

You also can’t add reference to sqlite3.dll, because it’s an unmanaged dll. I simply add it as element to solution and set it to copy to output directory. After all, your solution should look roughly like this:

NHibernate_tests

I also have a base class for my tests that does all the setup for me. Here’s how it looks like:

[TestFixture]
public abstract class DbTestsBase<T>
{
    protected ISessionFactory factory;
    protected T sut;
 
    private string dbFile;
 
    public void Init(params Assembly[] assembliesWithMappings)
    {
        dbFile = GetDbFileName();
        EnsureDbFileNotExists();
 
        NHibernateProfiler.Initialize();
        var configuration = new Configuration()
            .AddProperties(new Dictionary<string, string>
                               {
                                   { Environment.ConnectionDriver, typeof( SQLite20Driver ).FullName },
                                   { Environment.Dialect, typeof( SQLiteDialect ).FullName },
                                   { Environment.ConnectionProvider, typeof( DriverConnectionProvider ).FullName },
                                   { Environment.ConnectionString, string.Format( "Data Source={0};Version=3;New=True;", dbFile) },
                                   { Environment.ProxyFactoryFactoryClass, typeof( ProxyFactoryFactory ).AssemblyQualifiedName },
                                   { Environment.Hbm2ddlAuto, "create" },
                                   { Environment.ShowSql, true.ToString() }
                               });
        foreach (var assembly in assembliesWithMappings)
        {
            configuration.AddAssembly(assembly);
        }
 
        new Remapper().Remap(configuration);
 
        factory = configuration.BuildSessionFactory();
    }
 
    [TearDown]
    public void TearDownTests()
    {
        factory.Dispose();
        EnsureDbFileNotExists();
        NHibernateProfiler.Stop();
    }
 
    private string GetDbFileName()
    {
        var path = Path.GetFullPath(Path.GetRandomFileName() + ".Test.db");
        if (!File.Exists(path))
        {
            return path;
        }
 
        // let's try again
        return GetDbFileName();
    }
 
    private void EnsureDbFileNotExists()
    {
        if (File.Exists(dbFile))
        {
            File.Delete(dbFile);
        }
    }
}

There are couple interesting points in this code:

I expose the SessionFactory, not Session. That’s because I use this class for testing repositories in stateful application, that manage the session themselves.

I save the database to file, instead of keeping it in memory like Ayende does. That’s connected to the previous fact. Apparently, in-memory database only lives as long as session that created it, which does not cut it for me.

I then use the class like this:

[TestFixture]
class Entity1Tests : DbTestsBase<Entity1>
{
    [SetUp]
    public void SetUp()
    {
        base.Init(typeof(Entity1).Assembly);
    }
 
    [Test]
    public void Member_should_action()
    {
        //some code
    }
}

Pretty simple and so far, covers my needs.

Technorati Tags: ,,

Storing long text and binary blobs with NHibernate

There comes a time when you have to store in the database not only pretty objects but also some not so pretty data. For example, let’s take the classical entity – the invoice.

 

Let’s say your application can receive invoice in three ways: by its own frontend, by email in rtf file, and by fax. In two later cases you may have a requirement to store the rtf file and the scanned fax as a proof in the database. How to approach that?

Let’s start by sketching our Invoice class:

public class Invoice
{
    public virtual Guid Id { get; protected set; }
    public virtual string InvoiceRtfString { get; set; }
    public virtual byte[] ScannedInvoiceJpg { get; set; }
    
    //a lot more stuff...
}

For the sake of example let’s say we want to keep the rtf file as text (well, that’s what it is underneath), and we want to keep the scanned invoice as byte array.

Let’s create the simplest mapping and see what NHibernate will produce out of it.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
                   namespace="ConsoleApplication1" 
                   assembly="ConsoleApplication1">
  <class name="Invoice">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <property name="InvoiceRtfString"/>
    <property name="ScannedInvoiceJpg"/>
  </class>
</hibernate-mapping>

If we now ask NHibernate to generate the schema for us, that is what it will produce:

create table Invoice (
    Id UNIQUEIDENTIFIER not null,
   InvoiceRtfString NVARCHAR(255) null,
   ScannedInvoiceJpg VARBINARY(8000) null,
   primary key (Id)
)

As you can see, we get quite little space for our Rtf file – hardly any file will fit in 255 characters, and certainly not one created by Microsoft Word.

If you check NHibernate documentation it suggests using StringClob for this, so let’s do just that. It also suggests we used BinaryBlob type for the scanned jpg so let us apply this change as well, and see how it affects the generated schema.

If we update the mapping:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
                   namespace="ConsoleApplication1" 
                   assembly="ConsoleApplication1">
  <class name="Invoice">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <property name="InvoiceRtfString" type="StringClob"/>
    <property name="ScannedInvoiceJpg" type="BinaryBlob"/>
  </class>
</hibernate-mapping>

…and generate schema…

create table Invoice (
    Id UNIQUEIDENTIFIER not null,
   InvoiceRtfString NVARCHAR(255) null,
   ScannedInvoiceJpg VARBINARY(8000) null,
   primary key (Id)
)

…it will surprisingly be exactly the same. I’m genuinely surprised. Is there anything else we can do?

Quick googling leads to this old post by Ayende, where he suggests the solution: specifying sql-type explicitly as NTEXT for the string column.

However, as Scott White noticed, NTEXT is deprecated since SQL Server 2008, we should use nvarchar(max) instead. Since we also expect size of serialized jpg images to be higher than 8kb, we’ll use varbinary(max) instead here as well.

So our final mapping looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" 
                   namespace="ConsoleApplication1" 
                   assembly="ConsoleApplication1">
  <class name="Invoice">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <property name="InvoiceRtfString" type="StringClob">
      <column name ="RtfText" sql-type="nvarchar(MAX)"/>
    </property>
    <property name="ScannedInvoiceJpg" type="BinaryBlob">
      <column name ="JpgData" sql-type="varbinary(max)" />
    </property>
  </class>
</hibernate-mapping>

And NHibernate produces the following DDL out of it:

create table Invoice (
    Id UNIQUEIDENTIFIER not null,
   RtfText nvarchar(MAX) null,
   JpgData varbinary(max) null,
   primary key (Id)
)

Which is exactly what we needed.

DNK Tags:

Two way one-to-many associations in NHibernate

As nice as the example from my previous post was (person having pets) it exhibits a problem. The problem is related to the fact that by their very nature associations in relational databases are bidirectional, whereas in objects they are unidirectional. Here we hit the mythical impedance mismatch.

Often however we want to have a bidirectional association in our object model. Person may have a set of pets, but then each pet has its owner. Then we have another problem. What if we add a pet to person’s pet collection, but forget to set pet’s owner? We’ll get inconsistencies.

The best way to alleviate that (or the best way I found. Remember that the big warning sign from the previous post still holds true) is to decide on using one side of association for manipulation, and have it modify the other.

public class Pet
{
    private Person _owner;
    public virtual Guid Id { get; protected set; }
    public virtual string Name { get; set; }
 
    public virtual Person Owner
    {
        get { return _owner; }
        set
        {
            if (Equals(_owner, value))
            {
                //there's no need to change anything
                return;
            }
            //sorry old owner, I'm no longer yours
            if (_owner != null)
            {
                _owner.Pets.Remove(this);
            }
            _owner = value;
            if (_owner != null)
            {
                //welcome to my new home
                _owner.Pets.Add(this);
            }
        }
    }
}

Here you see that when we set the Owner for a pet it takes care of removing the pet from the old owner, and adding it to the new one.

Is it all we need? Well, not quite, we forgot about the mapping.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="ConsoleApplication1" assembly="ConsoleApplication1">
  <class name="Pet">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <property name="Name"/>
    <many-to-one name="Owner" column="OwnerId" />
  </class>
</hibernate-mapping>

Notice one important thing. In our many-to-one we have set the value of column attribute to be the same as the one-to-many end’s key column attribute (see Person’ mapping in the previous post or below). If we didn’t NHibernate would create two different columns for that and I’m not sure how it would affect the behavior. Probably it’s something you’d want to avoid.

Are we done? Let’s see.

using(var session = factory.OpenSession())
using(var transaction = session.BeginTransaction())
{
    var krzysztof = new Person();
    new Pet {Name = "Rex", Owner = krzysztof};
    session.Save(krzysztof);
    transaction.Commit();
}

When we run that piece of code, with the mapping we have, here’s what will happen.

nh_2_additional_update

NHibernate will issue two INSERTS and one UPDATE (this is exactly the same SQL as code from my previous post would yield. The difference is, we can get rid of the UPDATE now). NHibernate Profiler shows us an alert by the UPDATE saying that its superfluous and if we used inverse=”true” it would go away. So let’s do just that.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="ConsoleApplication1" assembly="ConsoleApplication1">
  <class name="Person">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <property name="HasPets" access="readonly"/>
    <set name="Pets" cascade="all" inverse="true" >
      <key column="OwnerId" />
      <one-to-many class="Pet" />
    </set>      
  </class>
</hibernate-mapping>

If we run the application now, the additional UPDATE will go away, as advertised.

nh_2_no_update

Ok, we can INSERT our association into the DB, so let’s now SELECT it back.

using(var session = factory.OpenSession())
using(var transaction = session.BeginTransaction())
{
    var krzysztof1 = session.Get<Person>(id);
    foreach(var pet in krzysztof1.Pets)
        Debug.Assert(pet.Owner == krzysztof1);
 
    transaction.Commit();
}

Surprisingly, if we run this code we’ll get an exception:

NHibernate.LazyInitializationException was unhandled
  Message=”illegal access to loading collection”

The problem is that when creating Pet instances NHibernate sets the Parent property. Our setter tries to add the pet to Person’s Pets collection which is not initialized, hence the exception.

So how do we fix it? Same as the last time. We want to bypass the setter and set the owner directly into the field. We don’t need our setter’s code anyway since at this point NHibernate takes care of maintaining integrity of the association.

So we only need to add appropriate access attribute to our many-to-one mapping and we’re all set.

<many-to-one name="Owner" column="OwnerId" access="nosetter.camelcase-underscore" />

Hope this was helpful.

DNK Tags: ,

Read only collections and properties with NHibernate

warningsign

I’ve been working with NHibernate for the last couple of days, and as I make my way though it, I find out about things, that were not so obvious to me at first, so I decided to post them here, so that someone else can benefit as well.

First thing you learn about NHibernate (well ok – first thing I learned about NHibernate, but most of you probably as well) is that it requires you to mark your properties virtual, have parameterless constructor, and pay special attention to your GetHashCode() and Equals() methods.

With all that in mind (and following many tutorial that are out there) you may start crunching your entity classes to look like this:

public class Person
{
    public virtual Guid Id { get; set; }
    public virtual ISet<Pet> Pets{ get; set; }
    public virtual bool HasPets 
    { 
        get { return Pets.Count > 0; } 
        set { /*do nothing*/}
    }
}

with mapping like this:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="ConsoleApplication1" assembly="ConsoleApplication1">
  <class name="Person">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <property name="HasPets"/>
    <set name="Pets" cascade="all" >
      <key column="OwnerId" />
      <one-to-many class="Pet" />
    </set>      
  </class>
</hibernate-mapping>

This gets the job done, but it’s far from being persistence ignorant. You usually don’t want to expose a setter for your collections, so that they can be swapped. Also the set accessor on HasPets property is nothing short of an ugly hack. Although we have no explicit sign of NHibernate in the code, it is anything but persistence ignorant.

You can however make it so.

public class Person
{
    private readonly ISet<Pet> _pets = new HashedSet<Pet>();
 
    public virtual Guid Id { get; protected set; }
 
    public virtual ISet<Pet> Pets { get { return _pets; } }
 
    public virtual bool HasPets
    {
        get { return Pets.Count > 0; }
    }
}

Now it looks like a “normal” class. Will it work with NHibernate now though? – Absolutely*. The trick is to use mapping files appropriately.

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="ConsoleApplication1" assembly="ConsoleApplication1">
  <class name="Person">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <property name="HasPets" access="readonly"/>
    <set name="Pets" cascade="all" access="nosetter.camelcase-underscore" >
      <key column="OwnerId" />
      <one-to-many class="Pet" />
    </set>      
  </class>
</hibernate-mapping>

 

What’s changed? I added the access attributes to the mappings.

For HasPets I set it to readonly. That way NHibernate will read the property and use its value when doing INSERTs and UPDATEs, but will not include it in SELECTs.

For Pets one-to-many mapping I used value that tells NHibernate to use the getter to read the value of the property, and to write to the field directly, and that field name is _pets (by convention). There are quite a few more options, and you can use them to do some pretty powerful things, like enforcing consistency in two-way *-to-many mappings.

 

* the access=”readonly” is new to NHibernate v2.1

DNK Tags: