使用简单注入器解析通用装饰器

Resolving generic Decorators with Simple Injector

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

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

这里有一些例子classes/interfaces

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 会抛出错误,否则解析另一个泛型似乎无法解决。我不确定如何进行。有没有办法做这样的事情?有没有更好的方法在没有装饰器的情况下获得预期的功能?我们考虑的是部分 类,但这有其自身的一系列问题。

任何帮助将不胜感激!

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

在内部,您的代码仍然能够依赖于单个 IValidator<T>,它将解析为 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<IValidator<User>>() 或通过构造函数注入解析 IValidator<User> 时,您会返回 CompositeValidator<User>,它在内部引用任何和所有 IValidator<User>

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

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 的回答解决了您的代码的设计问题,因此他的解决方案将您带到了一个更好的地方。