使用名称来区分使用IoC的实例

6
我正在尝试使用Ninject,并修改我用Structure Map编写的代码,以查看它有多容易。在这个基础代码中,我有一个对象图,通过Structure Map注册表具有不同的配置,并且在运行时通过数据库中的值选择要使用的对象(在这种情况下,返回一些注入对象的WCF服务体)。例如(使用Structure Map代码):
Registry 1为IBusinessContext、IRules和ILogger类型设置所有默认值。这只是将GenericContext/Logger/Rules类型与接口一起添加,没有其他特殊化。
public GenericRegistry()
    {
        // Set up some generic bindings here
        For<ILogger>().Use<Loggers.GenericLogger>();
        For<IBusinessRule>().Use<Rules.StandardRule>();
        For<IBusinessContext>().Use<Contexts.GenericBusinessContext>();
        For<ILoggerContext>().Use<Loggers.GenericLoggerContext>();
    }

Registry 2将IBusinessContext设置为使用SpecialisedContext类,并告诉ctor使用SpecializedLogger。IBusinessContext的实例名为“SpecializedContext”。

public SpecializedRegistry()
    {
        // Old style syntax as it affects the default for IBusinessContext
        // Perhaps a hint at what I'm doing?
        InstanceOf<IBusinessContext>().Is.OfConcreteType<Contexts.SpecializedBusinessContext>().Named(SpecializedInstanceName).Ctor<ILogger>().Is<Loggers.SpecialisedLogger>();
    }

在 Structure Map 中,这一切都能正常进行(取决于使用的新或旧语法)。然而,当我使用 Ninject 时,我遇到了一个问题:期望未命名实例成为默认实例(这并不是 Ninject 的工作方式,我明白这一点)。这导致我进行了一些研究,所有的研究都表明使用具名实例是非常糟糕的想法。我知道可以使用自动注册或属性来设置名称或请求某个类型以实现更好的方法,但在我描述的系统中,需要在运行时找出在树的顶部要请求什么配置(并让 IoC 框架基于已注册的类型或规则来处理其余部分)。
那么...我是否只是在错误地使用 IoC 概念,期望通过名称请求我的顶层对象?还是通常有更好的方式来实现我所尝试的事情?我应该使用像 MEF 这样的东西,将这一切都视为插件吗?
请注意,我不像一个愚蠢的工厂,每一级代码都会从容器中请求类型 x 的实例,而仅限于启动操作。感谢您的时间和帮助 :)
1个回答

4

如果按名称设置ninject绑定是实现所需功能的唯一方法,那么这种做法并没有什么不妥。在我看来,基本语法如下:

Bind<IBusinessContext>().To<ConcreteBusinessContext>().Named("XYZ");

如果您需要使用特定的调用类来获取不同的绑定,可以尝试以下方法:

Bind<IIBusinessContext>().To<SomeOtherConcreteBusinessContext>().WhenInjectedInto<TypeOfCallingClass>();

然而,如果调用的类(我所说的是具有IBusinessContext的类)提供了一个配置值来确定要加载哪个具体类型,那么您将需要使用委托:

Bind<Func<string, IBusinessContext>>().ToMethod(ctx => str => DetermineWhichConcreteTypeToLoad(ctx, str));

//messy sudo code
static DetermineWhichConcreteTypeToLoad(IContext ctx, string str)
{
    if(str == "somevalue"){
        return ctx.Kernel.Get<ConcreteType1>();
    else
        return ctx.Kernel.Get<ConcreteType2>();
}

您的调用类将类似于:

class DoStuff
{
    Func<string, IBusinessContext>> contextFunc;

    DoStuff(Func<string, IBusinessContext>> contextFunc)
    {
        this.contextFunc = contextFunc;
    }

    void SomeMethod()
    {
        var configuredValue = GetConfiguredValueSomehow();
        var context = contextFunc(configuredValue); //<-- this passes your config value back to ninject in the ToMethod() lambda
        //do something with context
    }
}

在这个例子中,不需要使用命名实例,因为你有一个加载特定具体类型的方法,但是如果你想做像这样的事情,仍然可以使用命名实例:
Bind<IBusinessContext>().To<ConcreteBusinessContext>().Named("config1");
Bind<IBusinessContext>().To<SomeOtherBusinessContext>().Named("config2");

Bind<Func<string, IBusinessContext>>().ToMethod(ctx => str => ctx.Kernel.Get<IBusinessContext>().Named(str));

class DoStuff
{
    Func<string, IBusinessContext>> contextFunc;

    DoStuff(Func<string, IBusinessContext>> contextFunc)
    {
        this.contextFunc = contextFunc;
    }

    void SomeMethod()
    {
        var configuredValue = "config1";
        var context = contextFunc(configuredValue); //<-- this will passthrough "config1" to the above ToMethod() method and ask for a IBusinessContext named "config1"

    }
}

编辑:我忘记提到了,如果您的配置值不必来自调用代码,则这将使事情变得更加容易。您的代码可以采用以下形式:

// this method can just be a global method in you app somewhere
static string GetConfigValue()
{
    //something like
    return AppSetting.Get("config");
}

Bind<IBusinessContext>().To<ConcreteBusinessContext>().When(r => GetConfigValue() == "config1");
Bind<IBusinessContext>().To<SomeOtherBusinessContext>().When(r => GetConfigValue() == "config2");

class DoStuff
{
    IBusinessContext context;

    DoStuff(BusinessContext context)
    {
        this.context = context;
    }

    void SomeMethod()
    {
        //use the context value as you normally would
    }
}

您可以发挥创意,而不是使用魔术字符串,您的config方法可以加载一个枚举类型,而您的When()方法可以测试枚举相等性,而不是字符串。这被称为在ninject中的上下文绑定。作为曾经热衷于SM的用户,我可以告诉您,这比SM拥有的任何功能都强大得多。请查看其余的When()方法,看看您能做什么。


谢谢Aaron!这为我解决了一些问题 :) 非常感激。 - NoodleAwa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接