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

Noo*_*Awa 6 c# ninject inversion-of-control

我正在尝试Ninject,我正在修改我用Structure Map编写的代码,看看它有多容易.在这个基本代码中,我有一个通过Structure Map注册表具有不同配置的对象图,并且在运行时通过数据库中的值选择要使用的对象(在这种情况下,为了拉回注入了一些对象的wcf服务主体) .例如(使用结构图代码):

注册表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>();
    }
Run Code Online (Sandbox Code Playgroud)

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>();
    }
Run Code Online (Sandbox Code Playgroud)

这一切都在结构图中按预期工作(取决于旧的或新的语法).

但是,当我一直在使用Ninject时我遇到了一个问题,期望未命名的实例是默认的(不是Ninject如何工作,我得到了).这导致了一些研究,这些研究都表明使用命名实例是一个非常糟糕的想法.我知道有更好的方法可以使用自动注册或属性来设置名称或请求某种类型,但在我所描述的系统中,需要在运行时有一种方法来确定要求的配置在树的顶部(让IoC框架根据已注册的类型或规则计算出其余部分).

所以...我只是在这里使用IoC概念错误,希望通过名称来询问我的顶级对象,或者通常有更好的方法来做我正在尝试做的事情?我应该使用像MEF这样的东西来处理这些插件吗?

我强调我不是像愚蠢的工厂那样使用它,并且在每个级别的代码中询问容器中x类型的实例,它只是启动操作.

在此先感谢您的时间和帮助:)

Aar*_*nHS 4

通过名称设置 ninject 绑定并没有什么问题,如果这是实现您需要的 IMO 的唯一方法。

所以基本语法是:

Bind<IBusinessContext>().To<ConcreteBusinessContext>().Named("XYZ");
Run Code Online (Sandbox Code Playgroud)

或者,如果您需要特定的调用类来获取不同的绑定,那么您可以尝试:

Bind<IIBusinessContext>().To<SomeOtherConcreteBusinessContext>().WhenInjectedInto<TypeOfCallingClass>();
Run Code Online (Sandbox Code Playgroud)

但是,如果调用类(我指的是其构造函数中具有 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>();
}
Run Code Online (Sandbox Code Playgroud)

你的调用类将类似于:

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
    }
}
Run Code Online (Sandbox Code Playgroud)

在该示例中,不需要命名实例,因为您有一个加载特定具体类型的方法,但是如果您想做这样的事情,您仍然可以使用命名实例:

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"

    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:我忘了提及,如果您的配置值不必来自调用代码,那么这会让事情变得更容易。您的代码可以类似于:

// 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
    }
}
Run Code Online (Sandbox Code Playgroud)

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