27
Jul
09

Dependency Injection for NHibernate

Sly has asked about the DependencyInjectionInterceptor class I referred to in a previous post.

Here is the class:

    ///<summary>
    /// An NHibernate interceptor that instantiates objects from a DI container.
    ///</summary>
    public class DependencyInjectionInterceptor : EmptyInterceptor
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(DependencyInjectionInterceptor));
        private readonly IKernel container;
        private readonly Check chk = new Check();
        ///<summary>
        /// Construct the <see cref="DependencyInjectionInterceptor"/>.
        /// Entities found by name in the container will be resolved from there.
        /// All other entities will use default NHibernate instantiation.
        ///</summary>
        ///<param name="container">The IoC container</param>
        public DependencyInjectionInterceptor(IKernel container)
        {
            this.container = container;
        }
        /// <summary>
        ///             Instantiate the entity class. Return <see langword="null" /> to indicate that Hibernate should use the default
        ///             constructor of the class
        /// </summary>
        /// <param name="entityName">the name of the entity </param>
        /// <param name="entityMode">The type of entity instance to be returned. </param>
        /// <param name="id">the identifier of the new instance </param>
        /// <returns>
        /// An instance of the class, or <see langword="null" /> to choose default behaviour
        /// </returns>
        /// <remarks>
        ///             The identifier property of the returned instance
        ///             should be initialized with the given identifier.
        /// </remarks>
        public override object Instantiate(string entityName, EntityMode entityMode, object id)
        {
            try
            {
                return container.Resolve(entityName, new {Id = id});
            }
            catch(ComponentNotFoundException)
            {
                chk.Assert(!container.HasComponent(entityName),
                           string.Format(
                                   "Entity {0}, registered in the container, could not be constructed with an 'Id' " +
                                   "dynamic argument. The DependencyInjectionInterceptor expects to find a component " +
                                   "with a contructor having all dependencies satisfied and with a property named 'Id' to " +
                                   "take the object's id (key).",
                                   entityName));
                log.DebugFormat(
                        "Instantiate: Entity {0} was not registered in the container. Using default NHibernate contruction.",
                        entityName);
                return null;
            }
        }
    }

IKernel is the Castle Micro Kernel (within Windsor). Each entity (mapped class) needs to be registered in the container and must have an Id property.

One important ‘gotcha’ is that Castle will inject into public properties by default, so a class with a many-to-one will be given a default instance of the ‘one’ type by Castle, which you probably don’t want. The simplest way to stop this is to decorate the property with the DoNoWireAttribute in Castle.Core. There are other smarter ways.

As I mentioned in my reply to Sly, alternatives based on the latest NH release are described on NHForge.

About these ads


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: