7

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.

Community
  • 1
  • 1
blogbydev
  • 1,445
  • 2
  • 17
  • 29

1 Answers1

-3

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.

Ruskin
  • 1,504
  • 13
  • 25