Model Binding Parte 3: Actualización y Validaciones (Serie ASP.NET vNext)

Este es el quinto de una serie de post que estoy haciendo sobre ASP.NET vNext.

La próxima release de Visual Studio incluye un montón de nuevas características de edición de código (de las que hablaré también en el futuro).

Este post es el tercero de tres en el que hablamos sobre el nuevo soporte de Model Binding en Web Forms. Model Binding es una extensión del sistema de databinding de ASP.NET Web Forms , y ofrece un paradigma de acceso a datos centrados en el código. Hace uso de un montón de conceptos de binding que introdujimos con ASP.NET MVC - y los integra en el modelo de controles de servidor de Web Forms.

Si aún no lo has hecho, lee las dos primeras partes de esta serie sobre Model Binding, que muestran las bases de seleccionar datos a través de la propiedad SelectMethod en controles de datos como el GridView y sobre cómo filtrar esos datos basándose en la entrada de usuario.

En el post de hoy veremos cómo Model Binding mejora la experiencia de data binding cuando actualizamos datos.

Introducción

Vamos a partir del ejemplo del GridView del post anterior, configurado para usar Model Binding para mostrar algunos datos de la tabla Products de la base de datos Northwind. El GridView llama al método configurado GetProducts(), que que está usando Entity Framework Code First para devolver tan sólo la propiedad Products del contexto instanciado de Northwind.

Es muy importante que la propiedad DataKeyNames sea la primary key del modelo, de forma que el GridView sepa viajar en la página entre el navegador y el servidor:

<asp:GridView ID="productsGrid" runat="server" DataKeyNames="ProductID"

    ModelType="ModelBindingPart3.Product"

    AllowPaging="true" AllowSorting="true" AutoGenerateColumns="false"

    SelectMethod="GetProducts">

    <Columns>

        <asp:BoundField DataField="ProductID" HeaderText="ID" />

        <asp:BoundField DataField="ProductName" HeaderText="Name" SortExpression="ProductName" />

        <asp:BoundField DataField="UnitPrice" HeaderText="Unit Price" SortExpression="UnitPrice" />

        <asp:BoundField DataField="UnitsInStock" HeaderText="# in Stock" SortExpression="UnitsInStock" />

    </Columns>

</asp:GridView>

Aquí tenéis el codebehind (que contiene el método GetProducts()):

public partial class _Default : Page

{

    private Northwind _db = new Northwind();

 

    public IQueryable<Product> GetProducts()

    {

        return _db.Products;

    }

}

Si ejecutamos esta página nos muestra una tabla con los datos de los productos:

Grid con los datos de los productos

Grid con los datos de los productos

Como el método GetProducts() devuelve un IQueryable<Product>, podemos paginar y ordenar los datos fácilmente en nuestro Grid. Sólo las 10 filas que están visibles son las que se devuelven a la página desde la base de datos.

Habilitando el soporte de edición

Podemos habilitar la edición de las filas del GridView seteando el atributo AutoGenerateEditButton a true. El GridView renderizará un link de edición para cada fila, el usuario puede hacer clic para saltar al modo edición.

Lo siguiente, necesitamos configurar el GridView para que llame al método de actualización (Que añadiremos en el code-behind en un momento). Esto lo hacemos seteando el atributo UpdateMethod con el nombre de nuestro método, en este caso "UpdateProduct". Nuestro Grid View se parece entonces a esto:

<asp:GridView ID="productsGrid" runat="server" DataKeyNames="ProductID"

    ModelType="ModelBindingPart3.Product"

    AllowPaging="true" AllowSorting="true"

    AutoGenerateColumns="false" AutoGenerateEditButton="true"

    SelectMethod="GetProducts" UpdateMethod="UpdateProduct">

    <Columns>

        <asp:BoundField DataField="ProductID" HeaderText="ID" />

        <asp:BoundField DataField="ProductName" HeaderText="Name" SortExpression="ProductName" />

        <asp:BoundField DataField="UnitPrice" HeaderText="Unit Price" SortExpression="UnitPrice" />

        <asp:BoundField DataField="UnitsInStock" HeaderText="# in Stock" SortExpression="UnitsInStock" />

    </Columns>

</asp:GridView>

Si ejecutamos la página ahora, podemos hacer clic en cualquier link de edición para poner el grid en el modo edición:

 

Grid View en modo edición

Grid View en modo edición

Ahora tenemos que implementar el método UpdateProduct() en el code-behind. Hay un par de formas de hacerlo:

Primera forma:

En esta primera forma haremos que el sistema de model binding pase una instancia de Product a nuestro método de actualización, que usaremos para enlazar nuestro modelo EF para que aplique los cambios a la base de datos:

public void UpdateProduct(Product product)

{

    // 'product' was implicitly model bound using data-control values

    _db.Entry(product).State = System.Data.EntityState.Modified;

    _db.SaveChanges();

}

El método sólo tiene un parámetro del tipo al que está enlazado el GridView - que es el tipo Product. Cuando es llamado por el sistema de Model Binding, creará una instancia de la clase Product e intenta enlazar los valores del control de datos (nuestro GridView) en cada miembro. El método UpdateProduct le dice a EF Code First que el estado del objeto ha sido modificado - esto hará que lo marque como objeto modificado, para al final guardar los cambios.

Segunda Forma:

La primera forma funciona muy bien para clases simples, normalmente el control de datos no será capaz de ofrecer valores para cada miembro del objeto de datos, ya que no fueron renderizados en la página o representan relaciones con otros objetos, una mejor aproximación es cargar el objeto a actualizar desde la base de datos (usando la primary key), e indicar explícitamente al sistema de Model Binding que enlace los valores del control de datos a las propiedades que puedan. Vamos a cambiar un poco el método de actualización para que funcione de esa forma:

public void UpdateProduct(int productId)

{

    var product = _db.Products.Find(productId);   

    // Explicitly model bind data-control values onto 'product'

    TryUpdateModel(product);

    _db.SaveChanges();

}

Esta vez, el sistema de Model Binding obtendrá el parámetro productId de los DataKeys del grid (lo hace implicitamente; no es necesario ningún value provider). Después obtenemos el objeto de la base de datos, y llamamos al método TryUpdateModel para enlazar los valores del control al objeto. Una vez hecho, ya podemos guardar los cambios en la base de datos.

Validación del modelo.

Por supuesto, la mayoría de modelos de datos incluyen algún tipo de reglas de validación. Para hacer esto más fácil, el sistema de Model binding de Web Forms soporta el modelo de validación usando los mismos atributos de validación del namespace System.ComponentModel.DataAnnotations que usan ASP.NET Dynamic Data, MVC, Entity Framework y Silverlight RIA Services. Podemos decorar las propiedades de nuestro modelo de clases con estos atributos para ofrecer información sobre la "forma" de nuestro modelo, incluyendo detalles sobre qué propiedades necesitan un dato y el rango de valores válidos.

Vamos a actualizar la clase Product con información de validación que se corresponda en la base de datos:

public class Product

{

    public int ProductID { get; set; }

    [Required, StringLength(40)]

    public string ProductName { get; set; }

    [StringLength(20)]

    public string QuantityPerUnit { get; set; }

    [DataType(DataType.Currency)]

    public decimal? UnitPrice { get; set; }

    public short? UnitsInStock { get; set; }       

    public short? UnitsOnOrder { get; set; }

    public short? ReorderLevel { get; set; }       

    public bool Discontinued { get; set; }

    public int? CategoryID { get; set; }

    public virtual Category Category { get; set; }

}

Ahora, cuando se enlazan valores a este tipo, el sistema de Model Binding comprobará estas reglas cualquier violación de elllas en la propiedad ModelState de la página. Y explícitamente podemos comprobar la propiedad para asegurarnos de que el estado del modelo es válido ántes de aplicar los cambios:

public void UpdateProduct(int productId)

{

    var product = _db.Products.Find(productId);           

    TryUpdateModel(product);           

    // Check whether there were any validation errors

    if (ModelState.IsValid)

    {

        _db.SaveChanges();

    }           

}

Para que poder mostrar los posibles errores en el modelo, podemos añadir un control <asp:ValidationSummary>, con la propiedad ShowModelStateErrors  a true:

<asp:ValidationSummary runat="server" ShowModelStateErrors="true" />

Ahora, si intentamos actualizar una propiedad con un valor erroneo, el GridView se mantendrá en el modo de edición y veremos el error en el control ValidationSummary:

GridView con un mensaje en el ValidationSummary

GridView con un mensaje en el ValidationSummary

Podemos añadir mensajes personalizados al model state de la página, para representar otros errores que no se cubran con los atributos de validación. Igual que con ASP.NET MVC Controllers, la clase base de una página de Web Forms ahora tiene la propiedad "ModelState" que podemos usar para calcular mensajes de error personalizados que los controles de validación de la página no pueden mostrar y usarlos para mostrar el error que queramos.

Vídeo de Enlace de modelos y filtrado.

Damian Edwards nos muestra en un video el uso dem odel binding para implementar escenarios de actualización. Podéis ver el vídeo aquí.

Resúmen

El sistema de Model Binding en ASP.NET Web Forms 4.5 hace fácil el trabajo con datos y la entrada de usuario usando un paradigma de código centrado en datos. Toma prestado algunos de los conceptos de model binding que se introdujeron en ASP.NET MVC, y usan de forma consistente los atributos de DataAnnotations tanto en MVC como en Web Forms. Todas las técnicas que hemos visto son aplicables a otros controles de datos de ASP.NET Web Forms como el FormView, DetailsView y el ListView.

Espro que sirva.

Scott.

Traducido por: Juan María Laó Ramos.

Artículo original.