【问题标题】:Ninject Injection of all instances of a generic type with ninjectNinject 使用 ninject 注入泛型类型的所有实例
【发布时间】:2012-06-07 20:25:45
【问题描述】:

我希望能够使用 ninject 将特定泛型类型的所有实例注入到一个类中。例如,我有一堆类似于以下格式的自定义提取器:

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

我想将这些提取器的所有实例注入到一个类中,该类负责使用 ninject 多重绑定来处理此文件。

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*/
    }
}

过去,我通过一个直接访问内核的管理类 (IProvideExtractors) 来做到这一点,但我不喜欢这种方法,想知道是否有人知道更好的方法来做到这一点。使用 ninject 多重绑定,我可以使用 kernel.GetAll(typeof(IExtract&lt;&gt;)) 获得所有我感兴趣的实例

【问题讨论】:

  • Process 方法中是否需要IExtract&lt;TEntity&gt; 是通用的?因为如果不是,那么我将创建一个非通用的IExtract,而IExtract&lt;TEntity&gt; 将继承自IExtract。在ProcessDataExtract 构造函数中正确注册后,您将依赖IEnumerable&lt;IExtract&gt; allExtractors

标签: c# ninject constructor-injection


【解决方案1】:

我正在寻找相关的东西:我不想使用约定扩展单独指定所有绑定。

首先:你需要注入List&lt;IExtract&gt;并继承IExtract&lt;T&gt; : IExtract。 这仅仅是因为在 C# 中您不能指定包含不同泛型的集合的类型。正如您在问题中指出的那样,它是无效的语法 - 出于此答案之外的充分理由。

您可以稍后将IExtract 的元素从列表中拉出,并使用反射来获取泛型类型参数并将其转换回来。或者,如果您知道您在寻找什么提取器:

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

现在您可能有一堆类,您希望将一些 T 绑定到 IExtract`。

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

在哪里

MyEntity : BaseEntity
YourEntity : BaseEntity

您可以指定如下约定

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

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

【讨论】:

  • 我真的很喜欢这个绑定生成器,它看起来很酷,而且我认为你是对的,扩展非泛型将在绑定方面解决这个问题
  • 要替换什么IEnvironmentConfigFile呢?
  • 哦该死的我错过了一个 - 从我的代码中得到它;)我猜 IEnvironmentConfigFile 将是 IExtract。但它应该通过 GenericArgumentBindingGenerator 的构造函数来使其成为泛型。如上所述,您确实想绑定Bind&lt;IExtract&gt;().To&lt;ExtractImpl&lt;YourEntity&gt;&gt;();
【解决方案2】:

这个问题的答案似乎是 ninject 没有办法做到这一点

【讨论】:

【解决方案3】:

选项 A

我很确定你可以做到这一点:

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

然后在绑定模块的Load 方法中列出你的绑定:

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

NInject 会将它们传递给您的构造函数,该构造函数会宣传对它们的依赖。过去我已经成功地做到了。

选项 B

改变

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

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

这将允许:

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

成为:

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

NInject 绑定也会被调整:

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

【讨论】:

  • 是的,所以在 ninject 中有一个注入绑定的所有实例的概念,这是我想要利用的。您可以使用kernel.GetAll(typeof(IExtract&lt;&gt;)) 在上述实例中直接从ninject 执行此操作,这将返回包含我所有提取器的IEnumerable&lt;object&gt;。我的问题不在于这个,我的问题是我无法弄清楚如何在我的构造函数中指定它,因为上面的代码不是有效的 C#
  • 我更正了语法。但是,您将只能使用一个具体的TExtract。这需要是一个非泛型基类(抽象与否)。或者您可以将类型参数从IExtract 推送到ExtractFrom,这将消除ProcessDataExtract 的同质性。我会将我的意思添加到我的答案中。
  • 我想你说的是我可以使用Enumerable&lt;IExtract&lt;object&gt;&gt; allExtractors,我很确定这是不可能的,因为我不认为typeof(IExtract&lt;object&gt;).IsAssignableFrom(tyepof(IExtract&lt;MyConcreteEntity&gt;))compilify.net/1tj
  • 为了让您的反射生活更轻松,请使用编码TEntity 的具体IExtract 类的命名约定,这样您就不必每次添加新类时都进行循环。
  • 这似乎有点像解决这个问题,但肯定有一个更优雅的解决方案
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-28
  • 1970-01-01
  • 1970-01-01
  • 2011-06-06
  • 2017-01-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多