9

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

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

我想将这些提取器的所有实例注入到一个类中,该类负责使用 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*/
    }
}

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

4

3 回答 3

4

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

首先:您需要注入List<IExtract>和继承IExtract<T> : 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;
    }
}
于 2013-12-14T11:25:29.983 回答
3

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

于 2012-06-18T09:18:11.057 回答
1

选项 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>();
...
于 2012-06-07T20:41:48.403 回答