gpt4 book ai didi

C# 使用动态构造的事件处理程序处理 DDD 域事件

转载 作者:行者123 更新时间:2023-11-30 15:16:07 26 4
gpt4 key购买 nike

我正在构建一个使用 ASP.NET Core 2.0 和 EF Core 2.0 的应用程序。至于在我的领域中解耦不同类型的逻辑,我使用 DDD(领域驱动设计)的领域事件。让我们深入实现,看看我有什么,然后我将讨论我的问题。首先让我们看看我的领域事件相关类的通用实现。首先是一个标记接口(interface),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() 方法中调用。我在这里调用调度是为了在一个工作单元的事务中调度所有事件:

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

但是我有一些其他的事件处理程序,我想在其中注入(inject)一些依赖项,即存储库:

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

我的问题如下:在 DomainEventsDispatch 方法中,我使用 Activator 类在运行时动态构造事件处理程序。在此行抛出异常并显示以下消息:

System.MissingMethodException: 'No parameterless constructor defined for this object.'

这是合乎逻辑的,因为在 OrderCreatedEventHandler 中只有一个构造函数注入(inject)了存储库。我的问题是:是否可以在我动态构造的处理程序中注入(inject)该存储库?如果不是,有什么解决方案或解决方法可以解决我的问题?

附加信息:

作为 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();
}

如果您需要有关我的问题的更多信息/代码,请告诉我,我会尽快将它们包括在内。提前致谢!

最佳答案

我决定按照@Devesh Tipe 的要求放置问题的最终解决方案。批准的解决方案解决了我的问题,但我在我的代码库中进行了多次重构,以便以更优雅的方式处理域事件。使用以下解决方案,我们能够创建具有依赖关系的域处理程序,这些依赖关系通过 Autofac 依赖框架在运行时解析。让我们深入研究代码,包括整个解决方案:

首先我有一个领域事件的标记接口(interface):

public interface IDomainEvent
{
}

然后我有一个用于域处理程序的接口(interface):

public interface IHandler<in T> where T : IDomainEvent
{
void Handle(T domainEvent);
}

此外,我还有一个 EventDispatcher 负责分派(dispatch)/处理一个事件:

public class EventDispatcher : IEventDispatcher
{
private readonly ILifetimeScope _lifetimeScope;

public EventDispatcher(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}

public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent
{
foreach (dynamic handler in GetHandlers(eventToDispatch))
{
handler.Handle((dynamic)eventToDispatch);
}
}

private IEnumerable GetHandlers<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent
{
return (IEnumerable) _lifetimeScope.Resolve(
typeof(IEnumerable<>).MakeGenericType(
typeof(IHandler<>).MakeGenericType(eventToDispatch.GetType())));
}
}

如您所见,此处检索并调用了相应的处理程序及其已解析的依赖项。此调度程序用于执行程序类,例如:

public class DomainEventHandlingsExecutor : IDomainEventHandlingsExecutor
{
private readonly IEventDispatcher _domainEventDispatcher;

public DomainEventHandlingsExecutor(IEventDispatcher domainEventDispatcher)
{
_domainEventDispatcher = domainEventDispatcher;
}

public void Execute(IEnumerable<IEntity> domainEventEntities)
{
foreach (var entity in domainEventEntities)
{
var events = entity.Events.ToArray();
entity.Events.Clear();

foreach (var @event in events)
{
_domainEventDispatcher.Dispatch(@event);
}
}
}
}

注入(inject)到我的数据库上下文中:

    public MokaKukaTrackerDbContext(DbContextOptions<MokaKukaTrackerDbContext> options, IDomainEventHandlingsExecutor domainEventHandlingsExecutor) : base(options)
{
_domainEventHandlingsExecutor = domainEventHandlingsExecutor;
}

public override int SaveChanges()
{
var numberOfChanges = base.SaveChanges();

_domainEventHandlingsExecutor.Execute(GetDomainEventEntities());

return numberOfChanges;
}

private IEnumerable<IEntity> GetDomainEventEntities()
{
return ChangeTracker.Entries<IEntity>()
.Select(po => po.Entity)
.Where(po => po.Events.Any())
.ToArray();
}

最后但同样重要的是,我创建了 AutofacModule,我在其中注册了与域事件处理相关的所有处理程序和逻辑:

public class AutofacEventHandlingBootstrapperModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<EventDispatcher>().As<IEventDispatcher>().InstancePerLifetimeScope();
builder.RegisterType<DomainEventHandlingsExecutor>().As<IDomainEventHandlingsExecutor>().InstancePerLifetimeScope();

RegisterEventHandlersFromDomainModel(builder);
}

private static void RegisterEventHandlersFromDomainModel(ContainerBuilder builder)
{
var domainModelExecutingAssembly = new DomainModelExecutingAssemblyGetter().Get();

builder.RegisterAssemblyTypes(domainModelExecutingAssembly)
.Where(t => t.GetInterfaces().Any(i => i.IsClosedTypeOf(typeof(IHandler<>))))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
}
}

这当然必须在 Startup.cs 中注册:

   public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMokaKukaTrackerDbContext(CurrentEnvironment, Configuration);

return new AutofacServiceProvider(CreateIoCContainer(services));
}

private static IContainer CreateIoCContainer(IServiceCollection services)
{
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterModule(new AutofacInjectorBootstrapperModule());
builder.RegisterModule(new AutofacEventHandlingBootstrapperModule());

return builder.Build();
}

就是这样,希望对大家有帮助!

关于C# 使用动态构造的事件处理程序处理 DDD 域事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50438497/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com