【发布时间】:2018-05-20 19:10:29
【问题描述】:
我正在使用 ASP.NET Core 2.0 和 EF Core 2.0 构建应用程序。至于在我的领域中解耦不同类型的逻辑,我使用 DDD(领域驱动设计)的领域事件。让我们深入研究实现,看看我有什么,然后我将讨论我的问题。
首先让我们看看我的领域事件相关类的通用实现。首先是一个marker接口,IDomainEvent:
public interface IDomainEvent
{
}
在它旁边我有一个通用的IHandler 类:
public interface IHandler<in T> where T : IDomainEvent
{
void Handle(T domainEvent);
}
然后我有一个DomainEvents 类:
private static List<Type> _handlers;
public static void Init()
{
InitHandlersFromAssembly();
}
private static void InitHandlersFromAssembly()
{
_handlers = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(x => x.GetInterfaces().Any(y => y.IsGenericType && y.GetGenericTypeDefinition() == typeof(IHandler<>)))
.ToList();
}
public static void Dispatch(IDomainEvent domainEvent)
{
foreach (var handlerType in _handlers)
{
if (CanHandleEvent(handlerType, domainEvent))
{
dynamic handler = Activator.CreateInstance(handlerType);
handler.Handle((dynamic)domainEvent);
}
}
}
private static bool CanHandleEvent(Type handlerType, IDomainEvent domainEvent)
{
return handlerType.GetInterfaces()
.Any(x => x.IsGenericType
&& x.GetGenericTypeDefinition() == typeof(IHandler<>)
&& x.GenericTypeArguments[0] == domainEvent.GetType());
}
如您所见,DomainEvents 类初始化了正在执行的程序集的所有域事件。 Dispatch 方法在我的域的自定义 DbContext 的覆盖 SaveChanges() 方法中调用。我在这里调用 dispatch 是为了在一个工作单元的事务中调度所有事件:
public override int SaveChanges()
{
DomainEventsDispatcher.Dispatch(ChangeTracker);
return base.SaveChanges();
}
以及DomainEventDispatcher的实现:
public static class DomainEventsDispatcher
{
public static void Dispatch(ChangeTracker changeTracker)
{
var domainEvents = GetDomainEventEntities(changeTracker);
HandleDomainEvents(domainEvents);
}
private static IEnumerable<IEntity> GetDomainEventEntities(ChangeTracker changeTracker)
{
return changeTracker.Entries<IEntity>()
.Select(po => po.Entity)
.Where(po => po.Events.Any())
.ToArray();
}
private static void HandleDomainEvents(IEnumerable<IEntity> domainEventEntities)
{
foreach (var entity in domainEventEntities)
{
var events = entity.Events.ToArray();
entity.Events.Clear();
DispatchDomainEvents(events);
}
}
private static void DispatchDomainEvents(IDomainEvent[] events)
{
foreach (var domainEvent in events)
{
DomainEvents.Dispatch(domainEvent);
}
}
到目前为止一切都很好,它与简单的域事件处理程序配合得很好,例如:
public class OrderCreatedEventHandler : IHandler<OrderCreatedEvent>
{
public void Handle(OrderCreatedEvent domainEvent)
{
Console.WriteLine("Order is created!");
}
}
但我还有一些其他的事件处理程序,我想在其中注入一些依赖项,即存储库:
public class OrderCreatedEventHandler : IHandler<OrderCreatedEvent>
{
private readonly IOrderHistoryRepository _orderHistoryRepository;
public OrderCreatedEventHandler(IOrderHistoryRepository orderHistoryRepository)
{
_orderHistoryRepository = orderHistoryRepository;
}
public void Handle(OrderCreatedEvent domainEvent)
{
_orderHistoryRepository.Insert(new OrderHistoryLine(domainEvent));
}
}
我的问题如下:在DomainEvents 类Dispatch 方法中,我使用Activator 类在运行时动态构造事件处理程序。在这一行会引发异常,并显示以下消息:
System.MissingMethodException: 'No parameterless constructor defined for this object.'
这是合乎逻辑的,因为在OrderCreatedEventHandler 中只有一个构造函数注入了存储库。我的问题是:是否可以在我的动态构造的处理程序中注入该存储库?如果不是,我的问题有什么解决方案或解决方法?
附加信息:
作为 IoC 框架,我使用 Autofac,并在 Startup.cs 中配置它,其中域事件也被初始化:
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMokaKukaTrackerDbContext(CurrentEnvironment, Configuration);
services.RegisterIdentityFramework();
services.AddAndConfigureMvc(CurrentEnvironment);
var autofacServiceProvider = new AutofacServiceProvider(CreateIoCContainer(services));
DomainEvents.Init();
return autofacServiceProvider;
}
private static IContainer CreateIoCContainer(IServiceCollection services)
{
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule(new AutofacInjectorBootstrapperModule());
return builder.Build();
}
如果您需要有关我的问题的更多信息/代码,请告诉我,然后我会尽快将它们包括在内。提前致谢!
【问题讨论】:
-
将
IContainer实例传递给DomainEvents.Init(),然后使用该容器创建处理程序实例 -
这只是一个想法,但也许您可以在 autofac 中注册所有事件处理程序及其依赖项,并使用 autofac 容器而不是使用 Activator 来解决。 Autofac 能够自动选择正确的构造函数。 Autofac 很强大。
-
首先,你根本不应该使用这样的静态类,它是一种反模式。只需将其创建为常规类(带有接口)并将其注入到您的上下文中。如果您想使用一次性的作用域或瞬态服务(DbContext 是,因为它的默认生命周期是作用域的),那么您当前的方法存在很大问题。最后但同样重要的是,
DomainEventDispatcher不应该依赖于 EntityFramework,这样混合和紧密耦合它违反了关注点 -
@ConstantinGalbenu 和 bart-256 感谢 cmets,可能是解决此问题的方法!但根据 Tseng 的回答,整个实现存在很大问题。
-
这不是大问题,你只需要不使用静态/全局变量;做一些重构,应该没问题。
标签: c# asp.net-core domain-driven-design autofac