2015-02-24 16 views
3

驗證規則合同:如何使用 「複合設計模式」 與Ninject

public interface IValidationRule 
{ 
    bool IsValid(); 
} 

混凝土驗證規則:

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

複合:

public class ValidationRuleComposite : IValidationRule 
{ 
    private readonly IEnumerable<IValidationRule> _validationRules; 

    public ValidationRuleComposite(IEnumerable<IValidationRule> validationRules) 
    { 
     _validationRules = validationRules; 
    } 

    public bool IsValid() 
    { 
     return _validationRules.All(x => x.IsValid()); 
    } 
} 

當我詢問遏制者IValidationRule我想要得到ValidationRuleComposite。如果我向容器詢問IValidationRule的列表,我希望獲得IValidationRule的所有實現,但ValidationRuleComposite除外。

如何我Ninject實現這一目標?

+0

好了,那問題就要求一個單一IValidationRule並期待特定之一(ValidationRuleComposite類)。這有什麼意義?爲什麼不特別要求ValidationRuleComposite類(或類的接口)?其餘的問題應該是微不足道的。你需要記住的是從IValidationRule的列表中移除ValidationRuleComposite,否則你將有一個「循環引用」的情況。 – 2015-02-26 10:03:36

回答

0

隨着Soldarnal的幫助下,我來到了以下解決方案:

public static class KernelExtensions 
{ 
    public static void BindComposite<TComposite, TCompositeElement>(this StandardKernel container) where TComposite : TCompositeElement 
    { 
     container.Bind(x => x.FromAssemblyContaining(typeof(TComposite)) 
      .SelectAllClasses() 
      .InheritedFrom<TCompositeElement>() 
      .Excluding<TComposite>() 
      .BindAllInterfaces() 
      .Configure(c => c.WhenInjectedInto<TComposite>())); 

     container.Bind<TCompositeElement>().To<TComposite>() 
      .When(IsNotCompositeTarget<TComposite>); 
    } 

    private static bool IsNotCompositeTarget<TComposite>(IRequest x) 
    { 
     if (x.Target == null) 
      return true; 
     return x.Target.Member.ReflectedType != typeof(TComposite); 
    } 
} 

用法:

var container = new StandardKernel(); 
container.BindComposite<ValidationRuleComposite, IValidationRule>(); 
0

我不知道你會如何與Ninject直接做到這一點,但你可以使用Ninject創建一個類,然後創建您的驗證規則。

public class ValidationRuleFactory : IValidationRuleFactory 
{ 
    public IValidationRule CreateComposite() 
    { 
     var rules = CreateRules(); 
     return new ValidationRuleComposite(rules); 
    } 

    private IEnumerable<IValidationRule> 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<IValidationRuleFactory>().To<ValidationRuleFactory>().InSingletonScope(); 

然後你注入這個類,並用它來創建複合

public class MyClass() 
{ 
    private readonly IValidationRuleFactory _validationRuleFactory; 

    public MyClass(IValidationRuleFactory validationRuleFactory) 
    { 
     _validationRuleFactory = validationRuleFactory; 
    } 

    public bool CheckValid() 
    { 
     var composite = _validationRuleFactory.CreateComposite(); 
     return composite.IsValid(); 
    } 
} 
0

連線了有效性規則的你的具體實例,其中Ninject,像這樣。

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

現在,只要你有在其構造函數採用IValidationRule服務,您將得到ValidationRuleComposite具體類型既ValidationRule1ValidationRule2注入。

據我所知,Ninject不會當它涉及到注入相同類型的多個實例發揮好。在這種情況下,我們避免這樣做,因此解決IValidationRule總是導致複合類型。

但是,您可以使用Reflection自動查找所有類型,排除名稱中包含後綴「Composite」的任何掃描約定,然後遍歷這些類型以首先將它們綁定到self,然後創建要注入的實例數組。查看自定義掃描實現的this example,以及its usage

+0

_據我所知,Ninject在注入同一類型的多個實例時效果並不理想_ @ NightOwl888怎麼樣? Ninject沒有問題解決'公衆SomeConstructor(IEnumerable 項目){}' – 2015-02-26 10:07:43

0

在這裏,我假設你希望所有的驗證規則,而不是他們的部分清單,按照更一般的模式。 我會稍微改變作文課,這樣你可以做一個

kernel.Get<IValidationRuleComposite>() 

kernel.GetAll<IValidationRule>() 

一個簡單的例子如下。 的接口

public interface IValidationRule 
{ 
    bool IsValid(); 
} 
public interface IValidationRuleComposite : IValidationRule 
{ 
    void ValidationRuleCompose(List<IValidationRule> 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<IValidationRule> _validationRules; 
public void ValidationRuleCompose(List<IValidationRule> validationRules) 
{ 
    _validationRules = _validationRules.Union(validationRules).ToList(); 
} 
public ValidationRuleComposite() 
{ 
    _validationRules = new List<IValidationRule>(); 
} 
public bool IsValid() 
{ 
    Debug.WriteLine("Composite Valid"); 
    return _validationRules.All(x => x.IsValid()); 

} 

} 

和主

 StandardKernel kernel = new StandardKernel(); 
     kernel.Bind<IValidationRule>().To<MyClass1>(); 
     kernel.Bind<IValidationRule>().To<MyClass2>(); 
     kernel.Bind<IValidationRuleComposite>().To<ValidationRuleComposite>(); 

     IValidationRuleComposite try1 = kernel.Get<IValidationRuleComposite>(); 

     IEnumerable<IValidationRule> rules = kernel.GetAll<IValidationRule>(); 
     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()); 

EDIT

等量替代,保護您的複合構造

public interface IValidationRuleCompositeConstr : IValidationRule 
{ 

} 
public class ValidationRuleCompositeOriginal : IValidationRuleCompositeConstr 
{ 
    private readonly IEnumerable<IValidationRule> _validationRules; 

    public ValidationRuleCompositeOriginal(IEnumerable<IValidationRule> validationRules) 
    { 
     _validationRules = validationRules; 
    } 

    public bool IsValid() 
    { 
     return _validationRules.All(x => x.IsValid()); 
    } 
} 

與相應的使用:

StandardKernel kernel = new StandardKernel(); 
    kernel.Bind<IValidationRule>().To<MyClass1>(); 
    kernel.Bind<IValidationRule>().To<MyClass2>(); 
    kernel.Bind<IValidationRuleCompositeConstr>().To<ValidationRuleCompositeOriginal>(); 

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

首先要建立綁定了IEnumerable <IValidationRule>將被注入綜合。您可以直接綁定他們的個人:

// Bind all the individual rules for injection into the composite 
kernel.Bind<IValidationRule>().To<MyClass>().WhenInjectedInto<ValidationRuleComposite>(); 
kernel.Bind<IValidationRule>().To<RuleTwo>().WhenInjectedInto<ValidationRuleComposite>(); 

或者你也可以建立了IEnumerable很容易與convention binding extensions,讓你不必添加一個單獨裝訂每一個人的具體規則。只是一定要添加Exlcuding子句複合類,像這樣:

using Ninject.Extensions.Conventions; 

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

在我的例子中,複合材料和混凝土的其餘均在同一裝配,但很明顯,你可以改變,如果他們」你的慣例結合在其他地方。

最後,我們需要設置綁定,以便在任何其他位置請求IValidationRule,Ninject提供複合。似乎沒有被現有的這種優雅的方法,所以我寫了我自己當條款,以避免循環注入:

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

對不起,我看不到問題的第二部分的答案「如果我問容器的IValidationRule列表我想獲得除了ValidationRuleComposite之外的所有IValidationRule的實現「在ValidationRuleComposite的構造函數之外(這是問題的第一部分:」當我詢問IValidationRule的包含器時,我想獲得ValidationRuleComposite「)。非常感謝您的幫助! – 2015-03-08 16:16:38

+0

您能否告訴我在您的示例中獲取ValidationRuleComposite的代碼?我得到一個「錯誤激活IValidationRule」,如果我做「kernel.Get ()」 而「kernel.Get ()」明顯工作 ,但它會創建一個依賴項(使用具體類而不是其界面)。 簡單的「kernel.Bind ()。到();」似乎有所作爲: 在此之後,我可以獲得「kernel.Get ()」。 感謝您澄清 – 2015-03-08 17:18:04

+0

When表達式存在問題。如果目標爲空,則需要返回true。看到我的答案。 – Rookian 2015-03-08 20:01:01