我在解决实现通用接口的类型时遇到问题。以下是事件类,通用接口和实现通用接口的类。
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; }
我仍然不确定如何解决此问题。
答案 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
}
}