如何使用Simple Injector从ValidationContext获得服务?

提摩太

在我的Asp.Net MVC Core项目中,我使用SimpleInjector作为IoC。我之所以使用它,是因为有可能注册开放的泛型。

在某些视图模型中,我实现了IValidatableObject

public class MyViewmodel: IValidatableObject
{
    public string SomeProperty { get;set; }

    //...

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        //...
        IMyService service = validationContext.GetService(typeof(IMyService)) as IMyService;
    }
}

方法GetService返回,null因为IMyService已由SimpleInjector在应用程序中注册。

在我的控制器中,我使用了这样的验证:

[HttpPost]
public async Task<IActionResult> Edit(MyViewmodel model)
{
    if (ModelState.IsValid)
    {
        //...
    }

    return View(model);
}

那么,有没有办法从IServiceProviderValidationContext中的Asp.Net Core获取IMyService

史蒂文

尽管将验证逻辑放置在模型对象本身内部没有任何固有的错误,但是当该验证逻辑需要服务才能工作时,问题就开始出现。在这种情况下,您最终将应用Service Locator反模式(通过调用validationContext.GetService)。

相反,当涉及需要服务才能运行的更复杂的验证时,最好将数据和行为分开这使您可以将验证逻辑移至单独的类。此类可以应用构造函数注入,因此不必使用任何反模式。

为此,请从您自己的可以验证实例的抽象开始。例如:

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

在此抽象之上,您可以定义尽可能多的实现,例如一个(或多个)用于验证的实现MyViewmodel

public class MyViewmodelValidator : IValidator<MyViewmodel>
{
    private readonly IMyService service;
    public MyViewmodelValidator(IMyService service) => this.service = service;

    public IEnumerable<string> Validate(MyViewmodel instance)
    {
        yield return "I'm not valid.";
    }
}

这就是使一切运转所需的所有应用程序代码。当然,您应该IValidator<T>根据您的应用程序需求接口进行建模

剩下的就是确保MVC在验证视图模型时使用这些验证器。这可以通过自定义IModelValidatorProvider实现来完成

class SimpleInjectorModelValidatorProvider : IModelValidatorProvider
{
    private readonly Container container;

    public SimpleInjectorModelValidatorProvider(Container container) =>
        this.container = container;

    public void CreateValidators(ModelValidatorProviderContext ctx)
    {
        var validatorType =
            typeof(ModelValidator<>).MakeGenericType(ctx.ModelMetadata.ModelType);
        var validator = (IModelValidator)this.container.GetInstance(validatorType);
        ctx.Results.Add(new ValidatorItem { Validator = validator });
    }
}

// Adapter that translates calls from IModelValidator into the IValidator<T>
// application abstraction.
class ModelValidator<TModel> : IModelValidator
{
    private readonly IEnumerable<IValidator<TModel>> validators;

    public ModelValidator(IEnumerable<IValidator<TModel>> validators) =>
        this.validators = validators;

    public IEnumerable<ModelValidationResult> Validate(ModelValidationContext ctx) =>
        this.Validate((TModel)ctx.Model);

    private IEnumerable<ModelValidationResult> Validate(TModel model) =>
        from validator in this.validators
        from errorMessage in validator.Validate(model)
        select new ModelValidationResult(string.Empty, errorMessage);
}

剩下要做的唯一一件事就是添加SimpleInjectorModelValidatorProvider到MVC管道并进行所需的注册:

services.AddMvc(options =>
    {
        options.ModelValidatorProviders.Add(
            new SimpleInjectorModelValidatorProvider(container));
    });

// Register ModelValidator<TModel> adapter class
container.Register(typeof(ModelValidator<>), typeof(ModelValidator<>),
    Lifestyle.Singleton);

// Auto-register all validator implementations
container.Collection.Register(
    typeof(IValidator<>), typeof(MyViewmodelValidator).Assembly);

瞧!您可以使用它—一个完全松散耦合的验证结构,可以根据应用程序的需求进行定义,同时使用诸如构造器注入之类的最佳实践,并且可以对验证代码进行全面测试,而不必求助于反模式。与MVC基础架构紧密结合。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何使用Simple Injector注册AutoMapper 4.2.0

如何使用FluentScheduler正确配置Simple Injector

使用Simple Injector的Update-Database,不带服务。AddDbContext <>()

使用$ injector与直接注入服务

如何使用Simple Injector注册命名实例

如何使用 Simple Injector 向基类注入依赖项?

如何在Simple Injector中使用Ninject的命名绑定?

如何使用Simple Injector注册嵌套的泛型类型?

如何使用Injector模拟功能中的注入服务

使用Simple Injector的asp.net Core Web API服务的正确DefaultScopedLifestyle是什么?

如何将ASP.NET Core App设置与Simple Injector一起使用

如何使用Simple Injector装饰ASP.NET MVC控制器

如何使用Simple Injector有条件地注册集合?

如何在Singleton类,Simple Injector中使用瞬态记录器

如何获得Websocket会话服务?

我如何获得服务意向?

运行时注入:如何使用Guice获得最幼稚的Injector?

使用Simple Injector进行AutoFixture自动模拟

使用 Simple Injector 注册多个 DbContext

使用通用存储库实现Simple Injector

对于WPF窗口介体服务,使用Simple Injector通过键解析实例的替代方法是什么?

如何使用 xUnit 为使用 Entity Framework Core 和 Simple Injector 构建的 Asp.NetCore WebAPI 构建测试?

如何让 Simple Injector 为同一个接口使用不同的具体类,但使用不同的类

如何使用Simple Html Dom仅获得每个TR的第一个TD?

如何将 winston 与 simple() 结合使用来获得自定义输出?

在Simple Injector中,为什么单例或作用域服务依赖于瞬态服务是错误的

角度单元测试,模拟使用$ injector注入的服务

如何获得我使用的服务包的符号名称?

使用相同服务标签的售后如何获得剩余库存?