[英]Circular component dependency detected Repository pattern with autofac dependency injections
[英]Autofac Circular Component Dependency - Generic Repository
我想向Autofac注冊我的服務,但是我有一個循環依賴關系,這會引發錯誤。 基本上,我將引發一些事件,這些事件將由事件處理程序處理。 有一個域事件分派器,它將這些事件分派給事件處理程序,這將在實體框架的SaveChanges方法中發生。 圖形如下:
現在,我有四個事件處理程序(OrderCreatedEventHandler,OrderStatusChangedEventHandler,OrderDispatchedEventHandler,PaymentReceivedEventHandler),並且還會添加更多事件處理程序。 所有這些事件處理程序都將向客戶配置的電子郵件/短信發送給收件人。
在將域事件分派器的構造函數依賴項添加到dbcontext之前,我的解決方案運行良好。 我的實現如下:通用存儲庫接口:
public interface IAsyncRepository<T> where T : BaseEntity
{
// Removed for brevity
}
通用存儲庫實現:
public class EfRepository<T> : IAsyncRepository<T> where T : BaseEntity
{
protected readonly MyDbContext _dbContext;
public EfRepository(MyDbContext dbContext)
{
_dbContext = dbContext;
}
}
IDomainEventDispatcher:
public interface IDomainEventDispatcher
{
void Dispatch<T>(T domainEvent) where T : BaseDomainEvent;
}
IDomainEventHandler:
public interface IDomainEventHandler
{
Task HandleAsync<T>(T domainEvent) where T : BaseDomainEvent;
bool AppliesTo(Type provider);
}
DomainEventHandler:
public abstract class DomainEventHandler<T> : IDomainEventHandler where T: BaseDomainEvent
{
public bool AppliesTo(Type handler)
{
return typeof(T).Equals(handler);
}
public async Task HandleAsync<X>(X domainEvent) where X : BaseDomainEvent
{
await HandleAsync((T)(object)domainEvent);
}
protected abstract Task HandleAsync(T domainEvent);
}
OrderCreatedEventHandler:
public class OrderCreatedEventHandler : DomainEventHandler<OrderCreatedEvent>
{
private readonly IAsyncRepository<EmailTemplate> _emailTemplate;_notificationRecipientRepository;
public OrderCreatedEventHandler(IAsyncRepository<EmailTemplate> emailTemplate)
{
_notificationRecipientRepository = notificationRecipientRepository;
}
protected override async Task HandleAsync(OrderCreatedEvent domainEvent)
{
// Implementation removed
}
}
OrderDispatchedEventHandler:
public class OrderDispatchedEventHandler : DomainEventHandler<OrderDispatchedEvent>
{
private readonly IAsyncRepository<NotificationRecipient> _notificationRecipientRepository;
public OrderDispatchedEventHandler(IAsyncRepository<NotificationRecipient> notificationRecipientRepository)
{
_notificationRecipientRepository = notificationRecipientRepository;
}
protected override async Task HandleAsync(OrderDispatchedEvent domainEvent)
{
// Implementation removed
}
}
DomainEventDispatcher:
public class DomainEventDispatcher : IDomainEventDispatcher
{
private readonly IEnumerable<IDomainEventHandler> _eventHandlers;
public DomainEventDispatcher(IEnumerable<IDomainEventHandler> eventHandlers)
{
_eventHandlers = eventHandlers ?? throw new ArgumentNullException(nameof(eventHandlers));
}
public void Dispatch<T>(T domainEvent) where T : BaseDomainEvent
{
IDomainEventHandler eventHandler = GetEventHandler(domainEvent);
eventHandler.HandleAsync(domainEvent);
}
private IDomainEventHandler GetEventHandler<T>(T model) where T : BaseDomainEvent
{
IDomainEventHandler handler = _eventHandlers.FirstOrDefault(p => p.AppliesTo(model.GetType()));
if (handler == null)
{
throw new InvalidOperationException($"Event handler for {model.GetType().ToString()} not registered.");
}
return handler;
}
}
MyDbContext:
public class MyDbContext : DbContext
{
private readonly IDomainEventDispatcher _domainEventDispatcher;
public MyDbContext(IDomainEventDispatcher domainEventDispatcher)
: base("MyDbContextConnection", throwIfV1Schema: false)
{
_domainEventDispatcher = domainEventDispatcher;
}
public override async Task<int> SaveChangesAsync()
{
try
{
var result = await base.SaveChangesAsync();
var entitiesWithEvents = ChangeTracker.Entries<BaseEntity>()
.Select(e => e.Entity)
.Where(e => e.Events.Any())
.ToArray();
foreach (var entity in entitiesWithEvents)
{
var events = entity.Events.ToArray();
entity.Events.Clear();
foreach (var domainEvent in events)
{
_domainEventDispatcher.Dispatch(domainEvent);
}
}
return result;
}
catch (DbUpdateException ex)
{
//Removed for brevity
}
}
}
}
Web API中的Autofac注冊:
builder.RegisterType<MyDbContext>()
.InstancePerRequest();
builder.RegisterGeneric(typeof(EfRepository<>))
.As(typeof(IAsyncRepository<>))
.InstancePerRequest();
builder.RegisterType<OrderCreatedEventHandler>()
.As<IDomainEventHandler>();
builder.RegisterType<OrderDispatchedEventHandler>()
.As<IDomainEventHandler>();
builder.RegisterType<OrderStatusChangedEventHandler>()
.As<IDomainEventHandler>();
builder.RegisterType<PaymentEventHandler>()
.As<IDomainEventHandler>();
builder.RegisterType<DomainEventDispatcher>()
.As<IDomainEventDispatcher>();
根據他們的文檔,Autofac不支持循環構造函數依賴項:
不支持具有循環構造函數依賴關系的兩種類型。 當您嘗試解析以這種方式注冊的類型時,您將獲得異常。
如何改善我的設計,使Autofac變得“幸福”?
幫助您解決這類問題的一件事是,當您將重要部分歸結為問題時。 就像, 真的,真的把它煮下來。 您在循環依賴方面遇到了問題,這些都與類和構造函數有關,所以事情的實質是:
public interface IAsyncRepository<T> where T : BaseEntity
{
}
public class EfRepository<T> : IAsyncRepository<T> where T : BaseEntity
{
public EfRepository(MyDbContext dbContext)
{
}
}
public interface IDomainEventDispatcher
{
}
public interface IDomainEventHandler
{
}
public abstract class DomainEventHandler<T> : IDomainEventHandler where T: BaseDomainEvent
{
}
public class OrderCreatedEventHandler : DomainEventHandler<OrderCreatedEvent>
{
public OrderCreatedEventHandler(IAsyncRepository<EmailTemplate> emailTemplate)
{
}
}
public class OrderDispatchedEventHandler : DomainEventHandler<OrderDispatchedEvent>
{
public OrderDispatchedEventHandler(IAsyncRepository<NotificationRecipient> notificationRecipientRepository)
{
}
}
public class DomainEventDispatcher : IDomainEventDispatcher
{
public DomainEventDispatcher(IEnumerable<IDomainEventHandler> eventHandlers)
{
}
}
public class MyDbContext : DbContext
{
public MyDbContext(IDomainEventDispatcher domainEventDispatcher)
{
}
}
那里的方法,接口的實現等並不是很重要。
下一步是從方程式中刪除Autofac。 Autofac基本上是調用new SomeClass()
和連接依賴項的一種好方法。
如果您必須自己制作其中之一,該怎么辦? 你不能
讓我們刪除接口並使用具體的類名:
EfRepository<T>
需要MyDbContext
MyDbContext
需要DomainEventDispatcher
DomainEventDispatcher
需要所有處理程序,但 EfRepository<T>
如果嘗試嘗試,則無法重新創建任何這些內容,因為存在循環依賴性。
有幾種方法可以解決這類問題,並且都需要進行一些設計
重要的是考慮一下如果必須手動創建該怎么辦。 如果不是您可以手動解決的問題,Autofac將無法解決。
Autofac擁有有關循環依賴關系的文檔,該文檔顯示了如何使用屬性注入來打破循環依賴關系。 還有關於屬性和方法注入的文檔 ,可以幫助並顯示示例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.