使用Ninject上下文创建依赖于上下文的单例和工厂

时间:2013-11-21 16:57:09

标签: c# dependency-injection ninject

背景

我有一个实现IEnvironment的'root'实例。在我的应用程序中,可能有多个([1..N])IE环境。每个环境都包含一堆对象,其中一些包含ninject工厂,另一些是单例(Bind< ...>()。To< ...> .InSingletonScope())。

我使用单个IEnvironment实现了一个简单使用默认绑定的原型,所有依赖对象都传入了它们的单个实例,工厂是使用Ninject ToFactory()扩展创建的。这有效。

我几乎完全使用ctor-injection,我宁愿保持这种方式。

目标

如何为每个环境创建不同的上下文?也就是说,IEnvironment中的每个实例应该得到相同的ISingleton,并且这些对象中的所有工厂都应该将这个单例传递给它的创建对象,但每个IE环境都应该有一个不同的ISingleton实例。

问题

我已阅读有关Ninject的Contextual BindingContext Preservation的文章。但我不明白如何将其映射到我的问题。据我所知,我不能使用命名绑定或属性,因为我可能有N个IE环境,并且该数字可能依赖于从外部传递的一些信息。

那么 - 我如何设置Ninject或我的应用程序以使其工作?我正在寻找一个答案来解释如何解决这个问题的概念方法。

1 个答案:

答案 0 :(得分:3)

如果您符合以下条件,则可以使用.InNamedScope(...)

  • 环境不会创建另一个环境
  • ISingleton不在环境之外使用(即,只注入环境或注入环境的依赖关系,并为每个环境实例化)。

备注:如果要解决环境中工厂使用ISingleton的新对象,则需要使用上下文保留扩展。

this.Bind<IEnvironment>().To<Environment>().DefinesNamedScope("SomeName");
this.Bind<ISingleton>().To<Singleton>.InNamedScope("SomeName")

- &GT;确保在环境请求范围中仅创建一个Singleton实例。

当然,如果能够更好地满足您的需求,您也可以创建自己的InScope(...)

另一个替代是使用继承的构造函数参数(如果需要在以后创建实例时保留上下文,请再次使用上下文保存):

IEnvironment CreateEnvironment()
{
  var singleton = IResolutionRoot.Get<ISingleton>();
  return IResolutionRoot.Get<IEnvironment>(new ConstructorArgument("singleton", singleton, true);
}

提示:构造函数参数采用参数shouldInherit,在这种情况下应为true。 我还建议使用与ConstructorArgument不同的参数。使用ConstructorArgument时,参数名称需要匹配(即当某个类想要注入ISingleton时,它需要有一个名为“singleton”的ctor参数)。 这将是另一种选择:

public class TypedConstructorArgument:ITypedParameter,IConstructorArgument     {         public TypedConstructorArgument(Type argumentType,object argumentValue)             :this(argumentType,argumentValue,false)         {         }

    public TypedConstructorArgument(Type argumentType, object argumentValue, bool shouldInherit)
    {
        Ensure.ArgumentAssignableFrom(argumentType, argumentValue, "argumentValue");

        this.ArgumentType = argumentType;
        this.ArgumentValue = argumentValue;
        this.ShouldInherit = shouldInherit;

        this.Name = argumentType.FullName;
    }

    public virtual string Name { get; private set; }

    public virtual bool ShouldInherit { get; private set; }

    public virtual Type ArgumentType { get; private set; }

    public virtual object ArgumentValue { get; private set; }

    public object GetValue(IContext context, ITarget target)
    {
        return this.ArgumentValue;
    }

    public bool AppliesToTarget(IContext context, ITarget target)
    {
        return this.MatchesType(target.Type);
    }

    public override bool Equals(object obj)
    {
        var other = obj as IParameter;
        return other != null && this.Equals(other);
    }

    public override int GetHashCode()
    {
        return this.GetType().GetHashCode() ^ this.Name.GetHashCode();
    }

    public bool Equals(IParameter other)
    {
        return other.GetType() == this.GetType() && other.Name.Equals(this.Name);
    }

    public bool MatchesType(Type type)
    {
        return this.ArgumentType == type;
    }
}