Ninject映射到泛型类型

时间:2015-09-08 15:27:24

标签: c# dependency-injection ninject inversion-of-control

我有一堆事件由事件处理程序处理,如下所示:

事件处理程序:

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)
{
    //...
}
...

活动:

public class DeliveryCreated : IEvent
{
    public Guid DeliveryId { get; set; }
    ...
}

public class DeliveryUpdated : IEvent
{
    public Guid DeliveryId { get; set; }
    ...
}

我需要编写一个Ninject绑定,根据Event类型的请求,给我一个消耗这些事件的事件处理程序。这就是我想出的:

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 ^
            });
        }
    }

但这实际上不会编译 - 我在评论上面的一行上遇到了障碍://这部分^

我需要查询:

_context.Get<DeliveryCreated>() 

并收到一个DeliveryCreatedEventHandler。

任何帮助都会受到赞赏!!

谢谢, ħ

2 个答案:

答案 0 :(得分:1)

使用约定扩展功能实际上可以很容易地解决这个问题:

kernel.Bind(x => x.FromAssemblyContaining(typeof(DeliveryCreated))
    .IncludingNonePublicTypes() // may not be needed in your case
    .SelectAllClasses()
    .InheritedFrom(typeof(IConsume<>))
    .BindAllInterfaces());

以下测试成功(语法来自FluentAssertions):

kernel.Get<IConsume<DeliverCreated>>().Should().BeOfType<DeliveryEventHandlers>();
kernel.Get<IConsume<DeliveryUpdated>>().Should().BeOfType<DeliveryEventHandlers>();

或者,如果您想确保不希望将IConsume<...>实现绑定到不必要的更多类型,则可以按如下方式替换BindAllInterfaces语句:

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));

同样,我已经确认它确实有效。

答案 1 :(得分:0)

您可以手动扫描程序集并注册实现IConsumer<>接口的所有类。像这样:

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);
    }
}

如果您知道该活动的单个消费者,请使用以下内容:

var single_consumer = kernel.Get<IConsume<DeliveryCreated>>();

如果可能有多个消费者,请使用以下内容获取所有消费者:

var all_consumers = kernel.GetAll<IConsume<DeliveryCreated>>();