NHibernate and default constructors

One of the first things you learn about NHibernate is that in order for it to be able 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.


Posted 03-19-2011 10:48 AM by Krzysztof Koźmic
Filed under:

[Advertisement]

Comments

Sandor Drieënhuizen wrote re: NHibernate and default constructors
on 03-21-2011 4:57 AM

Just as a side note: if you use the (string, bool) overload of ObsoleteAttribute and provide `true` as the second argument, the compiler will issue an error instead of a warning when calling the empty constructor. This offers an actual protection against accidental use.

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)