简体   繁体   中英

Register all class that implements IValidator<SomethingValidatable> with Unity

Suppose I defined some validation logic like this:

public class Rule1 : IValidator<SomethingValidatable>
{
    public IList<Error> Validate(SomethingValidatable item) 
    {
         // apply validation logic to item
    }
}

Somewhere in code I bind IValidator<SomethingValidatable> to one or more concrete Rule :

protected override void ConfigureContainer(IUnityContainer container)
{
    container
        .RegisterType<IValidator<SomethingValidatable>, Rule1>("a")
        .RegisterType<IValidator<SomethingValidatable>, Rule2>("b");
}

and where I need this validation occurs I have somethin like:

public class HereIValidateClass 
{
    private readonly IValidator<SomethingValidatable>[] validators;

    public HereIValidateClass(IValidator<SomethingValidatable>[] validators)
    {
        this.validators = validators;
    }

    public IList<Error> DoSomething() 
    {
        foreach (var validator in validators)
        {
            var errors = validator.Validate(something);
            // append to IList<Error>
        }

        return errors;
    }
}

This do the job. But I would avoid to register explicitely one by one each rules. I really like to say: Dear Unity, register each class in a given namespace that implements IValidator<SomethingValidatable> .

How about an extension method for IUnityContainer:

public static class UnityExtensions
{
    public static void RegisterValidators(this IUnityContainer container)
    {
        var types = AllClasses.FromLoadedAssemblies().GetValidators();
        foreach (var type in types)
        {
            var interfaces = WithMappings.FromAllInterfaces(type);
            foreach (var @interface in interfaces)
            {
                container.RegisterType(@interface, type);
            }
        }
    }

    public static IEnumerable<Type> GetValidators(this IEnumerable<Type> types)
    {
        var wantedType = typeof(IValidator<>);
        return from type in types 
               from interfaceType in type.GetInterfaces() 
               where interfaceType.IsGenericType 
                    && !interfaceType.ContainsGenericParameters 
                    && wantedType.IsAssignableFrom(interfaceType.GetGenericTypeDefinition()) 
               select type;
    }
}

And if you need more info about the specific registrations you can use an attribute.

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class MyValidationAttribute : Attribute
{
    public string RegistrationName { get; set; }

    public MyValidationAttribute(string name)
    {
        RegistrationName = name;
    }
}

And then change the extension method to something like this:

    public static void RegisterValidators(this IUnityContainer container)
    {
        var types = AllClasses.FromLoadedAssemblies().GetValidators();
        foreach (var type in types)
        {
            var interfaces = WithMappings.FromAllInterfaces(type);
            foreach (var @interface in interfaces)
            {
                var myAttr = type.GetCustomAttributes<MyValidationAttribute>(false);
                foreach (var attr in myAttr)
                {
                    // Register with specific name.
                    container.RegisterType(@interface, type, attr.RegistrationName);
                }
            }
        }
    }

And change your registration:

protected override void ConfigureContainer(IUnityContainer container)
{
    container.RegisterValidators();
}

It will register each implementation for IValidator , regardless of T.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM