Я создаю приложение, используя ASP.NET Core 2.0 с EF Core 2.0.Что касается разделения различных видов логики в моем домене, я использую доменные события DDD (Domain Driven Design).Давайте углубимся в реализацию и посмотрим, что у меня есть, тогда я буду обсуждать мою проблему.Прежде всего, давайте посмотрим на общую реализацию моих классов, связанных с событиями домена.Во-первых, интерфейс маркера, 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
вызывается в переопределенном методе SaveChanges()
моего пользовательского DbContext
домена.Я вызываю диспетчеризацию здесь для того, чтобы отправлять все события в транзакции единицы работы:
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!");
}
}
Но у меня есть некоторые другие обработчики событий, в которые я хотел бы внедрить некоторую зависимость, а именно Repository:
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
class Dispatch
метод я использую Activator
class для динамического конструирования обработчиков событий во время выполнения.В этой строке выдается исключение со следующим сообщением:
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();
}
Если вам нужна дополнительная информация / код о моей проблеме, дайте мне знать, тогда я включу их как можно скорее.Заранее спасибо!