Autofac-解决通用界面

时间:2018-07-10 00:04:06

标签: generics asp.net-core autofac

我在解决实现通用接口的类型时遇到问题。以下是事件类,通用接口和实现通用接口的类。

public class ExampleEvent : IDomainEvent
{
    public ExampleEvent()
    {
        DateTimeEventOccurred = DateTime.Now;
    }

    public DateTime DateTimeEventOccurred { get; private set; }
}

public interface IHandle<T> where T : IDomainEvent
{
    void Handle(T args);
}

public class ExampleEventHandler : IHandle<ExampleEvent>
{
    public ExampleEventHandler()
    {
    }

    public void Handle(ExampleEvent args)
    {
        //Handle Event
    }
}

这是我的Autofac配置:

var builder = new ContainerBuilder();
        builder.RegisterAssemblyTypes(Assembly.Load(assembly)).AsClosedTypesOf(typeof(IHandle<>));
        builder.Populate(services);
        return new AutofacServiceProvider(builder.Build());

最后,这是包含负责引发事件的容器和方法的类:

公共类DomainEventHandler     {         公共ILifetimeScope _container {get;组; }

    public DomainEventHandler(ILifetimeScope container)
    {
        _container = container;
    }

    public void Raise<T>(T args) where T : IDomainEvent
    {
        foreach (var handler in _container.Resolve<IEnumerable<IHandle<T>>>())
        {
            handler.Handle(args);
        }
    }
}

调用Resolve方法时,结果是一个空集合。但是,如果我将泛型替换为具体类型,则容器将成功解析并提供ExampleEventHandler的实例。将TryResolve与通用属性一起使用时,将得出true值,同时输出一个空实例。

第一次实现此模式时,我开始使用StructureMap并遇到了相同的问题,因此我认为在两种情况下我都在做一些愚蠢的事情。在不使用泛型的其他许多情况下,我也能够成功使用容器。

更新:

该问题似乎与某种类型的比较有关。在我的savechanges方法中调用所有排队的域事件时,如果我传递域事件的新实例,则依赖项将解决。

例如,这将成功:

foreach (var domainEvent in entity.DeferredEvents.ToArray())
                {
                    _handler.Raise(new EstimateAuditEvent(1,1, DateTime.Now));
                }

这将失败:

foreach (var domainEvent in entity.DeferredEvents.ToArray())
                {
                    _handler.Raise(domainEvent);
                }

我在实体上的收藏如下:

public List<IDomainEvent> DeferredEvents { get; set; }

我仍然不确定如何解决此问题。

1 个答案:

答案 0 :(得分:0)

此问题是由您致电_handler.Raise引起的。如您所说,DeferredEvents存储在List<IDomainEvent>中。这将导致失败的代码如下所示:

foreach (var domainEvent in entity.DeferredEvents.ToArray())
{
    _handler.Raise<IDomainEvent>(domainEvent);
}

因此,您的Raise方法将如下所示:

public void Raise<IDomainEvent>(IDomainEvent args) where T : IDomainEvent
{
    foreach (var handler in _container.Resolve<IEnumerable<IHandle<IDomainEvent>>>())
    {
        handler.Handle(args);
    }
}

在查看您的注册时,没有实现IHandle<IDomainEvent>接口的事件处理程序。只有一种实现IHandle<ExampleEvent>接口。因此,因此您致电解决IEnumerable<IHandle<IDomainEvent>>的请求将不会产生任何结果。

更新

  

那么在那种情况下,将没有办法遍历一个接口或基类的集合吗?在上述假设下,Autofac将基于接口或基类而不是派生类型来解析实例,但是我已经在许多Domain Events示例中看到了相同的方法。

Autofac根据注册进行解析。在您的示例中,ExampleEventHandler实现了IHandle<ExampleEvent>并注册为IHandle<ExampleEvent>,因此,如果您解析ExampleEventHandler,则不会返回IEnumerable<IHandle<IDomainEvent>>的实例。

但是,我不认为注册是错误的,我认为错误在于调用Raise方法的方式。 Raise方法具有未使用的通用类型参数T(至少在您的示例中未使用)。在您的示例中,Raise方法实际上是这样的:

public void Raise(IDomainEvent args)
{
    foreach (var handler in _container.Resolve<IEnumerable<IHandle<IDomainEvent>>>())
    {
        handler.Handle(args);
    }
}

您必须更新代码,以便将IDomainEvent的派生类型用于T,以便Autofac可以找到正确的处理程序:

_handler.Raise<ExampleEvent>(domainEvent);

或者,您可以更改所有处理程序以实现IHandle<IDomainEvent>>,然后如果事件的类型与预期的类型不匹配,则不执行任何操作:

public class ExampleEventHandler : IHandle<IDomainEvent>
{
    public ExampleEventHandler()
    {
    }

    public void Handle(IDomainEvent args)
    {
        if (!(args is ExampleEvent))
            return;

        //Handle Event
    }
}