I'm trying to introduce domain events into a project. The concept is described in Udi Dahan's post - http://www.udidahan.com/2009/06/14/domain-events-salvation/
Here's the domain event code
public interface IDomainEvent { }
public interface IHandleDomainEvents<T> where T : IDomainEvent
{
void Handle(T args);
}
public interface IEventDispatcher
{
void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent;
}
public static class DomainEvents
{
public static IEventDispatcher Dispatcher { get; set; }
public static void Raise<TEvent>(TEvent eventToRaise) where TEvent : IDomainEvent
{
Dispatcher.Dispatch(eventToRaise);
}
}
The most important part is the IEventDispatcher implementation that decouples the domain event from whatever needs to happen when the event is raised. The trick is to wire up this coupling through a container. Here's my attempt
Code for Registering all Domain event handlers....
var asm = Assembly.GetExecutingAssembly();
var handlerType = typeof(IHandleDomainEvents<>);
builder.RegisterAssemblyTypes(asm)
.Where(t => handlerType.IsAssignableFrom(t)
&& t.IsClass
&& !t.IsAbstract)
.AsClosedTypesOf(handlerType)
.InstancePerLifetimeScope();
And resolving all the event handlers in the dispatcher. The problem is no handlers are resolved.
public class EventDispatcher : IEventDispatcher
{
private readonly IContainer _container;
public EventDispatcher(IContainer container)
{
_container = container;
}
public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent
{
var handlers = _container.Resolve<IEnumerable<IHandleDomainEvents<TEvent>>>().ToList();
handlers.ForEach(handler => handler.Handle(eventToDispatch));
}
}
So I'm not registering the event handlers correctly or not resolving them. How do I check that the registration is working?
Example code of a handler
public class SendWebQuestionToCSO : IHandleDomainEvents<JobNoteCreated>
{
private readonly IConfig _config;
public SendWebQuestionToCSO(IConfig config)
{
_config = config;
}
public void Handle(JobNoteCreated args)
{
var jobNote = args.JobNote;
using(var message = new MailMessage())
{
var client = new SmtpClient {Host = _config.SmtpHostIp};
message.From = new MailAddress(_config.WhenSendingEmailFromWebProcessUseThisAddress);
...... etc
}
}
}
UPDATE After some trial and error the EventDispatcher is working ok! If I manually register a handler and then fire the domain event it works. The assembly scanning/registraion is my problem. The manual registration code...
builder.RegisterType<SendWebQuestionToCSO >().As<IHandleDomainEvents<JobNoteCreated>>();
So how do I scan all assemblies for all IHandleDomainEvents<>
given they look like this
public class SendWebQuestionToCSO : IHandleDomainE开发者_如何学Pythonvents<JobNoteCreated>
As Peter pointed out, the registration problem was with your Where()
clause.
When scanning assemblies Autofac filters components automatically based on the services you specify, so it would be equally correct to just use:
builder.RegisterAssemblyTypes(asm)
.AsClosedTypesOf(handlerType)
.InstancePerLifetimeScope();
The problem in your assembly scanning code is when you use IsAssignableFrom
. The filter will ask: "could an instance of SendWebQuestionToCSO
be assigned to a variable of IHandleDomainEvents<>
?"
The answer is obviously "no" since you can never have a variable of an open generic type.
The trick would be to inspect the interfaces implemented by each type and check whether any of them are closing the open generic interface type. Here's a revised scanner:
var asm = Assembly.GetExecutingAssembly();
var handlerType = typeof(IHandleDomainEvents<>);
builder.RegisterAssemblyTypes(asm)
.Where(t => t.GetInterfaces().Any(t => t.IsClosedTypeOf(handlerType)))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
精彩评论