如何根据工厂参数获取ninject绑定 - >名称空间路径

时间:2013-01-16 19:24:29

标签: dependency-injection inversion-of-control ninject ninject-extensions

我正在使用Ninject 3.0.1.10和来自NuGet的ninject.extensions.factory 3.0.1.0 - 在“真实”场景中我也将使用ninject.extensions.conventions(而不是手动绑定IFoo),但是我想保留这一点,试图简化问题。

我有一个IFoo接口及其多个实现,每个接口都在子命名空间和子文件夹下,名为Gen1和Gen2。我有一个IFooFactory接口,其意图是它根据指定的参数(字符串,枚举,等等)返回一个IFoo。

我在这个例子中使用枚举只是为了尝试使其更清晰 - 我最初创建了一个字符串版本,但感觉就像传递一个像字符串这样更随意的参数的反对意见只会混淆问题。

public enum ImplementationGeneration
{
    Gen1,
    Gen2,
    Gen3,
}

public interface IFoo
{
    void DoStuff();
}

public interface IFooFactory
{
    IFoo CreateFoo(ImplementationGeneration implementationGeneration);
}


namespace SomeRootNamespace.Gen1
{
    public class Foo : IFoo
    {
        public void DoStuff()
        {
            Console.WriteLine("Doing Type1 stuff");
        }
    }
}

namespace SomeRootNamespace.Gen2
{
    public class Foo : IFoo
    {
        public void DoStuff()
        {
            Console.WriteLine("Doing Type2 stuff");
        }
    }
}

现在,我明白让消费者“选择”这样的实现是一种理想情况下不存在的耦合形式,但恕我直言,它与Ninject已经支持的命名绑定具有相同的耦合级别。我想避免将属性添加到实现中,并且在工厂界面中使用GetGen1 / GetGen2 / etc方法非常适合这种情况,因为我最终通过某个开关违反OCP以将输入映射到要调用的方法(或手动使用反射)

我现在拥有的完整/正常工作代码,如果可能的话,我宁愿避免使用:https://gist.github.com/4549677

它使用了两种方法:

  1. 手动工厂实施,违反OCP并通过传递的枚举
  2. 使用带有IInstanceProvider实例的工厂扩展(子类StandardInstanceProvider来覆盖GetInstance)。
  3. 第二种方法似乎可能“接近”'正确的方式'来实现这一点,但是1)它保留了对内核的引用以便完成它的工作,这可能是一个坏主意, 2)因为我在调用期间搜索所有IFoo绑定时找不到IFoo绑定中的具体类型,所以它当前执行GetAll,因此它实例化的N-1个实例比它在这个场景中需要的实例多。 / p>

1 个答案:

答案 0 :(得分:1)

好吧,我发现的东西至少比我上面的东西好。

它毕竟使用命名绑定,ninject.extensions.conventions用于根据命名空间的最后部分命名所有绑定。这最终将名称附加到许多不需要它的绑定(只有一个实现可用于给定接口的绑定),尽管将名称附加到这些绑定不会导致它们的使用出现任何问题(至少在我的测试中) )。

如果出于某种原因对将来遇到这种问题的人来说是一个问题,你可以在通过约定设置绑定的代码中更加具体 - 例如,如果最后的命名空间部分只添加命名绑定在一个特定的集合或匹配一个特定的模式。

kernel.Bind(x => x
    .FromThisAssembly()
    .SelectAllClasses()
    .BindAllInterfaces()
    .Configure((binding, concreteType) =>
    {
        var concreteNamespace = concreteType.Namespace ?? String.Empty;
        var lastNamespacePart = concreteNamespace.Split('.').Last();
        binding.Named(lastNamespacePart);
    })
);

然后使用the UseFirstArgumentAsNameInstanceProvider根据工厂方法的第一个参数查找绑定名称(因此您不必为GetGen1,GetGen2等提供单独的方法)。我刚刚更改了GetName覆盖以执行ToString,因为我传递的是枚举而不是实际的字符串,但是它与链接的Wiki页面相同。

public class UseFirstArgumentAsNameInstanceProvider : StandardInstanceProvider
{
    protected override string GetName(System.Reflection.MethodInfo methodInfo, object[] arguments)
    {
        return arguments[0].ToString();
    }

    protected override ConstructorArgument[] GetConstructorArguments(System.Reflection.MethodInfo methodInfo, object[] arguments)
    {
        return base.GetConstructorArguments(methodInfo, arguments).Skip(1).ToArray();
    }
}

我会暂时搁置这个问题,以防万一有不同/更好的选择,但至少这似乎是合理的,没有任何明显的OCP问题。 :)