Desarrollando con código primero en Entity Framework 4

Traducción aproximada del artículo Code-First Development with Entity Framework 4 publicado en inglés por Scott Guthrie el 16 de julio del 2010.

 

.NET 4 incluye una versión muy mejorada de Entity Framework: una biblioteca de acceso a datos residente en el espacio de nombres System.Data.Entity.

Luego de ser introducida en .NET 3.5 SP1 recibimos gran cantidad de comentarios sobre aspectos que los desarrolladores consideraron como faltantes o incompletos en Entity Framework. El equipo de SQL hizo un excelente trabajo en escuchar y usar esas reacciones para enfocar sus esfuerzos en satisfacer las inquietudes en .NET 4.

Algunas de las grandes mejoras en EF4 incluyen:

  • Manejo de clases POCO: Ahora se pueden definir entidades sin tener que usar clases base especializadas o atributos de permanencia.
  • Cargado diferido: al cargar un modelo, sus sub-objetos pueden ser cargados según se necesiten en ves todos de un solo.
  • Compatibilidad con múltiples capas y entidades con automantenimiento: útiles para casos en los que la entidades fluyen a través de múltiples capas o llamadas de web sin estado.
  • Mejor calidad de SQL generado y mejor manejo de procedimientos almacenados: EF4 crea SQL más eficiente y es más fácil de integrar con procedimientos almacenados.
  • Generación automática de plurales: EF4 puede generar plurales para tablas (p.ej. Categories->Categoy).
  • Capacidad de pruebas mejorada: Es más fácil ahora simular el contexto de datos de EF4 usando interfases.
  • Mayor compatibilidad con LINQ: EF4 es compatible ahora con todos los operadores de LINQ.

Por otra parte, Visual Studio 2010 incluye mejor manejo de EF y un diseñador mucho más completo. Este diseñador es compatible con el desarrollo partiendo de la base de datos, donde se construye el modelo de datos en el diseñador usando una base de datos ya existente. También da la capacidad de usar un estilo de desarrollo partiendo del modelo, donde primero se define el modelo de datos en el diseñador y la base de datos es generada basándose en el resultado.

 

Desarrollo a partir del código con EF

Además del diseñador, EF4 permite un desarrollo más enfocado en el código al que llamamos “código primero” (o: partiendo del código). Esto permite un flujo de desarrollo muy eficiente:

  • No hay necesidad de usar el diseñador o de definir un mapa en XML.
  • Se puede definir el modelo usando clases tradicionales sin tener que derivar de clases base especiales.
  • Usa el estilo de convención en vez de configuración para proveer almacenaje en la base de datos sin tener que configurar nada de forma explícita.
  • Da la opción de redefinir el almacenaje basado en convenciones usando una API fluida para personalizarlo.

Para usar la modalidad de código primero hay que utilizar un componente descargable de forma separada. El CTP4 de este componente salió esta semana y de puede obtener acá. (Nota: para cuando escribo esta traducción, este componente es parte de el candidato a publicación de EF 4.1 disponible aquí o usando NuGet.)

Se puede usar sin problemas en VS 2010 y es compatible con cualquier tipo de proyecto (incluyendo ASPT.NET Web Forms y MVC).

 

Guía paso a paso: Creando NerdDinner usando el estilo de código primero

El año pasado escribí una guía práctica usando ASP.NET MVC 1.0 que fue publicado en línea y en un libro. La guía ayuda, paso a paso, a crear una aplicación llamada NerdDinner que provee una manera fácil de organizar eventos en línea y llevarlos a cabo. Pueden leer la guía original aquí. El libro Professional ASP.NET MVC 2 también contiene una versión actualizada.

La guía original parte de una base de datos ya definida y usa el diseñador de Visual Studio para crear el modelo de entidades con LINQ a SQL y LINQ a Entidades.

En este caso voy a demostrar el estilo de partir desde el código para crear una aplicación de manejo básico de datos (CRUD) en ASP.NET MVC usando EF4 para crear un modelo de datos y entonces producir la base datos.

 

Aplicación a desarrollar en este ejemplo.

 

La guía explica el procedimiento paso a paso. La aplicación completa está disponible para descargar al final del artículo.

 

Paso 1: Crear una nueva aplicación de ASP.NET MVC 2 vacía

Vamos a empezar creando un proyecto de ASP.NET MVC 2 nuevo en Visual Studio 2010. Yendo a Archivo->Nuevo->Proyecto, pueden usa la plantilla Aplicación vacía de ASP.NET MVC 2.

La plantilla genera una aplicación de ASP.NET MVC 2 sin controladores, modelos o vistas:

Proyecto justo depués de ser creado.

 

A continuación hay que definir el modelo para NerdDinner, es decir, la serie de objetos que representan los datos de nuestra aplicación, junto con las reglas de manejo de datos, la validación, y los algoritmos de proceso. El modelo es el núcleo de una aplicación basada en el patrón MVC y actúa como su fuerza fundamental. Vamos a usar la habilidad de partir del código en EF4 para crear este modelo.

 

Paso 2: crear el modelo

Vamos a suponer que todavía no tenemos una base de datos definida, y que estamos creando NerdDinner por primera vez.

 

No es necesario empezar con una base de datos

Partiendo desde el código, no necesitamos comenzar creando la base de datos o diseñando un esquema de datos. En vez de eso podemos empezar creando clases tradicionales de .NET que definen los objetos del modelo del dominio que sean convenientes para la aplicación sin tener que preocuparnos sobre su almacenamiento.

 

Creando clases del modelo

NerdDinner es una aplicación pequeña requiriendo un sistema de almacenamiento simple. Lo que queremos es poder definir eventos a los que la gente puede asistir (Dinners) . También queremos poder crear y guardar confirmaciones de asistencia (RSVP) y usarlas para medir el interés de las personas en alguna cena en particular.

Empecemos por crear dos clases, Dinner y RSVP, representando estos dos conceptos y añadiéndolas a nuestro proyecto:

 

public class Dinner
{
    public int DinnerID { get; set; }
    public string Title { get; set; }
    public DateTime EventDate { get; set; }
    public string Address { get; set; }
    public string HostedBy { get; set; }

    public virtual ICollection<RSVP> RSVPs { get; set; }
}

 

public class RSVP
{
    public int RsvpID { get; set; }
    public int DinnerID { get; set; }
    public string AttendeeEmail { get; set; }

    public virtual Dinner Dinner { get; set; }
}

 

Estas clases son objetos simples y llanos del CLR (conocidos como POCO por sus siglas en inglés). No es necesario derivarlas de ninguna clase base en especial o que implementen ninguna interfase específica. Además, las propiedades expuestas son tipos estándar de .NET; no se necesita decorarlos con atributos de permanencia.

La capacidad de crear clases del modelo sin tener que asociarlas con alguna base de datos, interfase de programación o esquema de datos en particular nos da gran flexibilidad en la forma de obtener acceso a los datos. Nos permite enfocarnos en el diseño de la aplicación y sus funciones sin preocuparnos sobre cómo implementar el almacenaje de datos. También nos da la libertad de cambiar el esquema de datos o su implementación en un futuro sin tener que reescribir los objetos del modelo o el código con el que interactúan.

 

Creando una contexto de datos para el almacenamiento de datos

Ya con nuestras clases POCO definidas, vamos a crear otra que sirve como medio para guardar y obtener objetos Dinner y RSVP de la base de datos.

Esta clase, a la que llamaremos NerdDinners, deriva de la base DbContext y expone dos propiedades públicas, una para objetos Dinner y otra para los de tipo RSVP:

 

public class NerdDinners : DbContext
{
    public DbSet<Dinner> Dinners { get; set; }
    public DbSet<RSVP> RSVPs { get; set; }
}

 

Los tipos DbContext y DbSet residen en la biblioteca de código primero de EF4. Necesitan agregar una referencia a System.Data.Entity.CTP que se encuentra en Archivos de programa\Microsoft ADO.NET Entity Framework Feature CTP4\Binaries (o System.Data.Entity en el GAC si usan EF 4.1). También necesitan agregar el espacio de nombres al archivo con la declaración using System.Data.Entity.

 

Ese es todo el código que hay que escribir

Las tres clases anteriores forman todo el código necesario para implementa un modelo básico junto con un componente de almacenamiento de datos para nuestra aplicación. No se necesita configurar ningún tipo de correspondencias para una base de datos, ni ejecutar herramientas especiales , ni editar archivos XML, ni usar ningún diseñador para hacer que nuestros objetos puedan ser guardados, actualizados o leídos de una base de datos.

 

Relaciones de permanencia basadas en convenciones

¿Cómo puede ser posible que no haya que editar archivos XML, o escribir código adicional, ni usar una herramienta para crear las relaciones entre el modelo y la base de datos?

En EF, el sistema de código primero viene configurado para usar convenciones en vez de configuración, lo que permite usar prácticas comunes en estructurado de datos en lugar de tener que configurarlo de manera explícita. Las convenciones pueden ser redefinidas si es necesario crear reglas diferentes. Pero si usan las que vienen predeterminadas notarán que hay que escribir poco código y que en el 90% de los casos simplemente funciona de la manera esperada sin tener que escribir ningún tipo de configuración.

En nuestro ejemplo, el contexto NerdDinners crea una correspondencia entre las propiedades Dinners y RSVPs y las tablas con los mismos nombres en la base de datos. Cada registro en la tabla Dinners corresponde a una instancia de la clase Dinner. De igual manera, cada registro en la tabla RSVPs corresponde a una instancia de la clase RSVP. Las propiedades en las clases corresponden a las columnas en las tablas.

Otras convenciones en EF incluyen la identificación automática de claves primarias y externas reconociendo ciertos patrones comunes (por ejemplo: una propiedad llamada ID o DinnerID en la clase Dinner es considerada como la clave primaria). EF también entiende otras convenciones más sofisticadas para crear relaciones entre modelos. El equipo de EF ha publicado un artículo en su blog que explica en mayor detalle las convenciones predeterminadas.

 

Ejemplos de código usando nuestro modelo

Las tres clases que creamos anteriormente contienen todo el código necesario para implementar el modelo y almacenamiento de NerdDinner. Veamos a continuación algunos ejemplos de cómo se pueden usar las clases en casos comunes de manejo de datos.

 

Consultas con expresiones en LINQ

Es posible usar expresiones de consulta en LINQ para obtener datos de la base usando el siguiente código. La expresión obtiene todas las cenas que ocurrirán en el futuro:

 

NerdDinners nerdDinners = new NerdDinners();

// Obtener las cenas que ocurren en el futuro.
var dinners = from dinner in nerdDinners.Dinners
                where dinner.EventDate > DateTime.Now
                select dinner;

 

También podemos aprovechar la relación entre Dinner y RSVP en las expresiones LINQ. En el caso siguiente, la declaración where filtra las cenas dejando solamente las que tienen una o más confirmaciones:

 

NerdDinners nerdDinners = new NerdDinners();

// Obtener solamente cenas con asistentes confirmados
var dinners = from dinner in nerdDinners.Dinners
                where dinner.RSVPs.Count > 0
                select dinner;

 

Es importante notar que el filtro anterior (donde obtenemos solamente cenas con una o más confirmaciones) se ejecuta en el servidor, haciendo la consulta muy eficiente y reduciendo el monto de datos transferidos.

 

Obteniendo una instancia única

Usando el método Single() de LINQ con una expresión lambda es posible obtener una instancia única de Dinner como en el código siguiente:

 

NerdDinners nerdDinners = new NerdDinners();

// Obtener una cena específica
var dinner = nerdDinners.Dinners.Single(d => d.DinnerID == 1);

 

Alternativamente, es posible usar el método Find() provisto por el código primero de EF para encontrar fácilmente una instancia basada en su ID:

 

NerdDinners nerdDinners = new NerdDinners();

// Obtener una cena específica
var dinner = nerdDinners.Dinners.Find(1);

 

Añadiendo una nueva cena

El siguiente código muestra cómo crear y luego agregar una nueva cena a la base de datos. Basta con crear un nuevo objeto Dinner, asignarle valores a las propiedades y luego agregarlo a la propiedad Dinners del contexto NerdDinner. El contexto de datos usa el patrón unidad de trabajo lo que permite añadirle múltiples objetos y luego usa SaveChanges() para almacenar todos los cambios en una transacción atómica.

 

NerdDinners nerdDinners = new NerdDinners();

// Crear y almacenar una nueva cena
var dinner = new Dinner
{
    Title = "Fiesta en la casa de Pedro",
    EventDate = DateTime.Parse("12/31/2010"),
    Address = "Edificio 40",
    HostedBy = "pedro@email.com"
};

nerdDinners.Dinners.Add(dinner);
nerdDinners.SaveChanges();

 

Actualizando una cena

El código abajo muestra como obtener una cena y actualizar una de sus propiedades para luego guardar los cambios de vuelta en la base de datos:

 

NerdDinners nerdDinners = new NerdDinners();

// Actualizando una cena ya existente
var dinner = nerdDinners.Dinners.Find(1);
dinner.Title = "Nuevo nombre";

nerdDinners.SaveChanges();

 

Paso 3: Crear un controlador de ASP.NET MVC que use nuestro modelo

Pasemos a crear un escenario más completo con nuestro modelo, usando una clase controladora que implemente la funcionalidad necesaria para publicar una lista de eventos futuros y que nos permita crear otros nuevos.

Para ello podemos hacer clic derecho en la carpeta Controllers y seleccionar Agregar->Controlador. Vamos a llamarlo HomeController.

Luego le añadimos tres acciones que usen el modelo NerdDinner creado con la modalidad de código inicial de EF:

 

public class HomeController : Controller
{
    NerdDinners nerdDinners = new NerdDinners();

    // GET: /
    public ActionResult Index()
    {
        var dinners = from dinner in nerdDinners.Dinners
                        where dinner.EventDate > DateTime.Now
                        select dinner;

        return View(dinners.ToList());
    }

    // GET: /Home/Create
    public ActionResult Create()
    {
        return View();
    }

    // POST: /Home/Create
    [HttpPost]
    public ActionResult Create(Dinner dinner)
    {
        if (ModelState.IsValid)
        {
            nerdDinners.Dinners.Add(dinner);
            nerdDinners.SaveChanges();

            return RedirectToAction("Index");
        }

        return View(dinner);
    }
}

 

El método de la acción Index obtiene y despliega la lista de eventos por venir.

Los métodos de la acción Create permiten crear nuevos eventos. El primero responde a HTTP GET cuando el usuario visita el URL /Home/Create y produce un formulario para introducir los detalles de la nueva cena. El segundo responde al comando HTTP POST producido por el formulario y guarda el nuevo evento en la base de datos. Si hay un problema de validación entonces el formulario es desplegado de nuevo con los mensajes de error apropiados.

 

Añadiendo vistas a nuestros controladores

El siguiente paso es agregar dos vistas a nuestro proyecto, una para Index y otra para Create.

Para agregar la vista Index al proyecto, colocamos el cursor en el método de la acción Index del controlador y, haciendo clic derecho, escogemos Agregar vista en el menú. Esto produce el cuadro de diálogo Agregar vista, donde especificamos que queremos que sea fuertemente tipada y que le vamos a pasar una lista de tipo IEnumerable conteniendo objetos de tipo Dinner:

 

Opciones en la creación de la vista Index.

 

Al hacer clic en Agregar, Visual Studio genera el archivo Views/Home/Index.aspx. Le vamos a insertar el siguiente código que genera una lista de Dinners en un elemento <ul> y también un vínculo a la acción de crear:

 

<h1>Eventos pendientes:</h1>
<ul>
<% foreach (var dinner in Model) { %>
    <li>
        <%=dinner.Title %> (<%=dinner.EventDate %>)
    </li>
<% } %>
</ul>

<p>
    <%=Html.ActionLink("Crear un nuevo evento", "Create") %>
</p>

 

Luego agregamos la vista Create poniendo el cursor sobre el método de esa acción en el controlador y de nuevo escogemos Agregar vista. En el cuadro de diálogo especificamos una vista fuertemente tipada a la que pasaremos objetos de tipo Dinner. También indicamos que queremos que el contenido de la vista sea basado en una plantilla para crear nuevos objetos:

 

Opciones en la creación de la vista Create.

 

Al hacer clic en Agregar, Visual Studio crea el archivo Views/Home/Create.aspx con el código esqueleto de un formulario para el objeto Dinner. Vamos a modificarlo ligeramente y también eliminar el campo de entrada para la propiedad DinnerID. Nuestro formulario final es el siguiente:

 

<h1>Crear un nuevo evento</h1>
<% using (Html.BeginForm()) {%>
  <%: Html.ValidationSummary(true) %>

  <fieldset>
    <div class="editor-label">
      <%: Html.LabelFor(model => model.Title) %>
    </div>
    <div class="editor-field">
      <%: Html.TextBoxFor(model => model.Title) %>
      <%: Html.ValidationMessageFor(model => model.Title) %>
    </div>

    <div class="editor-label">
      <%: Html.LabelFor(model => model.EventDate) %>
    </div>
    <div class="editor-field">
      <%: Html.TextBoxFor(model => model.EventDate) %>
      <%: Html.ValidationMessageFor(model => model.EventDate) %>
    </div>

    <div class="editor-label">
      <%: Html.LabelFor(model => model.Address) %>
    </div>
    <div class="editor-field">
      <%: Html.TextBoxFor(model => model.Address) %>
      <%: Html.ValidationMessageFor(model => model.Address) %>
    </div>

    <div class="editor-label">
      <%: Html.LabelFor(model => model.HostedBy) %>
    </div>
    <div class="editor-field">
      <%: Html.TextBoxFor(model => model.HostedBy) %>
      <%: Html.ValidationMessageFor(model => model.HostedBy) %>
    </div>

    <p>
      <input type="submit" value="Create" />
    </p>
  </fieldset>

<% } %>

 

Ya tenemos todo el código necesario en las vistas y el controlador para implementar el listado y creación de eventos en nuestra aplicación.

 

Paso 4: la base de datos

Con el código listo, podemos entonces ejecutar la aplicación.

 

¿Y qué pasó con la base de datos?

Todavía no tenemos una; gracias a el desarrollo de código primero no la hemos necesitado para definir y usar nuestro modelo de datos.

Sin embargo, vamos a ocupar una base de datos a la hora de ejecutar nuestra aplicación y querer almacenar los objetos Dinner y RSVP. Podemos crear la base de datos usando uno de los siguiente métodos:

  1. Creando un esquema de datos manualmente usando una herramienta con ese propósito (p. ej. SQL Management Studio o Visual Studio)
  2. Generando el esquema de datos automáticamente usando la biblioteca de código primero de EF

La segunda opción es muy atractiva y es la que vamos a usar para nuestra aplicación NerdDinner.

 

Configurando la cadena de conexión a la base de datos

Para empezar, vamos a crear una cadena de conexión que apunte al lugar en que residirá la base de datos. Esto se logra mediante añadir al archivo web.config de la aplicación una cadena llamada NerdDinners:

 

<connectionStrings>
  <add name="NerdDinners"
       connectionString="Data Source=|DataDirectory|NerdDinners.sdf"
       providerName="System.Data.SqlServiceCe.4.0"/>
</connectionStrings>

 

Clases de tipo DBContext en código primero de EF buscan por defecto, al ser creadas, una cadena de conexión con el mismo nombre que la clase. Como nuestra clase se llama NerdDinners, entonces va a buscar una cadena de conexión llamada “NerdDinners” al ser instanciada en nuestra aplicación.

 

Usando SQL CE 4

Es posible usar varios tipos de bases de datos con EF código primero, incluyendo SQL Server, SQL Server Express y MySQL.

Hace un par de semanas discutí nuestro progreso en hacer disponible la base de datos SQL CE 4 para ser incorporada aplicaciones de ASP.NET. SQL CE 4 es una base de datos liviana, gratuita, basada en archivos, fácil instalar y que puede ser incorporada en aplicaciones de ASP.NET. Es excelente para uso en entornos de hospedaje de web económicos y las bases de datos pueden ser fácilmente trasladadas a SQL Server si eso resulta necesario.

SQL CE es útil en las etapas iniciales cuando todavía se está moldeando la capa del modelo y se ocupa poder recrear la base de datos repetidamente. Igualmente vamos a usarlo mientras desarrollamos NerdDinners, teniendo la opción, más adelante, de usar SQL Express o SQL Server con sólo cambiar la cadena de conexión y sin tener que alterar el código.

La cadena de conexión del ejemplo apunta a un archivo de base de datos llamado NerdDinners.sdf y especifica a SQL CE 4 como el motor. Para que funcione primero hay que instalarlo, ya sea usando el instalador autónomo de CE 4, o como parte de WebMatrix (con el que viene incluido). El instalador es un programa pequeño que no tarda en ser descargado y ejecutado.

Importante: En la cadena de conexión anterior indiqué que el archivo NerDinners.sdf debe ser creando en la carpeta |DataDirectory| que, en una aplicación de ASP.NET, se refiere al directorio App_Data directamente debajo del de la aplicación. La plantilla Aplicación vacía de ASP.NET MVC 2 no la incluye, por lo que hay que crearla manualmente (haciendo clic derecho en el proyecto y escogiendo Agregar->Agregar carpeta de ASP.NET->App_Data).

 

Creación automática del esquema de datos

La biblioteca de código primero de EF puede generar automáticamente el esquema de datos y la base de datos a partir de las clases del modelo, evitándonos esa tediosa tarea.

La función es automática siempre y cuando la cadena conexión apunte a un archivo de base de datos de SQL CE 4 o SQL Express que todavía no exista.

Para verla en acción, podemos usar F5 y ejecutar NerdDinner. Esto iniciará el navegador apuntando al URL raíz (/), mostrando lo siguiente:

 

Al ejecutar por primera vez, EF genera la base de datos automáticamente.

 

El URL / de la aplicación invoca el método Index() que a su vez crea una instancia del contexto de datos NerdDinners para obtener los eventos futuros. Como el archivo NerdDinners.sdf al que de refiere la cadena de conexión todavía no existe, la biblioteca de código primero de EF lo genera de forma automática, usando el contexto de datos para deducir el esquema de datos.

Si desean ver el archivo de datos generado, pueden hacer clic en el ícono de Mostrar todos los archivos, actualizar el explorador de soluciones, y abrir la carpeta App_Data:

 

La base de datos generada puede examinarse en Visual Studio.

 

Pronto saldrá una actualización para Visual Studio 2010 que permite abrir y editar bases de datos de SQL CE 4 usando el explorador de servidores (tal como ya se pude hacer con las bases de datos de SQL Server). Mientras tanto, pueden usar la herramientas provistas por WebMatrix para examinar el contenido del archivo. (MD: Visual Studio SP1 ya está disponible y contiene la funcionalidad descrita.)

Puesto que no establecimos pautas para el almacenamiento de datos en nuestro contexto NerdDinners, EF usó sus reglas predeterminadas para asignar los nombres de las entidades en el esquema de datos. De haber creado reglas personalizadas, EF las habría usado como guía, tratando de satisfacer nuestra preferencia al respecto.

Repasando lo abarcado, estas son las dos clases POCO del modelo y el contexto NerdDinners:

 

public class Dinner
{
    public int DinnerID { get; set; }
    public string Title { get; set; }
    public DateTime EventDate { get; set; }
    public string Address { get; set; }
    public string HostedBy { get; set; }

    public virtual ICollection<RSVP> RSVPs { get; set; }
}

 

public class RSVP
{
    public int RsvpID { get; set; }
    public int DinnerID { get; set; }
    public string AttendeeEmail { get; set; }

    public Dinner Dinner { get; set; }
}

 

public class NerdDinners : DbContext
{
    public DbSet<Dinner> Dinners { get; set; }
    public DbSet<RSVP> RSVPs { get; set; }
}

 

Las tablas creadas durante la generación automática de la base de datos son:

 

Tablas generadas automáticamente por EF.

 

Abajo podemos ver la definición para la tabla Dinners. Los nombres de las columnas corresponden a las propiedades de la clase que definimos. Noten también que DinnerID ha sido designada como la columna identidad y como clave primaria:

 

Definición de la tabla Dinners.

 

Sigue la definición de la tabla RSVPs. De igual manera, las columnas corresponden a las propiedades de la clase y RsvpID es la clave primaria y columna identidad:

 

Definición de la tabla RSVPs.

 

También se ha establecido una relación de claves principal y externa de tipo uno a varios entre las tablas Dinners y RSVPs. La biblioteca de código primero de EF dedujo que esta relación era necesaria usando como base el hecho de que Dinner define una propiedad llamada RSVPs que es de tipo ICollection<RSVP>, y también porque RSVP define una propiedad llamada Dinner.

 

Añadiendo datos a la base

Añadámosle algunos eventos a la base de datos. Para ello hacemos clic en el vínculo Crear un nuevo evento para ir al formulario de creación:

 

Creando un nuevo evento.

 

Al hacer clic en el botón Crear, la cena o evento será guardado en la base de datos. Para crear múltiples eventos repetimos la secuencia varias veces. Cada nuevo evento es almacenado en la base de datos y luego aparecerá en la vista inicial con el listado de eventos futuros:

 

Al ser creados, los eventos pendientes con capturados en el listado principal.

 

Paso 5: alterando el modelo

A medida que la aplicación crece, tendremos que ajustar y reorganizar el modelo. La biblioteca de código primero de EF incluye varias atractivas características que facilitan la coordinación entre estos ajustes y la base de datos.

 

Agregando una propiedad a Dinner

Veamos qué está envuelto en un simple cambio a nuestra clase Dinner; específicamente al agregar una nueva propiedad llamada Country:

 

public class Dinner
{
    public int DinnerID { get; set; }
    public string Title { get; set; }
    public DateTime EventDate { get; set; }
    public string Address { get; set; }
    public string HostedBy { get; set; }
    public string Country { get; set; }

    public virtual ICollection<RSVP> RSVPs { get; set; }
}

 

Con el cambio hecho, ejecutemos la aplicación de nuevo con F5. El resultado es:

 

Al cambiar el modelo, la base de datos queda fuera de sincronía.

 

El error se produce al cambiar la estructura de la clase Dinner, ya que la tabla Dinner en la base de datos deja de reflejar el modelo.

Al generar la base de datos automáticamente, EF también creó una tabla llamada EdmMetadata que lleva cuenta de la estructura de los objetos del modelo que fueron usados al crear el modelo de datos:

 

EF crea un atabla para llevar cuenta de cambios en el modelo.

 

EF se percata de que hemos cambiado el modelo y ahora está fuera de sincronía con la base de datos generada, emitiendo el error.

 

Resincronizando el modelo con la base de datos

Hay varias formas de ponerlos de vuelta en sincronía:

  • Ajustando la base de datos manualmente para que coincida con el modelo
  • Borrando el archivo de la base de datos y ejecutando la aplicación de nuevo para EF la vuelva a generar
  • Configurando EF para que actualice automáticamente la base de datos cuando cambiamos el modelo

Veamos cómo usar esta última opción en NerdDinners.

 

Usando RecreateDatabaseIfModelChanges

El CTP 4 de la biblioteca de código primero para EF incluye una característica muy útil para la etapa de desarrollo que permite recrear la base de datos automáticamente cada vez que se alteran las clases del modelo. Al activarla, EF se percata de cambios en las clases usadas para generar el esquema de datos y lo modifica de manera que se mantenga en sincronía y sin intervención de nuestra parte.

 

ADVERTENCIA: RecreateDatabaseIfModelChanges cambió de nombre a partir del CTP 5 y es ahora llamado DropCreateDatabaseIfModelChanges. Ese es el nombre usado en el resto del artículo.

 

En los inicios del desarrollo de la aplicación es fácil reorganizar el modelo con relativamente poco esfuerzo y esta función es especialmente útil al librarnos de tener que también alterar manualmente el esquema de datos. Aún más con SQL CE, ya que el archivo de la base de datos puede ser eliminado y recreado en menos de un segundo, permitiendo un flujo de desarrollo extremadamente eficiente.

La manera más fácil de activarla es añadiendo la declaración Database.SetInitializer() al controlador del evento Application_Start() en el archivo Global.asax:

 

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    Database.SetInitializer<NerdDinners>(new
      DropCreateDatabaseIfModelChanges<NerdDinners>());

    RegisterRoutes(RouteTable.Routes);
}

 

Así le indicamos a EF que regenere el archivo NerdDinners.sdf cada vez que las clases de nuestro modelo cambien. Si ejecutamos la aplicación de nuevo ya no veremos el mensaje de error anterior. En vez de eso EF crea otra vez la base de datos automáticamente para que coincida con el nuevo modelo:

 

La base de datos fue regenerada automáticamente.

 

Datos iniciales en bases autoregeneradas

Algo que pueden haber notado en la captura de pantalla anterior es que, al ser regenerada la base de datos, perdimos los datos que teníamos. Eso demuestra la naturaleza de DropCreateDatabaseIfModelChanges; su uso no es para entornos de producción donde lo deseable es migrar la información de un esquema de datos a otro. Más bien, su propósito es proveer un medio simple y automático de mantener el esquema de datos al día durante la etapa de desarrollo.

Nota: Estamos trabajando en otro mecanismo para poder ayudar con las tareas de migración de datos en producción cuando el esquema de datos cambia. En nuestra opinión ese es un caso separado del que estoy discutiendo y que está diseñado para las etapas tempranas del desarrollo. El mecanismo de migración todavía no está listo en el CTP 4. (MD: Y tampoco en el RC 1).

Algo que EF provee es la habilidad de insertar datos iniciales en la base de datos cada vez que es recreada. Me parece una excelente opción ya que puedo entonces rápidamente hacer pruebas y reorganizar mi modelo sin tener que lidiar repetidamente con la entrada de datos de prueba.

Los datos de inicialización de especifican mediante una clase NerdDinnersInitializer como la mostrada abajo. En mi caso estoy creando dos eventos o cenas de muestra y agregándolos a la base de datos:

 

public class NerdDinnersInitializer :
             DropCreateDatabaseIfModelChanges<NerdDinners>
{
    protected override void Seed(NerdDinners context)
    {
        var dinners = new List<Dinner>
        {
            new Dinner
                {
                    Title = "Cena #1",
                    EventDate = DateTime.Parse("04/30/2011"),
                    Address = "One Microsoft Way",
                    Country = "EEUU",
                    HostedBy = "scottgu@nomail.com"
                },
            new Dinner
                {
                    Title = "Cena #2",
                    EventDate = DateTime.Parse("12/21/2012"),
                    Address = "Tikal",
                    Country = "Guatemala",
                    HostedBy = "estrellado@imperiomaya.com"
                }
        };

        dinners.ForEach(d => context.Dinners.Add(d));
    }
}

 

Y luego hay que modificar Database.Initializer() en Global.asax para que use NerdDinnersInitializer:

 

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    Database.SetInitializer(new NerdDinnersInitializer());

    RegisterRoutes(RouteTable.Routes);
}

 

De esta forma, cada vez que altere las clases del modelo, la base de datos será regenerada para reflejar los cambios y tendrá incluidos nuestros dos eventos iniciales:

 

Los datos iniciales son insertados automáticamente en la base de datos regenerada.

 

Reorganización fácil

Las funciones anteriores aligeran el trabajo de mejorar y reorganizar el código en la etapa de desarrollo, evitándonos tener que usar herramientas externas o escribir secuencias de comandos para mantener la base de datos al día con posibles cambios en el modelo.

Otra ventaja del uso de tipos verificados por el compilador para las clases del modelo, expresiones LINQ y los datos iniciales de la base, es que las herramientas de reorganización de código de Visual Studio pueden fácilmente aplicar de forma automática cambios a través de todo el código.

 

Paso 6: Añadiendo reglas de validación

Bueno, hemos creado una aplicación para introducción de datos simple y elegante.

Sin embargo, un problema es que, por el momento, no tiene ningún mecanismo para verificar que los datos provistos por el usuario son apropiados para cada campo en el formulario de creación de eventos. Es algo que tenemos que arreglar.

 

Usando DataAnnotations para validaciones

El mejor lugar para las reglas de validación en una aplicación ASP.NET MVC es el modelo. Así se las puede mantener en un sólo lugar y puede aplicarlas a través de todos los controladores y vistas pertinentes. ASP.NET MVC tiene varios mecanismos disponibles para la implementación de reglas de validación y es lo suficientemente flexible para soportar prácticamente cualquier sistema de validación imaginable.

ASP.NET MVC 2 incluye la biblioteca de reglas de validación System.ComponenModel.DataAnnotations que permiten declarar las normas como atributos en las clases del modelo. Pueden obtener más detalles sobre este mecanismo en un artículo que publiqué anteriormente. Vamos a usar este sistema para implementar la validación de datos en NerdDinner.

Volviendo a la clase Dinner, le vamos a añadir atributos de validación a las propiedades (nota: recuerden agregar el espacio de nombres System.ComponentModel.DataAnnotations):

 

public class Dinner
{
  public int DinnerID { get; set; }

  [Required(
   ErrorMessage = "Por favor especifique un título")]
  [StringLength(20,
   ErrorMessage = "El título es demasiado largo")]
  public string Title { get; set; }

  [Required(
   ErrorMessage = "Por favor especifique la fecha")]
  public DateTime EventDate { get; set; }

  [Required(
   ErrorMessage =  "Por favor especifique la ubicación")]
  [StringLength(30,
   ErrorMessage = "La difrección es demasiado larga")]
  public string Address { get; set; }

  [Required(
   ErrorMessage = "Por favor especifique una dirección")]
  [RegularExpression(".+\\@.+\\..+",
   ErrorMessage = "La dirección no parece válida")]
  public string HostedBy { get; set; }

  public string Country { get; set; }

  public virtual ICollection<RSVP> RSVPs { get; set; }
}

 

El atributo [Required] indica la propiedad no puede quedar sin contenido. El atributo [StringLength] nos permite indicar la extensión máxima permitida para una cadena de caracteres. El atributo [RegularExpression] nos permite definir reglas de formato y caracteres permitidos en una cadena, como en el caso de la dirección de correo electrónico.

 

Un poco de CSS y JavaScript

El último paso es visitar de nuevo Create.aspx para agregarle una referencia al archivo Site.css de nuestro proyecto y a dos archivos de JavaScript. También hay que añadir una línea que llame a Html.EnableClientValidation() antes del formulario:

 

<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
  <title>Crear evento</title>
  <link href="/Content/Site.css" rel="Stylesheet"
        type="text/css" />
  <script src="/Scripts/MicrosoftAjax.js"
          type="text/javascript"></script>
  <script src="/Scripts/MicrosoftMvcValidation.js"
          type="text/javascript"></script>
</head>
<body>
  <% Html.EnableClientValidation(); %>

 

Los cambios sirven para que cualquier mensaje de validación sea presentado en un estilo que lo haga más visible y para que las reglas de validación sean ejecutadas en el cliente y el servidor.

 

Ejecutando la aplicación

Iniciemos de nuevo la aplicación para crear un nuevo evento. Como primer paso, vamos a presionar el botón Crear sin digitar en ninguno de los campos. Como verán, el formulario presenta los errores de validación según se especificó en el modelo:

 

Mensajes de error para campos requeridos.

 

Como activamos la validación en el cliente (la línea de código en Create.aspx), los mensajes son modificados a medida que arreglamos los errores:

 

Las reglas de validación son ejecutadas también en el cliente.

 

Noten cómo el mensaje cambió una vez que el título sobrepasó los 20 caracteres especificados como el máximo para esta propiedad con [StringLength]. Por otra parte, el menaje para HostedBy cambió al ser satisfecho el atributo [Required] (que señala a la dirección de correo electrónico como obligatoria), y entrar en efecto el [RegularExrpession] (que verifica que la dirección tenga el formato correcto).

Estas reglas de validación funcionan tanto en el navegador (usando JavaScript), como en servidor (ayudando a protegernos aún cuando alguien trate de esquivar la validación con JavaScript) sin tener que hacer cambios en el controlador. Esta capacidad de poder especificar reglas de validación en el modelo y que sean aplicadas en todos lados es extremadamente poderosa, permitiéndonos también evolucionar la aplicación de forma más nítida.

Pueden obtener más detalles sobre los principios y la funcionalidad de validación del modelo en ASP.NET MVC 2 aquí.

 

Descargas

Pueden descargar aquí la solución de ejemplo resultante del artículo. Necesitan tener Visual Studio 2010 (o su versión gratuita de Visual Web Developer 2010 Express).

Importante: Para poder ejecutar el ejemplo tienen que ya haber instalado SQL CE 4. Pueden obtener acá la biblioteca de código primero de EF. Ninguna de estas descargas tendrá impacto negativo en sus máquinas.

El proyecto de muestra incluye el paquete de NuGet para el RC 1 de EF 4.1 por lo que no es absolutamente necesario descargar e instalar la biblioteca para ejecutar el ejemplo.

 

Conclusión

El CTP 4 de la biblioteca de código primero de EF salido esta semana provee una atractiva alternativa para modelar los datos partiendo desde el código. Trae consigo grandes ventajas en términos de productividad y flexibilidad. Mi enfoque principal en esta guía fue en ciertas características para mayor productividad provistas por el CTP 4, pero hay muchos otros aspectos dignos de atención como la interfase de programación fluida que permite crear reglas personalizadas para el esquema de datos, las mejoras en compatibilidad para pruebas, y otras funciones más avanzadas.

Pueden obtener acá el CTP 4 de la biblioteca de código primero de EF. (O el RC1 aquí). Para obtener aún más información sobre código primero en EF asegúrense de consultar estos artículos por el equipo de ADO.NET:

 

 

Espero haberles sido de ayuda,

 

Scott

 

3 Respuestas a “Desarrollando con código primero en Entity Framework 4”

  1. [...] biblioteca de código primero de Entity Framework permite, de forma sencilla, habilitar clases simples del CLR para que obtengan acceso a datos. Como [...]

  2. Juan Manuel dice:

    Hola como estas, me llamo juan y soy estudiante de programacion, y tengo una consulta, si tengo una clase cliente, banco, cuenta, deposito, Extraccion, como hago para hacer la merma del monto de la cuenta al realizar una extraccion? no se bien como usar razor para hacer eso. espero me des una mano. muchas gracias

Responder



Licencia de uso

El contenido de las traducciones está sujeto a los términos de protección de derechos de uso de los autores originales quienes han autorizado su publicación en este blog. Asegúrese de entender los terminos de la licencia de cada autor antes de usar tal contenido.

Mis propios artículos son publicados bajo los términos de la Licencia Reconocimiento-Compartir bajo la misma licencia 3.0 Estados Unidos de Creative Commons:

Creative Commons License
Blog de Maromas Digitales by Maromas Digitales, LLC is licensed under a Creative Commons Reconocimiento-Compartir bajo la misma licencia 3.0 Estados Unidos License.

License

The contents of all translated articles is subject to the copyright and licensing terms of the original authors and has been published here with their express permission. Verify the original author's licensing terms before using the contents of these articles.

My own articles are licensed under the Creative Commons Attribution-Share Alike 3.0 United States License:

Creative Commons License
Blog de Maromas Digitales by Maromas Digitales, LLC is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.