NHibernate 3.2 / Fluent NHibernate 1.3 / StructureMap 2.6.3 -
尝试将DDD作为架构策略,我通常不依赖于域实体。但是,我现在正在尝试向我的域实体添加更多行为,以便它们不会如此贫乏。一切顺利,直到我连接NHibernate。我有两个问题:
我一直在网上阅读,但我发现的大多数(如果不是全部)示例都已过时(或者只是旧版)。虽然NH阵营可能不赞成我正在做的事情,但我正在寻找NH的方式来做到这一点。
答案 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#,但原则是相同的。希望这会有所帮助。