Reputation: 550
I have a bunch of events which are processed by event handlers such as the following:
Event Handler:
public class DeliveryEventHandlers :
IConsume<DeliveryCreated>,
IConsume<DeliveryUpdated>{
readonly IDocumentSession _documentSession;
public DeliveryEventHandlers(IDocumentSession documentSession)
{
_documentSession = documentSession;
}
public void Consume(DeliveryCreated @event)
{
//...
}
public void Consume(DeliveryUpdated @event)
{
//...
}
...
Events:
public class DeliveryCreated : IEvent
{
public Guid DeliveryId { get; set; }
...
}
public class DeliveryUpdated : IEvent
{
public Guid DeliveryId { get; set; }
...
}
And I need to write a Ninject binding that will on request of an Event type, give me the Event handler(s) that consume those events. This is what I have come up with:
public void BindEventHandlers(IContext context) {
Kernel.Bind(x =>
{
x.FromAssemblyContaining(typeof(DeliveryCreated))
.SelectAllClasses()
.InheritedFrom<IEvent>()
.BindWith(new EventBindingGenerator());
});
}
public class EventBindingGenerator : IBindingGenerator
{
public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
{
yield return bindingRoot.Bind(x =>
{
x.FromAssemblyContaining(typeof(DeliveryEventHandlers))
.SelectAllClasses()
.InheritedFrom(IConsume<typeof(type)>);
// This Part ^
});
}
}
but this will not actually compile - I have hit a snag on the line above the comment: // This Part ^
I need to do a query for:
_context.Get<DeliveryCreated>()
and recieive a DeliveryCreatedEventHandler.
Any help would be muchos appreciated!!
Thanks, H
Upvotes: 1
Views: 318
Reputation: 13233
This can actually be solved quite easily with the conventions extension functionality:
kernel.Bind(x => x.FromAssemblyContaining(typeof(DeliveryCreated))
.IncludingNonePublicTypes() // may not be needed in your case
.SelectAllClasses()
.InheritedFrom(typeof(IConsume<>))
.BindAllInterfaces());
the following test succeeds (syntax is from FluentAssertions):
kernel.Get<IConsume<DeliverCreated>>().Should().BeOfType<DeliveryEventHandlers>();
kernel.Get<IConsume<DeliveryUpdated>>().Should().BeOfType<DeliveryEventHandlers>();
Alternatively, if you want to make sure you don't want to bind the IConsume<...>
implementations to more types than necessary, you can replace the BindAllInterfaces
statement as follows:
private static IEnumerable<Type> SelectConsumeInterfacesOnly(
Type type, IEnumerable<Type> baseTypes)
{
var matchingTypes = baseTypes.Where(t =>
t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof (IConsume<>));
return matchingTypes;
}
kernel.Bind(x => x.FromThisAssembly()
.IncludingNonePublicTypes()
.SelectAllClasses()
.InheritedFrom(typeof(IConsume<>))
.BindSelection(SelectConsumeInterfacesOnly));
Again, I've verified that it actually works.
Upvotes: 1
Reputation: 27861
You can manually scan the assembly and register all classes that implement the IConsumer<>
interface. Like this:
foreach (Type type in assembly.GetTypes().Where(x => x.IsClass))
{
foreach (
var @interface in
type.GetInterfaces()
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IConsume<>)))
{
kernel.Bind(@interface).To(type);
}
}
If you know that there will be a single consumer of the event, use the following:
var single_consumer = kernel.Get<IConsume<DeliveryCreated>>();
In case that there can be multiple consumers, use the following to obtain all of them:
var all_consumers = kernel.GetAll<IConsume<DeliveryCreated>>();
Upvotes: 0