如何在Ninject中使用“复合设计模式”

validation规则合同:

public interface IValidationRule { bool IsValid(); } 

具体validation规则:

 public class MyClass : IValidationRule { public bool IsValid() { return true; } } 

综合:

 public class ValidationRuleComposite : IValidationRule { private readonly IEnumerable _validationRules; public ValidationRuleComposite(IEnumerable validationRules) { _validationRules = validationRules; } public bool IsValid() { return _validationRules.All(x => x.IsValid()); } } 

当我向IValidationRule询问IValidationRule我想得到ValidationRuleComposite 。 如果我向容器询问IValidationRule列表,我想获得除ValidationRuleComposite之外的所有IValidationRule实现。

如何使用Ninject实现这一目标?

首先,您要设置将注入复合的IEnumerable 的绑定。 你可以单独绑定它们:

 // Bind all the individual rules for injection into the composite kernel.Bind().To().WhenInjectedInto(); kernel.Bind().To().WhenInjectedInto(); 

或者您也可以使用约定绑定扩展相当容易地设置IEnumerable,这样您就不必为每个单独的具体规则添加单独的绑定。 只需确保为复合类添加Exlcuding子句,如下所示:

 using Ninject.Extensions.Conventions; // Bind all the non-composite IValidationRules for injection into ValidationRuleComposite kernel.Bind(x => x.FromAssemblyContaining(typeof(ValidationRuleComposite)) .SelectAllClasses() .InheritedFrom() .Excluding() .BindAllInterfaces() .Configure(c => c.WhenInjectedInto())); 

在我的例子中,复合材料和其余的混凝土在同一个组件中,但显然你可以改变你的约定绑定,如果它们在其他地方。

最后,我们需要设置绑定,以便在其他任何地方请求IValidationRule,Ninject提供复合。 似乎没有一个优雅的方法存在,所以我写了自己的When子句,以避免周期性注入:

 // Now bind the composite to the interface for everywhere except itself kernel.Bind().To() .When(x => x.Target == null || x.Target.Member.ReflectedType != typeof(ValidationRuleComposite)); 

在这里,我假设您需要所有validation规则而不是它们的部分列表,根据更通用的模式。 我会略微更改Composition类,以便您可以执行

 kernel.Get() 

和a

 kernel.GetAll() 

一个简单的例子如下。 接口

 public interface IValidationRule { bool IsValid(); } public interface IValidationRuleComposite : IValidationRule { void ValidationRuleCompose(List validationRules); } 

和规则

 public class MyClass1 : IValidationRule { public bool IsValid() { Debug.WriteLine("Valid 1"); return true; } } public class MyClass2 : IValidationRule { public bool IsValid() { Debug.WriteLine("Valid 2"); return false; } } 

复合规则

 public class ValidationRuleComposite : IValidationRuleComposite { private List _validationRules; public void ValidationRuleCompose(List validationRules) { _validationRules = _validationRules.Union(validationRules).ToList(); } public ValidationRuleComposite() { _validationRules = new List(); } public bool IsValid() { Debug.WriteLine("Composite Valid"); return _validationRules.All(x => x.IsValid()); } } 

和一个主要的

  StandardKernel kernel = new StandardKernel(); kernel.Bind().To(); kernel.Bind().To(); kernel.Bind().To(); IValidationRuleComposite try1 = kernel.Get(); IEnumerable rules = kernel.GetAll(); foreach(IValidationRule trycomp in rules) { Debug.WriteLine("trycomp: " + trycomp.GetType().ToString()); trycomp.IsValid(); }; try1.ValidationRuleCompose(rules.ToList()); Console.WriteLine("{0}",try1.IsValid()); Debug.WriteLine("try1: " + try1.GetType().ToString()); 

编辑

等效替代,保留您的复合构造函数

 public interface IValidationRuleCompositeConstr : IValidationRule { } public class ValidationRuleCompositeOriginal : IValidationRuleCompositeConstr { private readonly IEnumerable _validationRules; public ValidationRuleCompositeOriginal(IEnumerable validationRules) { _validationRules = validationRules; } public bool IsValid() { return _validationRules.All(x => x.IsValid()); } } 

与相应的用法:

  StandardKernel kernel = new StandardKernel(); kernel.Bind().To(); kernel.Bind().To(); kernel.Bind().To(); IEnumerable rules = kernel.GetAll(); Ninject.Parameters.ConstructorArgument therules = new Ninject.Parameters.ConstructorArgument("therules", rules); IValidationRuleCompositeConstr try2 = kernel.Get(therules); Debug.WriteLine("Second Class"); Debug.WriteLine (string.Format("{0}",try2.IsValid())); 

我不知道如何使用Ninject直接执行此操作,但您可以使用Ninject创建一个类,然后创建validation规则。

 public class ValidationRuleFactory : IValidationRuleFactory { public IValidationRule CreateComposite() { var rules = CreateRules(); return new ValidationRuleComposite(rules); } private IEnumerable CreateRules() { //return all other rules here. //I would hard code them and add new ones here as they are created. //If you don't want to do that you could use reflection. } } 

因为这个类没有任何状态,你可以用单例范围创建它。

 kernel.Bind().To().InSingletonScope(); 

然后注入此类并使用它来创建复合

 public class MyClass() { private readonly IValidationRuleFactory _validationRuleFactory; public MyClass(IValidationRuleFactory validationRuleFactory) { _validationRuleFactory = validationRuleFactory; } public bool CheckValid() { var composite = _validationRuleFactory.CreateComposite(); return composite.IsValid(); } } 

你在Ninject中连接你的ValidationRule的具体实例,就像这样。

 this.Kernel.Bind().ToSelf(); this.Kernel.Bind().ToSelf(); this.Kernel.Bind().To() .WithConstructorArgument("validationRules", new IValidationRule[] { this.Kernel.Get(), this.Kernel.Get() }); 

现在,只要您的服务在其构造函数中使用IValidationRule ,您将获得ValidationRuleComposite具体类型,同时注入ValidationRule1ValidationRule2

据我所知,Ninject在注入多个相同类型的实例时效果不佳。 在这种情况下,我们避免这样做,因此解析IValidationRule始终会导致复合类型。

但是,您可以使用Reflection自动查找所有类型构建自己的扫描约定,排除名称中带有后缀“Composite”的任何类型,然后遍历类型以首先将它们绑定到self,然后创建实例数组注入。 看看这个自定义扫描实现的示例 及其用法 。

在Soldarnal的帮助下,我得出了以下解决方案:

 public static class KernelExtensions { public static void BindComposite(this StandardKernel container) where TComposite : TCompositeElement { container.Bind(x => x.FromAssemblyContaining(typeof(TComposite)) .SelectAllClasses() .InheritedFrom() .Excluding() .BindAllInterfaces() .Configure(c => c.WhenInjectedInto())); container.Bind().To() .When(IsNotCompositeTarget); } private static bool IsNotCompositeTarget(IRequest x) { if (x.Target == null) return true; return x.Target.Member.ReflectedType != typeof(TComposite); } } 

用法:

 var container = new StandardKernel(); container.BindComposite();