C# 使用名称区分使用IoC的实例

C# 使用名称区分使用IoC的实例,c#,inversion-of-control,ninject,C#,Inversion Of Control,Ninject,我正在试用Ninject,我正在修改用结构图编写的代码,看看它有多简单。在这个基本代码中,我有一个对象图,这些对象通过结构映射注册表具有不同的配置,并且在运行时通过数据库中的一个值选择要使用的对象(在本例中,是通过注入一些对象来拉回wcf服务体)。例如(使用结构映射代码): 注册表1设置IBusinesContext、IRules和ILogger类型的所有默认值。这只是在接口旁边添加类型GenericContext/Logger/Rules,没有其他专门化 public GenericRegis

我正在试用Ninject,我正在修改用结构图编写的代码,看看它有多简单。在这个基本代码中,我有一个对象图,这些对象通过结构映射注册表具有不同的配置,并且在运行时通过数据库中的一个值选择要使用的对象(在本例中,是通过注入一些对象来拉回wcf服务体)。例如(使用结构映射代码):

注册表1设置IBusinesContext、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>();
    }
publicGenericRegistry()
{
//在这里设置一些通用绑定
For().Use();
For().Use();
For().Use();
For().Use();
}
注册表2将IBusinesContext设置为使用SpecializedContext类,并告诉ctor使用SpecializedLogger。IBusinesContext的实例名为“SpecializedContext”

public SpecializedRegistry()
{
//旧式语法,因为它影响IBusinesContext的默认值
//也许是暗示我在做什么?
InstanceOf().Is.OfConcreteType().Named(SpecializedInstanceName.Ctor().Is();
}
这一切都在结构映射中按预期工作(取决于旧语法或新语法)

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

所以…我只是用IoC的概念来错误地期望通过名字来要求我的顶级对象,还是有更好的方法来做我想做的事情?我应该用MEF之类的东西来代替,并将其视为插件吗

我强调,我并不是像一个愚蠢的工厂那样使用它,而是在代码的每一层从容器中请求一个类型为x的实例,它只是启动操作


提前感谢您的时间和帮助:)

按名称设置ninject绑定没有什么错,如果这是实现您所需的唯一方法的话

因此,基本语法是:

Bind<IBusinessContext>().To<ConcreteBusinessContext>().Named("XYZ");
Bind().To().Named(“XYZ”);
或者,如果需要特定的调用类来获取不同的绑定,则可以尝试:

Bind<IIBusinessContext>().To<SomeOtherConcreteBusinessContext>().WhenInjectedInto<TypeOfCallingClass>();
Bind().To().whenInjectedTo();
但是,如果调用类(我指的是在其ctor中包含IBusinesContext的类)提供了一个配置值来确定要加载的具体类型,那么您将需要使用委托:

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>();
}
Bind().ToMethod(ctx=>str=>determinatewhichconcretetypetoload(ctx,str));
//凌乱的sudo代码
静态确定要加载的具体类型(IContext ctx,string str)
{
如果(str==“somevalue”){
返回ctx.Kernel.Get();
其他的
返回ctx.Kernel.Get();
}
您的调用类将类似于:

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
    }
}
// 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
    }
}
class-DoStuff
{
Func>contextFunc;
DoStuff(Func>contextFunc)
{
this.contextFunc=contextFunc;
}
void方法()
{
var configuredValue=getConfiguredValue();
var context=contextFunc(configuredValue);//str=>ctx.Kernel.Get().Named(str));
类灰凝灰岩
{
Func>contextFunc;
DoStuff(Func>contextFunc)
{
this.contextFunc=contextFunc;
}
void方法()
{
var configuredValue=“config1”;
var context=contextFunc(configuredValue);//GetConfigValue()=“config1”);
绑定().To().When(r=>GetConfigValue()=“config2”);
类灰凝灰岩
{
语境;
DoStuff(业务上下文)
{
this.context=上下文;
}
void方法()
{
//像通常一样使用上下文值
}
}

您可以很有创意,而不是使用神奇的字符串,您的config方法可以加载enum和When()方法可以根据枚举而不是字符串来测试相等性,但您知道了。这在ninject中被称为上下文绑定,我可以告诉您,作为SM的一个曾经狂热的用户,这比SM拥有的任何功能都要强大得多。签出When()的其余部分方法,看看你能做些什么。

谢谢Aaron!这为我澄清了一些事情:)非常感谢。
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
    }
}