我正在尝试使用Domain Events
来通知系统中发生了什么事情(借鉴here和here)。
我真的很接近让代码按照我想要的方式工作,但是,我已经碰到了一些砖墙。这是我的DomainEvents
课程:
public static class DomainEvents
{
[ThreadStatic]
private static IList<IEventHandler<IDomainEvent>> Actions;
public static void Register<T>(IEventHandler<T> callback) where T : IDomainEvent
{
if (Actions == null)
{
Actions = new List<IEventHandler<IDomainEvent>>();
}
Actions.Add(callback); // <---- Problem here, since I can't add callback to the collection.
}
public static void ClearCallbacks()
{
Actions = null;
}
public static void Raise<T>(T args) where T : IDomainEvent
{
if (Actions == null)
{
return;
}
foreach (var action in Actions)
{
if (action is IEventHandler<T>)
{
((IEventHandler<T>)action).Handle(args);
}
}
}
上述内容无法编译,因为Actions.Add
无法接受callback
,因为它是IEventHandler<T>
类型,而不是IEventHandler<IDomainEvent>
类型。这里有一些代码需要澄清。
这是从我的控制台应用程序中调用的:
DomainEvents.Register(new CustomerHasUnpaidDuesEventHandler());
CustomerHasUnpaidDuesEventHandler
实施IEventHandler<CustomerHasUnpaidDuesEvent>
,其中CustomerHasUnpaidDuesEvent
实施IDomainEvent
。
public class CustomerHasUnpaidDuesEventHandler : IEventHandler<CustomerHasUnpaidDuesEvent>
{
public IEmailSender EmailSender { get; set; }
public void Handle(CustomerHasUnpaidDuesEvent @event)
{
this.EmailSender.SendEmail(@event.Customer.EmailAddress);
}
}
public class CustomerHasUnpaidDuesEvent : IDomainEvent
{
public CustomerHasUnpaidDuesEvent(Customer customer)
{
this.Customer = customer;
}
public Customer Customer { get; set; }
}
这是我没有得到的 - 如果CustomerHasUnpaidDuesEvent
实施IDomainEvent
,那么为什么对Actions.Add
的调用失败?我该如何解决这个问题?
编辑:
为了使事情更清楚,这里是我的测试应用程序的完整代码:
class Program
{
static void Main()
{
DomainEvents.Register(new CustomerHasUnpaidDuesEventHandler());
var c = new Customer();
c.EmailAddress = "test@dfsdf.com";
c.CheckUnpaidDues();
}
}
public interface IEventHandler<in T> where T : IDomainEvent
{
void Handle(T args);
}
public interface IEmailSender
{
void SendEmail(string emailAddress);
}
public interface IDomainEvent
{
}
public static class DomainEvents
{
[ThreadStatic]
private static IList<IEventHandler<IDomainEvent>> Actions;
public static void Register<T>(IEventHandler<T> callback) where T: IDomainEvent
{
if (Actions == null)
{
Actions = new List<IEventHandler<IDomainEvent>>();
}
Actions.Add(callback);
}
public static void ClearCallbacks()
{
Actions = null;
}
public static void Raise<T>(T args) where T : IDomainEvent
{
if (Actions == null)
{
return;
}
foreach (IEventHandler<T> action in Actions)
{
(action).Handle(args);
}
}
}
public class CustomerHasUnpaidDuesEventHandler : IEventHandler<CustomerHasUnpaidDuesEvent>
{
public IEmailSender EmailSender { get; set; }
public void Handle(CustomerHasUnpaidDuesEvent @event)
{
this.EmailSender.SendEmail(@event.Customer.EmailAddress);
}
}
public class CustomerHasUnpaidDuesEvent : IDomainEvent
{
public CustomerHasUnpaidDuesEvent(Customer customer)
{
this.Customer = customer;
}
public Customer Customer { get; set; }
}
public class Customer
{
public string Name { get; set; }
public string EmailAddress { get; set; }
public bool HasUnpaidDues { get; set; }
public void CheckUnpaidDues()
{
HasUnpaidDues = true;
DomainEvents.Raise(new CustomerHasUnpaidDuesEvent(this));
}
}
干杯。 雅各
答案 0 :(得分:1)
编辑:
问题是,为了让IEventHandler<CustomerHasUnpaidDuesEvent>
位于IEventHandler<IDomainEvent>
列表中,我们需要T
作为IEventHandler<T>
中的协变模板参数(声明为IEventHandler<out T>
)。但是,为了允许函数Handle(T arg)
,我们需要T
逆变。严格来说,这种方式不起作用。想象一下:如果我们真的可以将IEventHandler<CustomerHasUnpaidDuesEvent>
插入到IEventHandler<IDomainEvent>
的列表中,那么有人可能会尝试使用某种类型的参数调用Handle
,而这些参数来自IDomainEvent
但是不是CustomerHasUnpaidDuesEvent
!这应该是不可能的。
解决方案是我们不需要Register
处的确切类型,因此我们可以保留对通用基接口的引用。实施在这里:http://ideone.com/9glmQ
旧答案无效,请保持一致以保持一致。
也许您需要声明IEventHandler接受T作为协变类型?
interface IEventHandler<in T> where T: IDomainEvent
{
void Handle();
// ...
}
修改:肯定CustomerHasUnpaidDuesEvent
是IDomainEvent
,但您需要IEventHandler<CustomerHasUnpaidDuesEvent>
成为IEventHandler<IDomainEvent>
。这正是协方差所做的。为了实现这一点,必须将IEventhandler中的模板参数声明为covariant(<in T>
而不是<T>
)。
答案 1 :(得分:1)
您的Register方法不需要通用:
public static void Register(IEventHandler<IDomainEvent> callback)
{
if (Actions == null)
{
Actions = new List<IEventHandler<IDomainEvent>>();
}
Actions.Add(callback);
}