How can i have a IServiceProvider available in ValidationContext parameter of IValidatableObject.Validate method

青春壹個敷衍的年華 提交于 2019-12-11 01:44:25

问题


Controller calls IValidatableObject.Validate internally and passes a ValidationContext object as an argument. I want to use validationContext.GetService() method to get a service object and use it.

I can pass this service as a dependency to controller constructor using AutoFac(DI Injection dll). How do i make it availale to the ValidationContext object?

This stackoverflow question might contain the answer but i do not understand it fully : Asp.Net MVC3: Set custom IServiceProvider in ValidationContext so validators can resolve services

Here is the code:

Model : Employee

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;

namespace ValidationContextDemo.Models
{
    public class Employee : IValidatableObject
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int DepartmentId { get; set; }

        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var result = new List<ValidationResult>();
            var EmployeeService = (Service.EmployeeService)validationContext.GetService(typeof(Service.EmployeeService));
            if (!EmployeeService.IsValidDepartment(this.DepartmentId))
            {
                result.Add(new ValidationResult("This DepartmentId does not exists"));
            }
            return result;
        }
    }
}

Repository : EmployeeRepository

using System.Collections.Generic;
using System.Linq;

namespace ValidationContextDemo.Models
{
    public class EmployeeRepository
    {
        public EmployeeRepository()
        {
            Employees = new List<Employee>() { 
            new Employee{Id=1, Name="Alpha", DepartmentId=1},
            new Employee{Id=2, Name="Beta", DepartmentId=1},
            new Employee{Id=3, Name="Gamma", DepartmentId=1}
            };
        }
        public List<Employee> Employees { get; set; }

        public void AddEmployee(Employee e)
        {
            Employees.Add(e);
        }

        public void UpdateEmployee(int id, Employee e)
        {
            Employee emp = Employees.Where(x => x.Id == id).FirstOrDefault();
            emp.Name = e.Name;
            emp.DepartmentId = e.DepartmentId;
        }

        public void DeleteEmployee(Employee e)
        {
            Employees.Remove(e);
        }
    }
}

Validation Source : Enum

namespace ValidationContextDemo.Enums
{
    public enum Department
    {
        Engineering=1,
        Sales=2,
        Shipping=3,
        HumanResources=4
    }
}

Service : EmployeeService

using System;
using System.Linq;

namespace ValidationContextDemo.Service
{
    public class EmployeeService
    {
        public bool IsValidDepartment(int departmentId)
        {
            return Enum.GetValues(typeof(Enums.Department)).Cast<Enums.Department>().Contains((Enums.Department)departmentId);
        }
    }
}

IServiceProvider : EmployeeServiceProvider

using System;

namespace ValidationContextDemo.Service
{
    public class EmployeeServiceProvider: IServiceProvider
    {

        public object GetService(Type serviceType)
        {
            if (serviceType==typeof(EmployeeService))
            {
                return new EmployeeService();
            }
            return null;
        }
    }
}

Controller : EmployeeController

using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using ValidationContextDemo.Models;
using ValidationContextDemo.Service;

namespace ValidationContextDemo.Controllers
{
    public class EmployeeController : ApiController
    {
        EmployeeRepository _repository;
        EmployeeServiceProvider _serviceProvider;
        public EmployeeController()
        {
            _repository = new EmployeeRepository();
            _serviceProvider = new EmployeeServiceProvider();
        }
        public IHttpActionResult Get()
        {
            return Json(_repository.Employees);
        }

        public HttpResponseMessage Post(Employee e)
        {
            ValidationContext vContext = new ValidationContext(e, _serviceProvider, null);
            e.Validate(vContext);
            _repository.AddEmployee(e);
            return new HttpResponseMessage(HttpStatusCode.Created);
        }

    }
}

Notice the ValidationContext parameter in Validate method of Employee model. Before model binding, when the validation happens, the IServiceProvider part of ValidationContext is null.

So, after model binding, when the code reaches inside my Controller Action "Post", i create another ValidationContext with a _serviceProvider and call Validate again.

My question is , how can i have this _serviceProvider in my ValidationContext before model binding.

Please let me know if this is still not clear.

Note: I created this example for the sake of this question, i am not using Autofac as a DI container in this example.


回答1:


For starters your above code example is pretty irrelevant to the problem you are facing because you can potentially hack around the issue by a simple null check in the Employee model as you are validating it explicitly:

ValidationContext vContext = new ValidationContext(e, _serviceProvider, null);
e.Validate(vContext);

I assume (ideally) you don't want to do that explicitly and unfortunately there is no easy way. You have to set up a fair amount of plumbing (i.e. model binders etc) to hook into the MVC/WebApi pipelines. This solution will most likely work for you if you have the time to follow it :).

PS: The solution is catered towards MVC but I don't see any reason why it can be tweaked for the API.



来源:https://stackoverflow.com/questions/30213330/how-can-i-have-a-iserviceprovider-available-in-validationcontext-parameter-of-iv

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!