2011-12-13 21 views
14

Estoy tratando de implementar una estrategia de validación en mi aplicación. Tengo una capa MVC, capa de servicio, repositorio y POCOs de dominio. Ahora, en la capa MVC, utilizo anotaciones de datos en mis modelos de vista para validar la entrada del usuario, lo que me permite darle al usuario comentarios rápidos. En el controlador, llamo a ModelState.IsValid para verificar la entrada antes de usar el automapper para configurar un objeto de dominio.Validación de la capa de servicio

Aquí es donde está mi problema. Paso mi objeto de dominio al Servicio que necesita validarlo contra mis reglas de negocio, pero ¿cómo devuelvo los errores de validación al controlador? Los ejemplos que he encontrado hacen una de las siguientes:

  • Lanza una excepción en la capa de Servicio y atrapa en el Contoller. Pero esto parece incorrecto, sin duda las excepciones son para casos excepcionales, y deberíamos devolver algo significativo.
  • Use un ModelStateWrapper e inyecte ModelStateDictionary en el servicio. Pero este método finalmente se desarrolla en una dependencia circular (el controlador depende del servicio, el servicio depende del controlador) y esto parece ser un mal olor de código.
  • Agregue un método Validar al POCO. El problema con esto es que una regla comercial puede depender de otros objetos POCO, por lo que seguramente esto debe hacerse en el Servicio que tiene acceso a las tablas y objetos necesarios.

¿Existe un método más simple que me falta? He visto muchas preguntas con respecto a esto, pero ninguna solución concreta aparte de las mencionadas anteriormente. Estoy pensando que cualquier método en el Servicio que haga la validación podría simplemente devolver algún objeto clave/valor que pueda usar en el controlador, pero no estoy seguro de si esta estrategia podría ser problemática más adelante.

+0

Puede encontrar la siguiente respuesta útil: http://stackoverflow.com/a/4851953/29407 –

+0

Gracias Darin, esto parece prometedor, aunque va por el enfoque de excepción. ¿Es esta la forma comúnmente aceptada de hacer esto? – James

Respuesta

13

Creo que una forma bastante elegante sería tener un método de validación en su servicio que devuelva un diccionario de errores de modelo de la lógica comercial. De esta forma, no hay inyección de ModelState en el servicio; el servicio simplemente valida y devuelve cualquier error. Luego depende del controlador fusionar estos errores ModelState nuevamente en ViewData.

lo tanto, el método de validación del servicio podría ser:

public IDictionary<string, string> ValidatePerson(Person person) 
{ 
    Dictionary<string, string> errors = new Dictionary<string, string>(); 

    // Do some validation, e.g. check if the person already exists etc etc 

    // Add model erros e.g.: 
    errors.Add("Email", "This person already exists"); 
} 

Y entonces se podría utilizar un método de extensión en su controlador para asignar estos errores en el ModelState, algo así como:

public static class ModelStateDictionaryExtensions 
{ 
    public static void Merge(this ModelStateDictionary modelState, IDictionary<string, string> dictionary, string prefix) 
    { 
     foreach (var item in dictionary) 
     { 
      modelState.AddModelError((string.IsNullOrEmpty(prefix) ? "" : (prefix + ".")) + item.Key, item.Value); 
     } 
    } 
} 

Y su controlador entonces usaría:

ModelState.Merge(personService.ValidatePerson(person), ""); 
+0

¿Se llamaría esto desde el controlador antes de usar Service.Create() (por ejemplo)? Pero, ¿qué ocurre si Service.Create() tiene alguna lógica de negocios especializada que falla en el objeto pasado en pasaje? ¿Cómo informaríamos al controlador? Me parece que cada método de Servicio requiere sus propias validaciones únicas y, por lo tanto, una forma de informar al controlador de los problemas. ¿Supongo que mis métodos de servicio podrían devolver IDictionary? – James

+0

Sí, puede llamar a Validar por separado, pero validar también debe invocarse desde el servicio en el método de creación. De esta forma, el método create también podría devolver un diccionario de errores. Si el diccionario está vacío, la validación fue exitosa. –

+0

Creo que este puede ser el camino a seguir. No quiero lanzar excepciones sin una buena razón, como se sugiere en muchas guías. ¿Has usado este método tú mismo? Mi principal preocupación es que si un método de servicio ya tiene un tipo de devolución, entonces no podré devolver IDictionary. Supongo que esta es la razón por la cual algunas guías usan ModelStateWrapper. – James

1

Como alternativa a cre En un diccionario intermedio como lo sugiere Ian, también podría hacer esto con un validador que acepte una función.

p. Ej.en la capa de servicio:

public void ValidateModel(Customer customer, Action<string, string> AddModelError) 
{ 
    if (customer.Email == null) AddModelError("Email", "Hey you forgot your email address."); 
} 

Luego, en su controlador se valida con una sola llamada:

myService.ValidateModel(model, ModelState.AddModelError); 

O que usted desea utilizar su validador en una aplicación de consola que no tienen acceso a un ModelStateDictionary, usted podría hacer esto:

errors = new NameValueDictionary(); 
myService.ValidateModel(model, errors.Add); 

Ambos trabajo porque ModelStateDictionary.AddModelError() y NameValueDictionary.Add() coincide con la firma del método para Action<string, string>.

Cuestiones relacionadas