【问题标题】:Resolving generic Decorators with Simple Injector使用 Simple Injector 解析通用装饰器
【发布时间】:2016-06-07 08:14:00
【问题描述】:

我正在尝试构建一个结构,其中我有一个基本的 IValidator 接口,该接口将基于一些元数据为我们的系统生成。我们希望为未来的开发人员提供以下灵活性:1)如果需要,在不干扰任何手写代码的情况下重新生成 IValidator 的具体实现;2)向 IValidator 添加装饰器,以便能够在不干扰自动执行的情况下扩展功能。生成的代码。

我想有一些方法在运行时使用 Simple Injector 的 RegisterDecorator 方法解析通用装饰器,这样我们的开发团队就不需要在每次添加装饰器时都去更新组合根。

这里有一些示例类/接口

public interface IValidator<T> where T : class
{
    void Validate(T entity);
}
public class ClientValidator : IValidator<Client>
{
    public void Validate(Client entity)
    {
        //Auto-generated
    }
}
public class UserValidator : IValidator<User>
{
    public void Validate(User entity)
    {
        //Auto-generated
    }
}
public class ClientValidatorDecorator : IValidator<Client> 
{
    private readonly IValidator<Client> clientValidator;

    public ClientValidatorDecorator(IValidator<Client> clientValidator)
    {
        this.clientValidator = clientValidator;
    }
    public void Validate(Client entity)
    {
        //New rules
        this.clientValidator.Validate(entity);
    }
}
public class UserValidatorDecorator : IValidator<User>
{
    private readonly IValidator<User> userValidator;

    public UserValidatorDecorator(IValidator<User> userValidator)
    {
        this.userValidator = userValidator;
    }
    public void Validate(User entity)
    {
        //New rules
        this.userValidator.Validate(entity);
    }
}
public class ValidationContext
{
    private readonly IValidator<Client> client;
    private readonly IValidator<User> user;

    public ValidationContext(IValidator<Client> client, IValidator<User> user)
    {
        this.client = client;
        this.user = user;
    }
}

我们正在尝试这样做:

public void RegisterServices(Container container)
{
    container.Register(typeof(IValidator<>), AssemblyManifest.GetAssemblies());
    container.RegisterDecorator(typeof(IValidator<>), GetType, Lifestyle.Transient, UseType);
}
private static Type GetType(DecoratorPredicateContext ctx)
{
    //Return appropriate Decorator
}
private static bool UseType(DecoratorPredicateContext ctx)
{
    //Predicate
}

不幸的是,除非我解决具体类型 RegisterDecorator 会引发错误,否则似乎解决了另一个泛型。我不确定如何进行。有没有办法做这样的事情?有没有更好的方法在没有装饰器的情况下获得预期的功能?我们正在考虑部分类,但这有其自身的一系列问题。

任何帮助将不胜感激!

【问题讨论】:

    标签: c# generics dependency-injection decorator simple-injector


    【解决方案1】:

    您可以使用 Composite Validator 来根据需要添加 IValidator&lt;&gt; 实现,而不是插入装饰器。此解决方案将允许代码包含同一类型的多个 IValidator&lt;&gt;

    在内部,您的代码仍将能够依赖于单个 IValidator&lt;T&gt;,该 CompositeValidator 将根据运行时在容器中注册的内容调用零个或多个验证器。

    复合验证器:

    public class CompositeValidator<T> : IValidator<T>
    {
        public readonly IEnumerable<IValidator<T>> validators;
    
        public CompositeValidator(IEnumerable<IValidator<T>> validators)
        {
            this.validators = validators;
        }
    
        public void Validate(T item)
        {
            foreach(var validator in this.validators)
            {
                validator.Validate(item);
            }
        }
    }
    

    容器是这样配置的:

    var assemblies = new[] { typeof(IValidator<>).Assembly };
    var container = new Container();
    container.RegisterCollection(typeof(IValidator<>), assemblies);
    container.Register(typeof(IValidator<>), typeof(CompositeValidator<>));
    

    assemblies 变量包含您要搜索验证器的所有程序集。

    当您使用container.GetInstance&lt;IValidator&lt;User&gt;&gt;() 或通过构造函数注入解析IValidator&lt;User&gt; 时,您会返回CompositeValidator&lt;User&gt;,它在内部引用了所有IValidator&lt;User&gt;

    【讨论】:

    • 这是一个很棒的分辨率!我将由团队管理它!谢谢!
    • 这种方式如何保证订单?
    • 这种风格唯一的坏处是它不再链接调用。如果所有 Validates 都相等,那很好,但是如果您有一个包含 ICommandHandler 的 CommandHandlerValidator,则这种样式将不起作用。
    • 我希望这个 CompositeValidator 实际上是一个装饰器,以便它在实际命令之前运行。因此,我只需要稍微改变一下,但这种风格最终确实对我有用!
    【解决方案2】:

    使用批量注册获取某个类型的装饰器的方法是调用接受TypesToRegisterOptions 对象的GetTypesToRegister 方法重载。这样您也可以指示 SI 返回装饰器。

    container.Register(typeof(IValidator<>), assemblies);
    
    var t1 = container.GetTypesToRegister(typeof(IValidator<>), assemblies);
    var t2 = container.GetTypesToRegister(typeof(IValidator<>), assemblies,
        new TypesToRegisterOptions { IncludeDecorators = true });
    
    foreach (Type t in t2.Except(t1)) {
        container.RegisterDecorator(typeof(IValidator<>), t);
    }
    

    请注意,我不建议使用此代码。 @qujck 的回答解决了您在代码中遇到的设计问题,因此他的解决方案将您带到了一个更好的地方。

    【讨论】:

    • 感谢您的回复!不管这是否是最好的解决方案,我都希望尽可能多地了解我正在使用的工具,所以这仍然是有价值的信息。
    • 这很好用。但是,像上面一样,您如何强制执行某个命令?谢谢!
    • @DanielLorenz 通过对它们进行排序。试试Enumerable.OrderBy
    • @Steven 我的排序可能需要某种属性,我可以在类型上查找,我假设?显然不能直呼其名。 :)
    • 啊,现在我明白你为什么更喜欢上面的风格了。不过,它们将不再被 SI 视为仅限装饰器的类型,因此在某些情况下您可能会遇到麻烦?
    猜你喜欢
    • 2019-05-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多