NHibernate: How to inject dependency on an entity
Asked Answered
T

3

9

NHibernate 3.2/Fluent NHibernate 1.3/StructureMap 2.6.3 -

Trying to follow DDD as an architectural strategy, I typically don't have dependencies on domain entities. However, I'm experimenting right now with adding more behavior to my domain entities so that they are not so anemic. Everything was going well until I hooked up NHibernate. I've got two issues:

  1. NH requires a parameterless constructor and I'd rather not have a ctor that shouldn't be used.
  2. When NH tries to instantiate my entity, it needs to resolve my dependencies but I haven't given NH anything with which it can do that.

I've been reading on the web, but most (if not all) of the examples I have found are outdated (or just old). Even though the NH camp probably doesn't approve of what I'm doing, I'm looking for the NH way to do this.

Tomaso answered 5/11, 2011 at 21:15 Comment(1)
I've always assumed this was possible by supplying your own implementation of IProxyFactoryFactory, but I've never actually tried to do it.Guendolen
T
4

The solution ended up an implementation of NHibernate's IInterceptor. It is actually a very simple implementation when you inherit from EmptyInterceptor and override JUST the Instantiate() and SetSession() methods. Here's my interceptor using StructureMap:

public class DependencyInjectionEntityInterceptor : EmptyInterceptor
{
    IContainer _container;
    ISession _session;

    public DependencyInjectionEntityInterceptor(IContainer container)
    {
        _container = container;            
    }

    public override void SetSession(ISession session)
    {
       _session = session;            
    }

    public override object Instantiate(string clazz, EntityMode entityMode, object id)
    {
        if (entityMode == EntityMode.Poco)
        {
            var type = Assembly.GetAssembly(typeof (SomeClass)).GetTypes().FirstOrDefault(x => x.FullName == clazz);
            var hasParameters = type.GetConstructors().Any(x => x.GetParameters().Any());
            if (type != null && hasParameters)
            {
                var instance = _container.GetInstance(type);

                var md = _session.SessionFactory.GetClassMetadata(clazz);
                md.SetIdentifier(instance, id, entityMode);
                return instance;
            }
        }
        return base.Instantiate(clazz, entityMode, id);
    }
}

Then, all you have to do is tell NHibernate to use your interceptor:

public FluentConfiguration GetFluentConfiguration(IContainer container)
{
    return Fluently.Configure()
        .Database(MsSqlConfiguration.MsSql2008
                  .ConnectionString(c => c.FromConnectionStringWithKey("Database"))
                      .ShowSql())
        .Mappings(m => 
            m.AutoMappings.Add(AutoMap.AssemblyOf<SomeClass>()))
        .ExposeConfiguration(x => 
            x.SetInterceptor(new DependencyInjectionEntityInterceptor(container)));                
}

When I was researching this, some suggested passing in the SessionFactory into the ctor of the interceptor class. Honestly, from a session management perspective, this approach would be better.

Tomaso answered 15/11, 2011 at 19:1 Comment(4)
What kind of dependencies do you inject into your entities?Columnist
Like I said, I'm only experimenting right now. It goes against my "nature". So far, I've injected a timeProvider, a notificationService, and a repository. At this point, I don't like it very much. But the exercise is leading me to a better design (one that actually doesn't have entities with dependencies). :)Tomaso
It would be nice if you would post your alternative solution ;) For now I can imagine entities without dependencies.Columnist
I've decided to create a typical "service" class called "ChatRoom" that handles behaviors like saving the conversation, creating new conversations, and notifying participants. This removes all dependencies from my "Conversation" entity. My conversation still has behavior, but nothing that requires dependencies. I think this design makes a lot more sense.Tomaso
C
2

If you need additional dependencies in your entities don't use constructor injection. Instead create an additional parameter in the entity method.

Now you will ask yourself how do you get the dependency. For this you can use CommandHandlers and Commands. The command handler takes the dependency within its constructor and calls the method of the entity. In the UI you create a command message and send it to a command processor which is responsible for calling the correct command handler.

I hope my explanation is comprehensible to you.

Domain:

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }

    public void SendNotification(string message, INotifier notifier)
    {
        notifier.SendMessage(string.Format("Message for customer '{0}' ({1}): {2}", Name, Id, message));
    }
}

The INotifier infrastructure component is passed through the method and not the constructor!

Infrastructure:

public interface INotifier
{
    void SendMessage(string message);
}

class EmailNotifier : INotifier
{
    public void SendMessage(string message)
    {
        // SmtpClient...
    }
}

class SMSNotifier : INotifier
{
    public void SendMessage(string message)
    {
        // SMS ...
    }
}

Command and CommandHandler:

public class NotificationCommandHandler : ICommandHandler<NotificationCommand>
{
    private readonly INotifier _notifier;

    public NotificationCommandHandler(INotifier notifier)
    {
        _notifier = notifier;
    }

    public void Execute(NotificationCommand commandMessage)
    {
        commandMessage.Employee.SendNotification(commandMessage.Message, _notifier);
    }
}

public class NotificationCommand
{
    public string Message { get; set; }
    public Employee Employee { get; set; }
}

The CommandHandler gets the INotifier through constructor injection. So you do not need to use your IoC Container like a ServiceLocator.

Usage i.e. in the UI in a controller:

public class Controller
{
    private readonly IMessageProcessor _messageProcessor;

    public Controller(IMessageProcessor messageProcessor)
    {
        _messageProcessor = messageProcessor;
    }

    public void SendNotification (Employee employee, string message)
    {
        var sendMailCommand = new NotificationCommand
        {
            Employee = employee,
            Message = message
        };

        _messageProcessor.Process(sendMailCommand);
    }
}

If you have questions about the command processor have a look at the mvccontrib project or ask a separate question.

Columnist answered 5/11, 2011 at 22:50 Comment(0)
K
0

Sorry my previous answer didn't address the specific question. I did some more research, and it looks like I have much more to learn about when and when not to use an anemic domain model. Regarding your question, I found this article to be very on topic. It is on java, not c#, but the principles are the same. Hope this helps.

Kattie answered 5/11, 2011 at 23:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.