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.