WebGrid en MVC 3, paso a paso (y II)

ASP.NET MVCDías atrás comentábamos por aquí cómo utilizar el helper WebGrid de MVC 3 para crear rejillas de datos, partiendo de un ejemplo muy sencillo, apenas un par de líneas de código, e introduciendo sucesivas mejoras hasta llegar a un interfaz bastante aceptable para manejar conjuntos de datos con paginación, ordenación, y columnas personalizadas.

WebGrid, con algo de estilo

Sin embargo, también comentábamos que la pasmosa simplicidad con la que podíamos poner en marcha un grid completo tenía su coste: por defecto, WebGrid necesita disponer de una copia en memoria del conjunto completo de datos sobre el que actúa. Sí, completo :-O.

Es decir, si tenemos un millón de filas de nuestra base de datos y queremos mostrarlas en un orden concreto y por páginas, debemos suministrar a WebGrid una colección con el millón de entidades materializadas en memoria; el helper las ordenará según el criterio activo en ese momento, contará el total de elementos, calculará las páginas necesarias para mostrarlos en función del número de elementos por página definidos, y obtendrá el subconjunto de instancias correspondientes a la página actual. Tras ello, generará el grid y el mecanismo de paginación que podemos ver en su pie.

Obviamente, el impacto en el rendimiento de nuestro sistema puede ser terrible. La materialización de entidades no es un proceso rápido, como tampoco lo es la ordenación en memoria cuando el número de elementos es importante. Y lo peor es que el procedimiento completo se repite para cada petición y usuario conectado, lo que hace que utilizar los mecanismos de paginación y ordenación por defecto no sea una buena idea la mayoría de las veces.

Sin duda es bastante mejor delegar al motor de base de datos las tareas de ordenación y selección de filas, y materializar en memoria sólo las entidades que sean necesarias en la vista actual, es decir, la página de datos visible. ¿Lo hacemos?

1. El Modelo, revisitado

Recordemos que en el post anterior teníamos una clase de servicios del Modelo simplísima, con un único método llamado ObtenerPersonas(), que utilizaba un contexto de objetos de Entity Framework para obtener todas las instancias de la clase Persona almacenadas en la base de datos:

public class ModelServices: IDisposable
{
    private readonly DatosEntities _datos = new DatosEntities();
 
    public IEnumerable<Persona> ObtenerPersonas()
    {
        return _datos.Personas.ToList();
    }
 
    public void Dispose()
    {
        _datos.Dispose();
    }
}

En este caso, dado que pretendemos delegar al Modelo la responsabilidad de paginar y ordenar adecuadamente los datos antes de enviarlos a WebGrid, necesitamos añadirle dos nuevos métodos: ContarPersonas(), y ObtenerPaginaDePersonas().

El primero de ellos, ContarPersonas(), que retornará el número de elementos totales existentes en la base de datos. Ya hemos comentado antes que WebGrid podía obtener este dato simplemente contando el total de elementos presentes en el origen de datos, pero como ahora pretendemos pasarle sólo los datos de la página actual, esta información debemos indicársela expresamente para que pueda construir el mecanismo de paginación correctamente.

El código de este método es bastante simple:

public int ContarPersonas()
{
        return _datos.Personas.Count();
}

Vamos ahora a implementar ObtenerPaginaDePersonas(). Como seguro podréis intuir, su objetivo es retornar el conjunto de instancias de Persona que deben aparecer en una página concreta de datos, teniendo en cuenta el número de elementos por página empleado y el criterio de ordenación actual.

Asumiendo que estamos utilizando LINQ, la obtención de una página concreta de datos es trivial utilizando los operadores Skip() y Take(). La única dificultad con la que podemos encontrarnos es la forma de pasar al Modelo el criterio de ordenación actual, pero, como podemos comprobar a continuación hay muchas formas de conseguirlo, casi tantas como desarrolladores que se enfrenten a ella. Para que os hagáis una idea, veremos dos posibilidades: utilizando expresiones de cadena, y basada en expresiones lambda.

1.1. ObtenerPaginaDePersonas(), magic string edition

Una posibilidad bastante sencilla de implementar consiste en que el Modelo reciba directamente una cadena de caracteres en la que se encuentre la expresión de ordenación utilizada. Si podemos asegurar, además, que esta cadena venga ya escrita en Entity SQL, simplemente tendríamos que insertar la cláusula de ordenación apropiada:

public IEnumerable<Persona> 
  ObtenerPaginaDePersonas(int paginaActual, int personasPorPagina, string criterioOrdenacion)
  {
     if (paginaActual<1) paginaActual = 1;
      return _datos.Personas
        .OrderBy(criterioOrdenacion)
        .Skip((paginaActual - 1) * personasPorPagina)
        .Take(personasPorPagina)
        .ToList();
  }

De esta forma, podemos invocar al Modelo pasándole directamente expresiones como “it.Apellidos DESC”, “it.FechaNacimiento ASC”, o incluso combinaciones como “it.Apellidos DESC, it.Nombre DESC”, que funcionarán perfectamente. Observad que la sintaxis debe ser eSQL (de ahí el prefijo “it.” que llevan delante las propiedades del objeto actual).

Este enfoque, aunque bastante sencillo y rápido de implementar tiene algunas contraindicaciones. El hecho de escribir estas expresiones en el interior de cadenas de texto hace que éstas sean inmunes a refactorizaciones o comprobaciones en tiempo de compilación, lo que aumenta la probabilidad de aparición de errores difíciles de detectar. Asimismo, estamos confiando ciegamente en que la expresión es eSQL sintáctica y semánticamente correcto, por lo que o bien nos arriesgamos a que reviente en tiempo de ejecución si no introducimos las comprobaciones oportunas, o bien debemos delegar a otro componente (por ejemplo, al Controlador) la responsabilidad de construir siempre expresiones válidas.

1.2. ObtenerPaginaDePersonas(), lambda edition

Si queremos evitar el envío de información en cadenas de texto, podemos seguir el enfoque propuesto por LINQ y utilizar árboles de expresión para trasladar los criterios de ordenación al Modelo.

En este caso, el método del Modelo recibirá, además de la página actual y el número de filas por página, un árbol de expresión que contiene la expresión de ordenación, y un elemento de la enumeración Direccion indicando si debe ordenarse ascendente o descendentemente. El código podría ser como el siguiente:

public IEnumerable<Persona> ObtenerPaginaDePersonas<T>(
           int paginaActual, int personasPorPagina, 
           Expression<Func<Persona,T>> ordenacion, 
           Direccion direccion)
{
    if (paginaActual<1) paginaActual = 1;
    IQueryable<Persona> query = _datos.Personas; 
    if (direccion == Direccion.Ascendente)
        query = query.OrderBy(ordenacion);
    else
        query = query.OrderByDescending(ordenacion);
    return query.Skip((paginaActual - 1) * personasPorPagina)
                .Take(personasPorPagina)
                .ToList();
}

Como se puede observar, se trata de un método genérico para dar cobertura a los distintos tipos de propiedad por los que es posible ordenar, que es el tipo de retorno del árbol de expresión ordenacion.

También se puede ver que se aprovecha de la ejecución diferida de LINQ to SQL para ir especificando de forma sucesiva las distintas cláusulas de la consulta. Ésta no será ejecutada hasta el final, en la llamada al método ToList(), que es cuando realmente se materializarán las instancias de Persona presentes en la página de datos actual.

De esta forma, conseguimos que desde el Controlador podamos obtener la página de datos, por ejemplo, así:

var personas = _services.ObtenerPaginaDePersonas(
                      page, 
                      personasPorPagina, 
                      p=>p.NumeroDeHijos,     // Expresión ordenación
                      Direccion.Descendente   // Ascendente);

Observad que, a diferencia de la técnica que vimos anteriormente, aquí sí que usamos tipado fuerte para indicar la ordenación. Un cambio de nombre de la propiedad NumeroDeHijos, por ejemplo, sería refactorizada correctamente o, cuanto menos, generaría un error en compilación.

Como principal inconveniente, decir que no es tan potente y flexible como la anterior. Por ejemplo, en este caso no podemos indicar de forma inmediata más de una propiedad de ordenación, cosa que antes sí era muy sencillo. Eso sí, en nuestro escenario, dado que vamos a ordenar según columnas independientes del grid, tampoco este aspecto es demasiado importante.

En definitiva, existen gran cantidad de posibilidades para implementar el método del Modelo que retorna las instancias pertenecientes a una página concreta de datos atendiendo a los criterios de ordenación especificados. Aquí hemos visto un par de ellas, pero basta con pararse a pensarlo un rato y fácilmente podremos idear un buen puñado más de soluciones.

Vamos a pasar a estudiar ahora qué cambios debemos realizar en el Controlador para realizar la paginación correcta.

2. El Controlador

Si habéis descargado el ejemplo del post anterior u os habéis peleado un poco con WebGrid, habréis notado que el mantenimiento del estado de la rejilla se realiza propagando entre llamadas una serie de parámetros en el query string que, por defecto, son los siguientes:

Querystring en peticiones generadas por WebGrid

  • page, que indica el número de página actual,
  • sort, la columna por la que estamos ordenando,
  • sortDir, la dirección de ordenación actual, indicada con las constantes “ASC” o “DESC”.

Básicamente se trata de la misma información que debemos suministrar al Modelo, previamente adaptada, para obtener la relación de personas de la página actual, por lo que el primer paso será utilizar la magia del binder para poder obtener estos valores directamente desde nuestra acción del controlador. De hecho, el siguiente podría ser un buen esqueleto de partida:

public ActionResult Index(int page = 1, string sort = "Apellidos", string sortDir = "ASC")
{
    // Obtener el número total de personas
    // Obtener la colección de personas de la página actual
    // Retornar la vista suministrándole esos datos
}

Observad el uso de parámetros opcionales para la acción, en los que especificamos un valor por defecto para cuando no estén indicados de forma explícita en la petición.

Ya en el cuerpo del método, la obtención del número de personas es inmediata gracias al método ContarPersonas() que hemos creado anteriormente en el Modelo. La obtención de las personas para poblar la rejilla depende del enfoque utilizado en la implementación del método ObtenerPaginaDePersonas(). A continuación veremos algunas posibilidades, aunque antes vamos a pararnos en otro detalle.

A diferencia del ejemplo del post anterior, en el que la Vista sólo necesitaba una lista de personas para maquetarse, en esta ocasión debemos suministrarle alguna información más:

  • el número de personas por página,
  • la colección de personas, aunque sólo las presentes en la página actual,
  • el número total de personas, para que WebGrid pueda crear la herramienta de navegación por páginas.

Por tanto, nos encontramos ante un escenario “de manual” para el uso de una entidad View Model, que contendrá toda esta información. Su definición podría ser la siguiente:

public class PaginaDePersonasViewModel
{
    public int NumeroDePersonas { get; set; }
    public IEnumerable<Persona> Personas { get; set; }
    public int PersonasPorPagina { get; set; }
}

De esta forma, podemos ir ya dando forma al cuerpo de la acción, que quedaría de la siguiente manera:

public ActionResult Index(int page = 1, string sort = "Apellidos", string sortDir = "ASC")
{
    const int personasPorPagina = 10;
    var numPersonas = _services.ContarPersonas();    
var
personas = ... // Obtener la colección de personas var datos = new PaginaDePersonasViewModel() { NumeroDePersonas = numPersonas, PersonasPorPagina = filasPorPagina, Personas = personas }; return View(datos); }

Como comentábamos anteriormente, la obtención de las personas a mostrar en la página actual dependerá de la implementación del Modelo. Veremos algunas posibilidades.

Una solución muy simple aunque bastante cándida e inocentona, basada en la primera de las implementaciones del Modelo que hemos visto anteriormente, podría ser la siguiente:

var personas = _services.ObtenerPaginaDePersonas(
                     page, 
                     personasPorPagina, 
                     "it." + sort + " " + sortDir
               );

Como podemos observar, construimos directamente la expresión de ordenación en Entity SQL basándonos en los parámetros recibidos en el query string. Obviamente, y dado que estos parámetros son fácilmente manipulables, un nombre de columna incorrecta o una dirección distinta de “ASC” o “DESC” provocarían una excepción en tiempo de ejecución:

Error al ordenar por una columna inexistente
Este error es fácil de evitar simplemente introduciendo código que compruebe estos parámetros y establezca valores por defecto en caso de existir algún problema, como el mostrado a continuación, donde comprobamos que la columna de ordenación se encuentra en una “lista blanca” de columnas permitidas, y que la ordenación es ascendente o descendente:

...
sortDir = sortDir.Equals("desc", StringComparison.CurrentCultureIgnoreCase) ? sortDir : "asc";
var validColumns = new[] { "apellidos", "fechanacimiento", "email", "numerodehijos" };
if (!validColumns.Any(c => c.Equals(sort, StringComparison.CurrentCultureIgnoreCase)))
    sort = "apellidos";
var personas = _services.ObtenerPaginaDePersonas(
                    page, 
                    personasPorPagina, 
                    "it." + sort + " " + sortDir
              );
...

Si en cambio preferimos utilizar en el Modelo la opción que vimos anteriormente basada en el uso de árboles de expresión, también podemos crear un código que suministre los criterios de ordenación en función de la columna y dirección actual:

IEnumerable<Persona> personas;
Direccion dir = sortDir.Equals("ASC", StringComparison.CurrentCultureIgnoreCase) ? 
                        Direccion.Ascendente:
                        Direccion.Descendente;
 
switch(sort.ToLower())
{
  case "fechanacimiento":
    personas = _services.ObtenerPaginaDePersonas(page, personasPorPagina, p => p.FechaNacimiento, dir);
    break;
  case "numerodehijos":
    personas = _services.ObtenerPaginaDePersonas(page, personasPorPagina, p => p.NumeroDeHijos, dir);
    break;
  case "email":
    personas = _services.ObtenerPaginaDePersonas(page, personasPorPagina, p => p.EMail, dir);
    break;
  case "apellidos":
    personas = _services.ObtenerPaginaDePersonas(page, personasPorPagina, p => p.Apellidos, dir);
    break;
  default:
    personas = _services.ObtenerPaginaDePersonas(page, personasPorPagina, p => p.Apellidos, dir);
    break;
}

En el proyecto que podréis descargar desde el enlace disponible al final del post, encontraréis ambas implementaciones completas.

Antes de pasar a la Vista, veamos el código completo del controlador pasando los criterios de ordenación al Modelo mediante cadenas. Exceptuando las líneas introducidas para mejorar la legibilidad del código, apenas llegamos a la docena de líneas para completar la acción:

public class Demo2Controller : Controller
{
    private ModelServices _services = new ModelServices();
 
    public ActionResult Index(int page = 1, string sort = "Apellidos", string sortDir = "ASC")
    {
        const int personasPorPagina = 10;
        var numPersonas = _services.ContarPersonas();
 
        sortDir = sortDir.Equals("desc", StringComparison.CurrentCultureIgnoreCase) ? sortDir : "asc";
        
        var validColumns = new[] { "apellidos", "fechanacimiento", "email", "numerodehijos" };
        if (!validColumns.Any(c => c.Equals(sort, StringComparison.CurrentCultureIgnoreCase)))
            sort = "apellidos";
        
        var personas = _services.ObtenerPaginaDePersonas(
                 page, 
                 personasPorPagina, 
                 "it." + sort + " " + sortDir
        );
 
        var datos = new PaginaDePersonasViewModel()
        {
            NumeroDePersonas = numPersonas,
            PersonasPorPagina = personasPorPagina,
            Personas = personas
        };
        return View(datos);
    }
}

3. La Vista

La mayor parte de la Vista que implementamos en el post anterior sigue siendo válida, sólo tendremos que retocarle un par de detalles que encontramos en las primeras cuatro líneas.

@model WebGridDemo.ViewModels.PaginaDePersonasViewModel
@{
    ViewBag.Title = "Personas";
    WebGrid grid = new WebGrid(rowsPerPage: Model.PersonasPorPagina);
    grid.Bind(Model.Personas, autoSortAndPage: false, rowCount: Model.NumeroDePersonas);   }

En la primera línea, en la directiva @model debemos definir el tipo de datos recibidos desde el Controlador, que como ya hemos visto, en este caso se trata de una instancia de PaginaDePersonasViewModel que contiene toda la información que necesita la Vista para maquetarse apropiadamente.

A continuación, tras establecer el título de la página, instanciamos el WebGrid suministrándole a su constructor el valor del parámetro rowsPerPage, el número de filas por página que estamos utilizando, que es el mismo usado por el Controlador a la hora de solicitar los datos al Modelo.

Finalmente, invocamos a su método Bind(), donde podemos indicamos:

  • el conjunto de Personas a mostrar en la página actual,
  • que no deseamos utilizar la ordenación y paginación automática, estableciendo autoSortAndPage a false,
  • mediante el parámetro rowCount, el número total de personas almacenadas en la base de datos.

Y afortunadamente, esto es todo lo que necesitamos hacer en la Vista. Por supuesto, no es necesario tocar nada en lo relativo a la definición de columnas, formatos, etc.

4. Y recapitulando…

A lo largo de este post hemos vuelto a recorrer el Modelo, Controlador y Vista que ya implementamos hace unos días, comentando los cambios que tenemos que hacer para conseguir una paginación de datos más eficiente.

Hemos visto distintas alternativas para implementar el Modelo, basadas en Entity Framework. No son las únicas posibilidades ni mucho menos, pero espero al menos que os den ideas para implementar vuestras propias soluciones. También hemos reescrito el Controlador, implementando el consumo de estos servicios del Modelo para obtener justo la información que necesitamos para mostrar una página de datos, y la hemos pasado a la Vista, a la que hemos tenido que hacer sólo ligeras modificaciones.

Finalmente, como el post ha quedado algo extenso, repasamos rápidamente qué es necesario hacer para implementar un WebGrid con una paginación eficiente:

  • En el Modelo:
    • Necesitamos un mecanismo para obtener el número total de elementos.
    • Asimismo, tendremos que crear un método para obtener los elementos pertenecientes a una página concreta, atendiendo a los criterios de ordenación suministrados.
  • En el Controlador:
    • Debemos capturar los parámetros page, sort y sortDir de la petición, a través de los cuales podremos conocer la página a mostrar, la columna y dirección de ordenación, respectivamente.
    • Obtener desde el Modelo el número total de elementos, así como el conjunto de instancias pertenecientes a la página actual de datos, según los criterios de ordenación actuales.
    • Retornar la vista, suministrándole:
      • Los datos a mostrar.
      • El total de elementos.
      • El número de elementos por página que estamos usando
  • En la Vista:
    • Indicar, al instanciar el WebGrid, el número de elementos por página (dato enviado desde el Controlador).
    • Enlazar en WebGrid a la colección suministrada por el Controlador, desactivando la paginación y ordenación automática, e indicándole el total de elementos disponibles.

Y eso es todo. En total, aunque depende de la solución empleada, introducir una paginación eficiente a nuestros WebGrids no debe suponer mucho más de una veintena de líneas, que seguro vale la pena introducir dados los beneficios aportados al rendimiento de nuestras aplicaciones 🙂

image
Como en otras ocasiones, podéis descargar el proyecto desde SkyDrive (requiere VS2010, SQL Express y ASP.NET MVC 3). ¡Que aproveche! 😉

Artículo original: WebGrid en MVC 3, paso a paso (y II) / Variable not found

ASP.NET MVC 3 Tools update

ASP.NET MVCYa me parecía que el equipo de MVC llevaba mucho tiempo en silencio ;-). Acaba de publicarse una actualización para las herramientas de ASP.NET MVC 3 en la que se incluyen algunas novedades interesantes, y a las que ya he podido echar un vistazo. Os comento lo que he encontrado de momento en ellas.

Novedades a primera vista

El primer pequeño cambio que notamos al instalar esta actualización, es que algunos de los cuadros de diálogo y nombres de plantillas que antes aparecían en español ahora aparecen de nuevo en inglés, como antes de instalar el paquete de idioma correspondiente. La plantilla de aplicaciones MVC 3 vuelve a llamarse “MVC 3 Web Application”, y los cuadros de diálogo vuelven su idioma original. Pero bueno, nada grave.

Nuevo cuadro de diálogo "Crear proyecto"

Justo a continuación, ya creando el primer proyecto MVC 3, vemos que en el cuadro de diálogo inicial ha sido introducida una nueva plantilla llamada “Intranet Application”, y aparece un checkbox “use HTML5 semantic markup”.

El uso de esta nueva plantilla generará un proyecto prácticamente idéntico al habitualmente creado utilizando la plantilla “Aplicación de internet”, pero en lugar de utilizar el sistema de membresía de ASP.NET, utilizará autenticación Windows. Por tanto, ni rastro de controlador Account, ni de su Modelo, ni sus vistas; sólo encontraremos instrucciones para activar en IIS 7 e IIS Express este modelo de seguridad.

Por otra parte, marcando el nuevo checkbox podemos hacer que las vistas sean generadas utilizando marcado semántico HTML5. En la práctica, esto sólo implica que en el marcado del Layout encontraremos tags como HEADER, NAV, SECTION, o FOOTER.

Más scripts, y más actualizados

Si nos quejamos de la velocidad de los chicos de MVC a la hora de sacar releases, los de jQuery no se quedan nada atrás… incluso diría que los superan. Por ejemplo, la plantilla de ASP.NET MVC 3 incluía por defecto jQuery 1.4.4, y recientemente ha aparecido la 1.5.2… en tres meses, han aparecido al menos 4 revisiones.

Actualización de jQuery vía NugetLos nuevos proyectos ASP.NET MVC vienen ya de serie con la versión 1.5.1, pero lo que es más interesante, es que ésta se incorpora en el proyecto como un paquete predescargado de Nuget. Por tanto, nada más abrir el proyecto recién creado, o en cualquier momento a partir de entonces, podemos utilizar Nuget para obtener las actualizaciones del mismo.

Lo mismo ocurre con jquery.Validation y con jQuery.UI. En ambos casos se ha incluido la versión 1.8, y de la misma forma, pueden ser actualizados a través de Nuget.

Eso sí, un detalle a tener en cuenta es que la inclusión de jquery desde nuestro código (el <script src..> que solemos encontrar en nuestro Layout o en algunas vistas) debemos modificarla de forma manual, puesto que el nombre de archivo incluye la versión específica, y Nuget no lo actualizará por nosotros.

Otra novedad al respecto es que se incluye de serie la biblioteca Modernizr, una interesante herramienta que nos facilita la detección de características de browsers, permitiéndonos el uso de tecnologías modernas como HTML5 o CSS3, mientras mantenemos la compatibilidad con navegadores anteriores.

Más andamiaje

El cuadro de diálogo de añadir controladores ha sido potenciado considerablemente. En este diálogo, hasta ahora sólo indicábamos el nombre del controlador y si queríamos generar código de acciones para las operaciones CRUD.

Ahora, tras instalar esta actualización, tendremos a nuestra disposición una herramienta mucho más potente y compleja, que nos permite crear controladores, vistas, e incluso clases de contexto de datos para Entity Framework 4.1 (que se instala de serie en nuestros proyectos) con métodos básicos de acceso a datos.

Nuevo cuadro de diálogo para añadir controladores
Disponemos de tres opciones para crear un controlador: vacío, con acciones de edición vacías, y con vistas de lectura y
edición usando Entity Framework.

Las dos primeras son equivalentes a lo que podíamos conseguir anteriormente, un controlador únicamente con la acción Index(), o con las acciones habituales CRUD, pero sin demasiado código en su interior.

La tercera opción, sin embargo, supone un gran avance respecto a las demás, puesto que permite, de un plumazo, generar todo el código que necesitamos para cubrir los habituales escenarios CRUD (consulta, altas, bajas, modificaciones y eliminación) basándose en el nuevo Entity Framework 4.1, que por cierto también acaba de salir del horno.

Obviamente, como es habitual, tendremos que retocar el código generado, pero como base no está nada mal la cantidad de trabajo que nos ahorra.

Por ejemplo, supongamos que creamos un proyecto MVC 3 vacío y añadimos a la carpeta Models una clase como la siguiente:

    public class Persona
    {
        [Key]
        public int IdPersona { get; set; }
 
        [Required]
        [StringLength(50)]
        public string Nombre { get; set; }
 
        [Required]
        [StringLength(50)]
        public string Apellidos { get; set; }
 
        public DateTime FechaNacimiento { get; set; }
    }

Como siempre, seleccionando la opción “Añadir controlador” sobre la carpeta Controllers del proyecto, podemos generar una clase controlador así:

Añadir controlador

Observad el desplegable donde se nos solicita la clase del contexto de datos. En ella podemos seleccionar el contexto de objetos (si usamos los tradicionales db-first o model-first de EF 4) o contexto de datos (usando code-first), desde donde se obtendrá la información. Además, si no existe, podemos incluso generar esta clase (para code-first) de forma automática desde este mismo punto.
A continuación, pulsando “Add”, se habrán generado automáticamente los siguientes elementos:

  • la clase PersonasController, dentro de la carpeta correspondiente, en cuyo interior encontraremos la implementación completa de las acciones Index, Details, Create, Edit y Delete,
  • vistas para cada una de las acciones anteriores, que tienen en cuenta algunas características definidas en el modelo de datos, como las anotaciones o relaciones entre entidades.

De hecho, el código generado es totalmente funcional. Si directamente ejecutamos la solución, gracias a la magia del code-first, tendremos funcionando un mantenimiento completo de la entidad Persona, sin ni siquiera necesidad de crear la base de datos de forma manual:

Mantenimiento generado

Bueno, y esto es todo de momento. Ya conforme vaya descubriendo más novedades o particularidades las iremos viendo por aquí.

Aquí tenéis el enlace para descargar la actualización de herramientas para MVC 3; en el mismo sitio encontraréis las notas de revisión, que describen los cambios introducidos.

Publicado originalmente en: http://www.variablenotfound.com/2011/04/aspnet-mvc-3-tools-update.html