NHibernate:如何注入对实体的依赖

发布于 2024-12-14 01:34:42 字数 367 浏览 0 评论 0原文

NHibernate 3.2/Fluent NHibernate 1.3/StructureMap 2.6.3 -

尝试遵循 DDD 作为架构策略,我通常不依赖于域实体。然而,我现在正在尝试向我的域实体添加更多行为,以便它们不那么贫乏。一切都很顺利,直到我连接了 NHibernate。我有两个问题:

  1. NH 需要无参数构造函数,而我不想有 不应该使用的 ctor。
  2. 当 NH 尝试实例化我的实体时,它需要解析我的 依赖项,但我没有给 NH 任何它可以做的事情 那。

我一直在网上阅读,但我发现的大多数(如果不是全部)示例都已过时(或只是旧的)。尽管 NH 阵营可能不赞成我正在做的事情,但我正在寻找 NH 的方式来做到这一点。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

七度光 2024-12-21 01:34:42

该解决方案最终实现了 NHibernate 的 IInterceptor。当您继承 EmptyInterceptor 并仅重写 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 中。老实说,从会话管理的角度来看,这种方法会更好。

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.

风柔一江水 2024-12-21 01:34:42

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

现在你会问自己如何获得依赖。为此,您可以使用 CommandHandlers 和 Commands。命令处理程序在其构造函数中获取依赖项并调用实体的方法。在 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 和 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 项目或问一个单独的问题。

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.

调妓 2024-12-21 01:34:42

抱歉,我之前的回答没有解决具体问题。我做了更多的研究,看起来我还有很多东西需要了解何时以及何时不使用贫血域模型。关于你的问题,我找到了这篇文章 非常切题。虽然是java上的,不是c#上的,但是原理是一样的。希望这有帮助。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文