简体   繁体   English

Ninject使用ninject注入泛型类型的所有实例

[英]Ninject Injection of all instances of a generic type with ninject

I would like to be able to use ninject to inject all instances of a particular generic type into a class. 我希望能够使用ninject将特定泛型类型的所有实例注入到类中。 For example I have a bunch of custom extractors of a format similar to: 例如,我有一堆类似于以下格式的自定义提取器:

public interface IExtract<TEntity> 
{ 
    TEntity ExtractFrom(MyBulkExportedEntity exportedEntity);
}

and I want to inject all instances of these extractors into a class responsible for processing this file using ninject multiple binding. 我想将这些提取器的所有实例注入一个负责使用ninject多重绑定处理该文件的类。

ie

public class ProcessDataExtract
{
    /*This isn't valid c# but demonstrates the intent of what i would like to do*/
    public ProcessDataExtract(IEnumerable<IExtract<>> allExtractors)
    {
    }

    public void Process(MyBulkExportedEntity exportedEntity)
    {
        /*loop through all of the extractors and pull relevant data from the object*/
    }
}

In the past i have done this by having a management class (IProvideExtractors) which accesses the kernel directly but i don't like this method and was wondering if anyone knows of a better way to do this. 在过去,我通过一个管理类(IProvideExtractors)直接访问内核但我不喜欢这种方法并且想知道是否有人知道更好的方法来做到这一点。 With ninject multiple binding I can then get all of the instances im interested in using kernel.GetAll(typeof(IExtract<>)) 使用ninject多重绑定,我可以获得所有对使用kernel.GetAll(typeof(IExtract<>))感兴趣的实例kernel.GetAll(typeof(IExtract<>))

I was looking for something related: I don't wanted to specify all the bindings separately using the Convention extension. 我正在寻找相关的东西:我不想使用Convention扩展分别指定所有绑定。

First: You need to inject List<IExtract> and inherit IExtract<T> : IExtract . 第一:您需要注入List<IExtract>并继承IExtract<T> : IExtract This is simply due to the fact, that in C# you can not specify the type of a collection containing different generics. 这只是因为在C#中您无法指定包含不同泛型的集合的类型。 As you noted in your question it is invalid syntax - for a good reason beyond this answer. 正如你在问题中提到的那样,它是无效的语法 - 超出这个答案的一个很好的理由。

You can later pull the elements of IExtract out of the list and use reflection to get the generic type paramer and cast it back. 您可以稍后将IExtract的元素从列表中拉出并使用反射来获取泛型类型的paramer并将其转换回来。 Or if you know for what extractor you are looking: 或者如果你知道你正在寻找什么提取器:

public IExtract<T> GetExtractor<T>() {
    return (IExtract<T>)Extractors.Find(e => e is ExtractImpl<T>);
}

Now you might have a bunch of classes for that you want some T bound to IExtract`. 现在你可能有一堆类,你想要一些T绑定到IExtract`。

Bind<IExtract>().To<ExtractImpl<MyEntity>>();
Bind<IExtract>().To<ExtractImpl<YourEntity>>();

where 哪里

MyEntity : BaseEntity
YourEntity : BaseEntity

You can specify a Convention as following 您可以将公约指定如下

Kernel.Bind(x => x.FromThisAssembly().SelectAllClasses()
    .InheritedFrom<BaseEntity>()
    .BindWith(new GenericArgumentBindingGenerator(typeof(IExtract<>))));

Where GenericArgumentBindingGenerator is defind as: GenericArgumentBindingGenerator的定义为:

public class GenericArgumentBindingGenerator : IBindingGenerator
{
    private readonly Type m_Generic;

    public GenericArgumentBindingGenerator(Type generic)
    {
        if (!generic.IsGenericTypeDefinition)
        {
            throw new ArgumentException("given type must be a generic type definition.", "generic");
        }
        m_Generic = generic;
    }

    public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (bindingRoot == null)
            throw new ArgumentNullException("bindingRoot");
        if (type.IsAbstract || type.IsInterface)
        {
            return Enumerable.Empty<IBindingWhenInNamedWithOrOnSyntax<object>>();
        }

        var bindings = new List<IBindingWhenInNamedWithOrOnSyntax<object>>();
        IBindingWhenInNamedWithOrOnSyntax<object> binding = bindingRoot
            .Bind(typeof(IExtract)) // you maybe want to pass typeof(IExtract) to constructor
            .To(m_Generic.MakeGenericType(type));

        bindings.Add(binding);

        return bindings;
    }
}

对此的答案似乎是没有办法用ninject做到这一点

Option A 选项A.

I'm pretty sure you can do this: 我很确定你能做到这一点:

public class ProcessDataExtract
{
    public ProcessDataExtract<TExtract>(IEnumerable<IExtract<TExtract>> allExtractors)
    {
    }
    ...etc...
}

And then list out your bindings in your binding module's Load method: 然后在绑定模块的Load方法中列出您的绑定:

...
Bind<IExtract<TEntity>>().To<SomeConcreteExtract>();
Bind<IExtract<TEntity>>().To<AnotherConcreteExtract>();
Bind<IExtract<TEntity>>().To<YetAnotherConcreteExtract>();
...

And NInject will deliver them to your constructor that advertises a dependency on a bunch of them. NInject会将它们传递给你的构造函数,这些构造函数会对它们的一堆广告进行广告宣传。 I've done that in the past with success. 我过去就成功了。

Option B 选项B.

Change 更改

public interface IExtract<TEntity> 
{ 
    TEntity ExtractFrom(MyBulkExportedEntity exportedEntity);
}

to

public interface IExtract
{ 
    TEntity ExtractFrom<TEntity>(MyBulkExportedEntity exportedEntity);
}

Which would allow: 这将允许:

        public ProcessDataExtract<TExtract>(IEnumerable<IExtract<TExtract>> allExtractors)
    {
    }
    ...etc...

to be: 成为:

    public ProcessDataExtract(IEnumerable<IExtract> allExtractors)
    {
    }
    ...etc...

And the NInject bindings would be adjusted, too: NInject绑定也会被调整:

...
Bind<IExtract>().To<SomeConcreteExtract>();
Bind<IExtract>().To<AnotherConcreteExtract>();
Bind<IExtract>().To<YetAnotherConcreteExtract>();
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM