簡體   English   中英

Autofac循環組件依賴關系-通用存儲庫

[英]Autofac Circular Component Dependency - Generic Repository

我想向Autofac注冊我的服務,但是我有一個循環依賴關系,這會引發錯誤。 基本上,我將引發一些事件,這些事件將由事件處理程序處理。 有一個域事件分派器,它將這些事件分派給事件處理程序,這將在實體框架的SaveChanges方法中發生。 圖形如下:

  1. 實體框架dbcontext具有域事件分派器的構造函數依賴項。
  2. 域事件分派器具有事件處理程序的構造函數依賴項。
  3. 事件處理程序具有通用存儲庫的構造函數依賴性,該通用存儲庫具有dbcontext。

現在,我有四個事件處理程序(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>

如果嘗試嘗試,則無法重新創建任何這些內容,因為存在循環依賴性。

有幾種方法可以解決這類問題,並且都需要進行一些設計

  1. 分手吧 有時,類具有僅由一種方法使用的依賴項。 將其分為兩類。 這增加了一些責任分離,並可以使用不會在鏈中造成循環依賴的那一半。 要考慮的另一件事可能是事件調度程序可以使用不會引起循環依賴的其他存儲庫或存儲庫類型。
  2. 使用方法參數。 例如,如果事件調度程序將存儲庫作為需要該存儲庫的各種方法的參數,而不是將其作為依賴項,該怎么辦? 它可能不那么漂亮,但是會打破圈子。
  3. 使用公共可設置屬性。 除了將存儲庫作為調度程序上的構造函數參數之外,還可以設置一個公共的“ Repository”屬性。 您可以創建調度程序,然后在以后設置屬性。 施工中無圈。

重要的是考慮一下如果必須手動創建該怎么辦。 如果不是您可以手動解決的問題,Autofac將無法解決。

Autofac擁有有關循環依賴關系的文檔,文檔顯示了如何使用屬性注入來打破循環依賴關系。 還有關於屬性和方法注入的文檔 ,可以幫助並顯示示例。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM