如何使用Ninject Conventions扩展将泛型类型与继承绑定

时间:2013-03-06 22:14:46

标签: ninject ioc-container ninject-extensions open-generics ninject-conventions

如何使用Ninject ConventionsInitializerForXXX(非泛型实现)绑定到IInitializer<XXX>(通用接口),以便IInitializer<T>的请求解析名称开始的非泛型实现使用 InitializerFor 并以typeof(T).Name结尾,如:

initializerFactory.CreateFor<Blue>();        //resolves InitializerOfBlue
initializerFactory.CreateFor<ShadeOfBlue>(); //resolves InitializerOfShadeOfBlue

其中没有非抽象类直接实现IInitializer<T>,而某些实现继承自其他实现:

  • InitializerForShadeOfBlue继承自InitializerForBlue
  • InitializerForBlue继承自摘要Initializer<Blue>
  • abstract Initializer<T>直接实现IInitializer<T>

我希望我可以使用.EndsWith(typeof(T).Name)来实现我可以使用的IInitializer<T>约定,因为 ShadeOfxxx 静脉中有数百个初始值设定项。如果我必须映射所有这些,我最好找到一种在运行时使用反射解决的方法。

鉴于以下内容:

UPDATE:与自定义绑定生成器的绑定(请参阅下面的答案以实现)

    void Bootstrap(IBindingRoot kernel)
    {
        kernel.Bind<IInitializerFactory>()
            .To<InitializerFactory>()
            .InSingletonScope();

        kernel.Bind(scanner =>
                    scanner.FromThisAssembly().SelectAllClasses()
                        .WhichAreNotGeneric()
                        .InheritedFrom(typeof(IComplexContent))
                        .BindAllInterfaces());

        kernel.Bind(scanner =>
                    scanner.FromThisAssembly().SelectAllClasses()
                        .WhichAreNotGeneric()
                        .InheritedFrom(typeof(IInitializer<>))
                        .BindWith<FirstTypeParameterNameMatchesEndOfBoundClassNameGenerator>());
    }

主要方法

void Main(IEnumerable<string> values)
{
    // setup bindings
    var kernel = new StandardKernel();
    Bootstrap(kernel);

    IInitializerFactory initializerFactory = 
        kernel.Get<IInitializerFactory>();

    IInitializer<ShadeOfBlueComplexContent> initializer = 
        initializerFactory.CreateFor<ShadeOfBlueComplexContent>();

    initializer.Initialize(values);
}

初始化工厂

interface IInitializerFactory
{
    IInitializer<T> CreateFor<T>() where T : class, IComplexContent, new();
}

class InitializerFactory : IInitializerFactory
{
    public IInitializer<T> CreateFor<T>() where T : class, IComplexContent, new()
    {
        return MagicallyGetInitializer<T>();
    }

    //behind the curtain, whirring noises are heard as 't' is resolved...
    private static IInitializer<T> MagicallyGetInitializer<T>() 
        where T : class, IComplexContent, new()
    {
        IInitializer<T> i = null;
        return i;
    }
}

初始化

interface IInitializer<out T> where T : IComplexContent
{
    T Initialize(IEnumerable<string> values);
}

abstract class Initializer<T> : IInitializer<T> where T : IComplexContent
{
    public abstract T Initialize(IEnumerable<string> values);
}

class InitializerOfBlue : Initializer<Blue>
{
    private readonly Blue _content;

    public InitializerOfBlue(Blue content) {_content = content;}

    public override Blue Initialize(IEnumerable<string> values)
    {
        _content.BlueSpecificProperty = values.ElementAt(0);
        //... populate other blue-specific properties like this
        return _content;
    }
}

class InitializerOfShadeOfBlue : InitializerOfBlue
{
    public InitializerOfShadeOfBlue(ShadeOfBlue content) : base(content){}
}

内容模型

interface IComplexContent
{
    string OneBasicProperty { get; set; }
    // other properties are specific to implementation
    string UniqueOperation();
}

abstract class BaseComplexContent : IComplexContent
{
    public string OneBasicProperty { get; set; }
    public abstract string UniqueOperation();
}

class Blue : BaseComplexContent
{
    // initializer sets this
    public string PropertyForAllKindsOfBlue { get; set; }

    // initializer doesn't interact with this
    public override string UniqueOperation() {return "I'm plain.";}
}

class ShadeOfBlue : Blue
{
    // initializer doesn't interact with this
    public override string UniqueOperation() {return "I'm fabulous!";}
}

2 个答案:

答案 0 :(得分:6)

你完成了指定类选择

    kernel.Bind(scanner =>
                scanner.FromThisAssembly().SelectAllClasses()
                    .WhichAreNotGeneric()
                    .InheritedFrom(typeof (IInitializer<>))

这已经足够了。您需要做的是添加自定义绑定生成器。这会为IInitializer<Blue>选择InitializerForBlue,为IInitializer<ShadeOfBlue>选择InitializerForShadeOfBlue

https://github.com/ninject/ninject.extensions.conventions/wiki/Projecting-Services-to-Bind

答案 1 :(得分:0)

BEGIN SOLUTION CANDIDATE - 自定义绑定生成器:

自定义绑定生成器

感谢您的建议,@ RemoGloor和@RubenBartelink。我很难过 - 问题是我最终将IInitializer<Blue>绑定到InitializerOfShadeOfBlue。我需要能够以某种方式将Blue中的泛型类型参数更改为ShadeOfBlue绑定候选中的IInitializer<Blue>,因为IInitializer<ShadeOfBlue>是将从工厂方法请求的/// <summary>Creates bindings on open generic types where bound implementations' /// names end with the name of the generic type argument</summary> public class FirstTypeParameterNameMatchesEndOfBoundClassNameGenerator : IBindingGenerator { public IEnumerable<IBindingWhenInNamedWithOrOnSyntax<object>> CreateBindings(Type type, IBindingRoot bindingRoot) { if (type == null) throw new ArgumentNullException("type"); if (bindingRoot == null) throw new ArgumentNullException("bindingRoot"); // only consider concrete, non-abstract classes if (type.IsInterface || type.IsAbstract) yield break; var bindingType = GetBindingType(type); if (bindingType != null) yield return bindingRoot.Bind(bindingType).To(type); // ARGH! bindingType == IInitializer`1[[Blue]] but I want // IInitializer`1[[ShadeOfBlue]] for type == ShadeOfBlue } private static Type GetBindingType(Type type) { Type goodMatch = null; foreach (var candidate in type.GetInterfaces()) { // skip non-generic interfaces if (!candidate.IsGenericType) continue; // assumption: using argument in first position var firstArg = candidate.GetGenericArguments().First(); if (!type.Name.EndsWith(firstArg.Name)) continue; // IInitializer<XXX> matches InitializerOfXXX goodMatch = candidate; break; } if (goodMatch == null) { // if no match on interfaces, walk through the ancestor types foreach (var candidate in type.GetAllAncestors()) { goodMatch = GetBindingType(candidate); if (goodMatch != null) break; } } return goodMatch; } 运行时。

有没有办法修改绑定候选的泛型类型参数列表?或者我咆哮错误的实施?我的OP或此答案的任何编辑建议都表示赞赏。

public static class TypeExtensions
{
    // returns all ancestor types starting with the parent
    public static IEnumerable<Type> GetAllAncestors(this Type type)
    {
        for (var current = type.BaseType; current != null; current = current.BaseType)
            yield return current;
    }
}

输入扩展程序助手

{{1}}

END SOLUTION CANDIDATE - 自定义绑定生成器