Este artículo proviene de un motor de traducción automática.

Los puntos de datos

Control de las validaciones con Entity Framework en WCF Data Services

Julie Lerman

Descargar el ejemplo de código

Julie LermanEstoy escribiendo esta columna en la víspera de la Conferencia de generación de Microsoft.El núcleo de toda la emoción de construir era, por supuesto, el nuevo Metro UI para Windows 8 que se encuentra en el nuevo Windows Runtime (WinRT).Si eres un geek de datos, usted puede has ya espera para ver qué opciones existen para proporcionar datos a aplicaciones de "estilo Metro".En este avance temprano, puede proporcionar datos de almacenamiento de archivos o desde la Web.Si desea interactuar con datos relacionales, opciones basadas en la Web incluyen JSON o XML sobre HTTP, zócalos y servicios.En el frente de los servicios, Metro-estilo apps proporcionará bibliotecas de cliente para consumir OData, lo que significa que cualquier experiencia que tienes hoy trabajando con OData a través de Microsoft.NET Framework, Silverlight o otras bibliotecas de cliente le dará una gran ventaja cuando esté listo para consumir OData en las aplicaciones de estilo de Metro.

Con esto en mente, te dedico esta columna a trabajar con OData.La versión de Entity Framework (EF) que contiene el primer código y la DbContext introdujeron una nueva API de validación.Les mostraré cómo aprovechar de validación del lado del servidor integrado cuando su modelo EF código primero es ser expuesto como OData mediante WCF Data Services.

Conceptos básicos de validación API

Usted puede estar familiarizado con configuración de atributos tales como necesarios o MaxLength a propiedades de la clase mediante anotaciones de datos o la API de fluido.Estos atributos se pueden comprobar automáticamente por la nueva API de validación."Entity Framework 4.1 validación," un artículo en el centro de desarrolladores de MSDN datos (msdn.microsoft.com/data/gg193959), demuestra esto, así como aplicar normas con la interfaz de IValidatableObject y el método ValidateEntity.Si bien ya se pueden validar datos y anotaciones IValidatable­objeto del lado del cliente, sus reglas también pueden comprobarse en el servidor junto con cualquier lógica de ValidateEntity que has añadido.Como alternativa, también puede desencadenar la validación de la demanda en el código de servidor.

Aquí, por ejemplo, es una simple clase de persona que utiliza dos anotaciones de datos (el primero especifica que la propiedad LastName es necesaria y la otra establece una longitud máxima del campo de cadena de IdentityCard):

public class Person
{
  public int PersonId { get; set; }
  public string FirstName { get; set; }
  [Required]
  public string LastName { get; set; }
  [MaxLength(10)]
  public string IdentityCardNumber { get; set; }
}

De forma predeterminada, EF realizará la validación cuando se llama a SaveChanges. Si cualquiera de estas reglas falla, EF producirá un Sys­tem.Da­ta.ENT­dad.DbEntityValidationException: que tiene una estructura interesante. Se describe cada error de validación en un DbValidationError, y DbValidationErrors se agrupan por instancia de objeto en conjuntos de EntityValidationErrors.

Por ejemplo, figura 1 muestra un DbEntityValidationException que se produce si EF detecta problemas de validación con dos diferentes instancias de la persona. El primer objeto EntityValidationErrors contiene un conjunto de DbValidationErrors para una sola persona donde había dos errores: no LastName y el IdentityCard tenían demasiados caracteres. La segunda instancia de la persona tenía un solo problema; por lo tanto, es sólo una DbValidationError en el segundo objeto de EntityValidationErrors.

DbEntityValidationException Contains Grouped Sets of Errors
Figura 1 DbEntityValidationException contiene conjuntos agrupados de errores

En el artículo de MSDN Data Developer Center mencioné, mostró la excepción que se pasa a un modelo-vista -­aplicación de controlador (MVC) que supo descubrir y mostrar los errores específicos.

En una aplicación distribuida, sin embargo, los errores pueden no hacerlo hacia el lado del cliente utilizado e indicarse tan fácilmente. Mientras que la excepción de nivel superior puede ser devueltos, la aplicación cliente no puede tener ninguna idea cómo profundizar un DbEntityValidationException para encontrar los errores. Con muchas aplicaciones, no incluso tendrá acceso a los nombres de System.Data.Entity y por lo tanto, ningún conocimiento de la DbEntityValidationException.

Más problemático es cómo WCF Data Services transmite excepciones por defecto. En el lado del cliente, sólo obtendrá un mensaje que indica "Error al procesar esta solicitud." Pero la frase crítica aquí es "por"defecto. Puede personalizar sus servicios de datos de WCF para analizar DbEntityValidationExceptions y devolver información útil de error al cliente. Esto es lo que me centraré en el resto de esta columna.

Errores de validación de ocultar WCF resultados del servicio de datos por defecto

Mi modelo está alojado en una capa de datos de DbContext he llamado PersonModelContext:

public class PersonModelContext : DbContext
  {
    public DbSet<Person> People { get; set; }
  }

Tengo un servicio de datos simples que expone el tipo de persona de este contexto para leer y escribir:

public class DataService : DataService<PersonModelContext>
{
  public static void InitializeService(DataServiceConfiguration config)
  {
    config.SetEntitySetAccessRule("People", EntitySetRights.All);
    config.DataServiceBehavior.MaxProtocolVersion =
      DataServiceProtocolVersion.V3;
  }
}

Porque estoy usando código primero, tendría que hacer algunos ajustes para obtener servicios de datos de WCF para trabajar con él. En lugar de ajustar, lo he sustituido la Microsoft.NET Framework 4 System.Data.Services y System.Data.ClientServices con las bibliotecas de Microsoft.Data.Services y Microsoft.Data.ClientServices de marzo de 2011 WCF Data Services CTP (véase bit.ly/mTI69m), que tiene esos ajustes construidos en. Es por eso la DataServiceProtocolVersion se establece en V3.

Finalmente, estoy consumiendo el servicio con una aplicación de consola simple que utiliza el siguiente método para insertar a una persona:

private static void InsertPersonNoLastName()
{
  var person = new Person
  {
    FirstName = "Julie",
    IdentityCardNumber="123456789",
  };
  var context = new PersonModelContext
   (new Uri("http://localhost:43447/DataService.svc"));
 context.AddToPeople(person);
 context.SaveChanges();
}

Observe que he descuidado establecer la propiedad LastName. Porque LastName está configurado para ser requeridos, el EF producirá una red­cion para el servicio de datos, pero la aplicación de consola sólo recibirán un DataServiceRequestException con el mensaje descrito anteriormente ("Error al procesar esta solicitud."). Si profundizar la excepción interna, encontrará que contiene el mismo mensaje y no hay detalles adicionales.

WCF Data Services tiene una opción que le permite enviar mensajes de excepción espalda con más detalles, agregando lo siguiente al método InitializeService:

#if DEBUG
  config.UseVerboseErrors = true;
#endif

Ahora el mensaje interno (que figura en la respuesta XML del servicio) le dice: "error en la validación de una o más entidades. Ver la propiedad 'EntityValidationErrors' para obtener más detalles". Pero lamentablemente, las EntityValidationErrors no consigue pasó atrás con la excepción. Así que ya sabéis que la API de validación se encuentra uno o más problemas, pero no podrá descubrir algo más sobre el error. Tenga en cuenta que envuelto UseVerboseErrors en una directiva del compilador. UseVerboseErrors debe ser utilizado para la depuración, no lo desea en el código de producción.

Reemplazar el método HandleException

WCF Data Services expone un método virtual de (Overrideable) llamado HandleException. Esto le permite capturar cualquier excepción que se produce en el servicio, analizarlo y construir su propio DataServiceException para volver a la persona que llama. En este método es que puede analizar los errores de validación y devuelve información más significativa a la aplicación que llama. La firma del método es:

protected override void HandleException(HandleExceptionArgs args)

El tipo de HandleExceptionArgs tiene un número de propiedades: excepción, ResponseContentType, esponseStatusCode, respuesta­escrito, UseVerboseErrors

Me interesa es la propiedad de excepción. Esto es donde puede capturar e identificar excepciones lanzada por la API de validación: DbEntityValidationException. También puede controlar otros tipos de errores aquí, pero se centrará en buscar y analizar las excepciones de validación. Tengo el espacio de nombres System.Data.Entity.Validation en mi mediante instrucciones en la parte superior de la clase para que no tenga que fuertemente escriba la excepción.

Comenzaré con la presunción que está validada sólo una sola entidad, razón por la cual yo estoy consultando sólo para la primera entidad­ValidationErrors contenida en la excepción, como se muestra en figura 2. Si desea que el servicio para validar varios objetos, asegúrese de utilizar el parámetro SaveChangesOptions.Batch cuando llames SaveChanges. Caso contrario, sólo se guardará y validado en un momento en un objeto y una vez que consigues un error, no hay más objetos serán guardados o validados.

Figura 2 construir un mensaje de excepción más útil

protected override void HandleException(HandleExceptionArgs args)
{
  if (args.Exception.GetType()==
    typeof(DbEntityValidationException))
  {
    var ex=args.Exception as DbEntityValidationException;
    var errors = ex.EntityValidationErrors.First().ValidationErrors.ToList();
    var errorMessage=new StringBuilder();
    foreach (System.Data.Entity.Validation.DbValidationError e in errors)
    {
      errorMessage.AppendLine(e.ErrorMessage);
    }
    args.Exception = new DataServiceException(500, errorMessage.ToString());
  }
}

Lo que está sucediendo en este método es que primero compruebe si la excepción es el tipo iniciado por la API de validación. Si es así, que tire la excepción en la variable "ex". A continuación, consulta para obtener una lista de todos los DbValidationErrors contenidos en el primer conjunto de EntityValidationErrors en la excepción. Entonces construir una nueva cadena de error utilizando la propiedad ErrorMessage de cada EntityValidationError y devuelva esa cadena a la aplicación que llama en un nuevos datos­ServiceException. EntityValidationError tiene otras propiedades, pero genera un mensaje de error completo utilizando el nombre de la propiedad y el problema de la validación en el mensaje de error. Con el primero de código puede especificar un mensaje de error personalizado, pero estoy contento con los valores predeterminados para esta demostración. En este ejemplo, el mensaje es "el campo apellido es necesario". Tenga en cuenta que el constructor de DataServiceException tiene una serie de sobrecargas. Estoy manteniendo simple proporcionando sólo el código 500 "error interno del servidor" y una cadena con el mensaje que quiero transmitir.

Analizar la nueva excepción en el cliente

Ahora, en el lado del cliente, aún obtendrás una excepción que dice "Error al procesar esta solicitud", pero esta vez de de que la excepción interna contiene el mensaje "el campo apellido es necesario".

Pero no es una cadena simple. El mensaje de un DataServiceRequestException es el formato en una respuesta HTTP porque la solicitud se realiza a través de HTTP:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns=
  "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code></code>
  <message xml:lang="en-US">The LastName field is required.&#xD;
  </message>
</error>

Una de las sobrecargas de la DataServiceException construyó en el servicio le permite insertar códigos de error personalizados. Si había utilizado, el código personalizado se muestra en el <code> elemento del error. Si está llamando al servicio de una aplicación Web, puede ser capaz de mostrar la respuesta HTTP directamente en su interfaz de usuario. De lo contrario, probablemente deseará analice lo que se puede controlar la excepción utilizando cualquier patrones utilizas en su aplicación para tratar los errores.

Estoy usando LINQ to XML para extraer el mensaje y, a continuación, puedo mostrarlo en mi aplicación de consola. Pido SaveChanges en un bloque try/catch, analizar y mostrar el mensaje de error (véase figura 3). Figura 4 muestra los resultados de la excepción de cliente.

Figura 3 analizar y mostrar el mensaje de Error devuelto por el servicio

try
{
  context.SaveChanges();
}
catch (Exception ex)
{
  var sr = new StringReader(ex.InnerException.Message);
  XElement root = XElement.Load(sr);
  IEnumerable<XElement> message =
    from el in root.Elements()
    where el.Name.LocalName == "message"
    select el;
  foreach (XElement el in message)
    Console.WriteLine(el.Value);
  Console.ReadKey();
}

Parsed Error Message Displayed in the Client
Figura 4 analiza el mensaje de Error mostrado en el cliente

Ahora voy tirar otra llave en el método InsertPerson. Además de descuidar la propiedad LastName, voy a poner demasiados caracteres en la identidad­tarjeta de propiedad. Recuerde que esta propiedad se ha configurado para tener un MaxLength de 10:

var person = new Person
{
  FirstName = "Julie",
  IdentityCardNumber="123456789ABCDE"
};

Ahora el método de HandleException encontrará dos DataValidation­errores de la instancia de la persona que trató de actualizar el servicio. StringBuilder contendrá un mensaje de dos líneas: una que describe el problema con la propiedad LastName y otro para explicar el problema con la propiedad IdentityCard.

En la aplicación de consola, esto será visto como un único mensaje de la excepción:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<error xmlns=
  "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
  <code></code>
  <message xml:lang="en-US">The field IdentityCardNumber must be a string or array type with a maximum length of '10'.&#xD;
    The LastName field is required.&#xD;
  </message>
</error>

El LINQ to XML parser después retransmitirá el mensaje a la consola, como se muestra en figura 5.

Console App Displaying Multiple Errors for a Single Entity
Figura 5 consola App mostrar varios errores para una sola entidad

Beneficiarse de validación incluso cuando desconectado

Ya has visto cómo, mediante un simple conjunto de requisitos aplicados mediante anotaciones de datos primer código, puede capturar y analizar las excepciones de validación de EF, devolverlos a un cliente y, en el lado del cliente, analizar la excepción regresó a través de HTTP. Si está trabajando con las reglas de validación aplicadas mediante configuraciones de propiedad o reglas más complejas que se pueden especificar con IValidationObject o reemplazando el método ValidateEntity, el EF siempre devolverá DbEntity­ValidationExceptions. Ahora sabe cómo analizar a través de aquellos y puede ampliar la lógica para acomodar varios objetos, proporcionar mensajes de error que contiene más detalles y manejarlos en el servidor o el cliente según lo requiera la aplicación.

Porque WCF Data Services devuelve OData, puede consumir estos servicios y aproveche la validación hoy y la práctica, por lo que puede estar dispuesta a hacer lo mismo con las tecnologías del futuro Metro-estilo.

Julie Lerman es un MVP de Microsoft.NET mentor y consultor que vive en las colinas de Vermont. Puede encontrar su presentación sobre acceso a datos y otro Microsoft.NETOS temas en grupos de usuarios y conferencias alrededor del mundo. Blogs de ella en thedatafarm.com/blog y es el autor del aclamado libro "Programming Entity Framework" (o ' Reilly Media, 2010). Seguirla en Twitter en twitter.com/julielerman.

Gracias al siguiente experto técnico para revisar este artículo: Mike Flasko