NHibernate:如何为实体注入依赖关系

时间:2011-11-05 21:15:44

标签: nhibernate structuremap

NHibernate 3.2 / Fluent NHibernate 1.3 / StructureMap 2.6.3 -

尝试将DDD作为架构策略,我通常不依赖于域实体。但是,我现在正在尝试向我的域实体添加更多行为,以便它们不会如此贫乏。一切顺利,直到我连接NHibernate。我有两个问题:

  1. NH需要一个无参数的构造函数,而我宁愿没有 不应该使用的ctor。
  2. 当NH尝试实例化我的实体时,它需要解决我的问题 依赖,但我没有给NH任何可以做的事情 这一点。
  3. 我一直在网上阅读,但我发现的大多数(如果不是全部)示例都已过时(或者只是旧版)。虽然NH阵营可能不赞成我正在做的事情,但我正在寻找NH的方式来做到这一点。

3 个答案:

答案 0 :(得分:4)

该解决方案最终实现了NHibernate的IInterceptor。当你从EmptyInterceptor继承并覆盖JUST Instantiate()和SetSession()方法时,它实际上是一个非常简单的实现。这是使用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);
    }
}

然后,你所要做的就是告诉NHibernate使用你的拦截器:

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)));                
}

当我研究这个时,有人建议将SessionFactory传递给拦截器类的ctor。老实说,从会话管理的角度来看,这种方法会更好。

答案 1 :(得分:1)

如果您的实体需要其他依赖项,请不要使用构造函数注入。而是在实体方法中创建一个附加参数。

现在您将问自己如何获得依赖性。为此,您可以使用CommandHandlers和命令。命令处理程序在其构造函数中获取依赖项并调用实体的方法。在UI中,您可以创建命令消息并将其发送到命令处理器,该命令处理器负责调用正确的命令处理程序。

我希望你的解释是可以理解的。

域:

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));
    }
}

INotifier基础架构组件是通过方法传递的,而不是构造函数!

基础设施:

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; }
}

CommandHandler通过构造函数注入获取INotifier。因此,您不需要像ServiceLocator那样使用IoC容器。

用法,即在控制器的UI中:

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);
    }
}

如果您对命令处理器有疑问,请查看mvccontrib项目或提出单独的问题。

答案 2 :(得分:0)

抱歉,我之前的回答没有解决具体问题。我做了一些更多的研究,看起来我还有更多要了解何时何时不使用贫血领域模型。关于你的问题,我发现this article非常关注主题。它是在java上,而不是c#,但原则是相同的。希望这会有所帮助。