如何使用"复合设计模式"与Ninject

时间:2015-02-24 09:55:15

标签: c# dependency-injection ninject ioc-container composite

验证规则合同:

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实现这一目标?

5 个答案:

答案 0 :(得分:4)

首先,您要为IEnumerable&lt; IValidationRule&gt;设置绑定。将被注入复合材料中。你可以单独绑定它们:

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

或者您也可以使用convention binding extensions轻松设置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<IValidationRule>()
    .Excluding<ValidationRuleComposite>()
    .BindAllInterfaces()
    .Configure(c => c.WhenInjectedInto<ValidationRuleComposite>()));

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

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

// 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));

答案 1 :(得分:1)

这里我假设你想要所有的验证规则而不是它们的部分列表,按照更通用的模式。 我会略微更改Composition类,以便您可以执行

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());

修改

等效替代方法,保留复合构造函数

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()));

答案 2 :(得分: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();
    }
}

答案 3 :(得分:0)

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

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 exampleits usage

答案 4 :(得分: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>();