Asp.net核心AutoFac注册泛型使用工厂

时间:2017-07-18 16:06:06

标签: c# asp.net ninject autofac

我正在使用Asp.net Core和AutoFac并遵循接受的答案:

Validation: How to inject A Model State wrapper with Ninject?

这使用ninject。我不明白如何在autoFac中执行相当于此ninject部分的操作,特别是kernel.Get

Func<Type, IValidator> validatorFactory = type =>
{
    var valType = typeof(Validator<>).MakeGenericType(type);
    return (IValidator)kernel.Get(valType);
};

kernel.Bind<IValidationProvider>()
    .ToConstant(new ValidationProvider(validatorFactory));

Startup.cs

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    var containerBuilder = new ContainerBuilder();

    IValidator ValidatorFactory(Type type)
    {
        var valType = typeof(Validator<>).MakeGenericType(type);

        //This line is the problem
        // return (IValidator)container.Resolve(valType);
    }


    containerBuilder.Register(x => new ValidationProvider(ValidatorFactory)).As<IValidationProvider>().SingleInstance();

    containerBuilder.RegisterType<UploadValidator>().As<Validator<AudioModel>>();

    containerBuilder.Populate(services);

    var container = containerBuilder.Build();

    return container.Resolve<IServiceProvider>();
}

问题是容器只有在使用.Build()后才可用,所以我不知道如何做到这一点。我是否需要在致电.Build()后注册此服务,然后再次致电.Build().Resolve()在此处使用错误的内容。

验证类:

internal sealed class ValidationProvider : IValidationProvider
{
    private readonly Func<Type, IValidator> _validatorFactory;

    public ValidationProvider(Func<Type, IValidator> validatorFactory)
    {
        _validatorFactory = validatorFactory;
    }

    public void Validate(object entity)
    {
        var results = _validatorFactory(entity.GetType()).Validate(entity).ToArray();

        if (results.Length > 0)
            throw new ValidationException(results);
    }

    public void ValidateAll(IEnumerable entities)
    {
        var results = (
            from entity in entities.Cast<object>()
            let validator = _validatorFactory(entity.GetType())
            from result in validator.Validate(entity)
            select result).ToArray();

        if (results.Length > 0)
            throw new ValidationException(results);
    }
}

public abstract class Validator<T> : IValidator
{
    IEnumerable<ValidationResult> IValidator.Validate(object entity)
    {
        if (entity == null)
            throw new ArgumentNullException(nameof(entity));

        return Validate((T)entity);
    }

    protected abstract IEnumerable<ValidationResult> Validate(T entity);
}

public class UploadValidator : Validator<AudioModel>
{
    protected override IEnumerable<ValidationResult> Validate(AudioModel model)
    {
        if (string.IsNullOrWhiteSpace(model.Name))
        {
            yield return new ValidationResult("Name", "Name is required");
        }
    }
}

1 个答案:

答案 0 :(得分:1)

Autofac具有强大的功能,使我们能够register factories基于参数创建实例。在您的示例中,我们可以使用Autofac注册Func<Type, IValidator>,并将其自动注入我们的ValidationProvider

var builder = new ContainerBuilder();
builder
    //register our factory function
    .Register<Func<Type, IValidator>>(
        x =>
        {
            //get a reference to the scoped container
            //e.g. if this is a web app, each HTTP request will
            //spawn a child container used for the lifetime of that request
            var context = x.Resolve<IComponentContext>();
            return type => 
            {
                //create the validator from our scoped container
                var valType = typeof(Validator<>).MakeGenericType(type);
                return (IValidator) context.Resolve(valType);
            }
        }
)};

public class ValidationProvider
{
    readonly Func<Type, IValidator> _factory;        
    //Autofac will see this class requires our previously registered
    //function and inject this for us
    public ValidationProvider(Func<Type, IValidator> factory)
    {
        _factory = factory;
    }
}

作为替代方案,您是否可以使用泛型参数约束IValidator?也许重构代码是不可行的,但如果是这样的话,可能更好的做法是为我们的服务提供他们所需的确切依赖,而不是可能隐藏其意图的工厂。

public interface IValidator<T>
{
    void Validate(T instance);
}

public class SomeClassRequiringAudioModelValidator
{
    readonly IValidator<AudioModel> _validator;
    public SomeClassRequiringAudioModelValidator(IValidator<AudioModel> validator)
    {
        _validator = validator;
    }
}
相关问题