4

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

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

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

  • InitializerForShadeOfBlue继承自InitializerForBlue
  • InitializerForBlue从抽象继承Initializer<Blue>
  • abstractInitializer<T>直接实现IInitializer<T>

我希望我可以将 a.EndsWith(typeof(T).Name)用于我可以使用的给定约定,因为在ShadeOfxxx静脉IInitializer<T>中实际上有数百个初始化程序。如果我必须映射所有这些,我最好找到一种在运行时通过反射解决的方法。

鉴于以下情况:

更新:使用自定义绑定生成器进行绑定(请参阅下面的答案以了解实现)

    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!";}
}
4

2 回答 2

6

您过度指定班级选择

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

这已经足够了。您需要做的是添加一个自定义绑定生成器。选择IInitializer<Blue>forInitializerForBlueIInitializer<ShadeOfBlue>forInitializerForShadeOfBlue

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

于 2013-03-06T23:51:52.643 回答
0

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

自定义绑定生成器

感谢@RemoGloor 和@RubenBartelink 的建议。不过我很难过 - 问题是我最终将 to 绑定IInitializer<Blue>InitializerOfShadeOfBlue. 我需要能够以某种方式将绑定候选中的泛型类型参数从更改为,因为Blue这是运行时从工厂方法请求的内容。ShadeOfBlueIInitializer<Blue>IInitializer<ShadeOfBlue>

有没有办法修改绑定候选的泛型类型参数列表?还是我在大声疾呼错误的实施?感谢对我的 OP 或此答案的任何编辑建议。

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

类型扩展助手

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

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

于 2013-03-08T02:14:19.517 回答