Compartir a través de


Creación de un modelo de datos de Entity Framework para una aplicación ASP.NET MVC (1 de 10)

Por Tom Dykstra

Nota:

Hay disponible una versión más reciente de esta serie de tutoriales para Visual Studio 2013, Entity Framework 6 y MVC 5.

En la aplicación web de ejemplo Contoso University se muestra cómo crear aplicaciones ASP.NET MVC 4 con Code First de Entity Framework 5 y Visual Studio 2012. La aplicación de ejemplo es un sitio web de una universidad ficticia, Contoso University. Incluye funciones como la admisión de estudiantes, la creación de cursos y asignaciones de instructores. En esta serie de tutoriales se explica cómo compilar la aplicación de ejemplo Contoso University.

Code First

Hay tres maneras de trabajar con datos en Entity Framework: Database First, Model First y Code First. Este tutorial es para Code First. Para obtener información sobre las diferencias entre estos flujos de trabajo e instrucciones sobre cómo elegir el mejor para cada escenario, vea Flujos de trabajo de desarrollo de Entity Framework.

MVC

La aplicación de ejemplo se basa en ASP.NET MVC. Si prefiere trabajar con el modelo ASP.NET Web Forms, vea la serie de tutoriales Enlace de modelos y web Forms y Mapa de contenido de acceso a datos de ASP.NET.

Versiones de software

Se muestra en el tutorial También funciona con
Windows 8 Windows 7
Visual Studio 2012 Visual Studio 2012 Express para Web. El SDK de Windows Azure lo instala automáticamente si aún no tiene VS 2012 o VS 2012 Express para Web. Visual Studio 2013 debería funcionar, pero no se probado con el tutorial y algunas selecciones de menú y cuadros de diálogo son diferentes. Se necesita ja versión VS 2013 del SDK de Windows Azure para la implementación de Windows Azure.
.NET 4.5 La mayoría de las características que se muestran funcionarán en .NET 4, pero algunas no. Por ejemplo, para la compatibilidad con enumeraciones en EF se necesita .NET 4.5.
Entity Framework 5
SDK 2.1 de Windows Azure Si omite los pasos de implementación de Windows Azure, no necesita el SDK. Cuando se publique una nueva versión del SDK, el vínculo instalará la versión más reciente. En ese caso, es posible que tenga que adaptar algunas de las instrucciones a las nuevas características y la interfaz de usuario.

Preguntas

Si tiene preguntas que no están directamente relacionadas con el tutorial, puede publicarlas en el foro de Entity Framework de ASP.NET, el foro de Entity Framework y LINQ to Entities o en StackOverflow.com.

Agradecimientos

En el último tutorial de la serie puede ver los agradecimientos y una nota sobre VB.

La aplicación web Contoso University

La aplicación que se va a compilar en estos tutoriales es un sitio web sencillo de una universidad.

Los usuarios pueden ver y actualizar la información de estudiantes, cursos e instructores. A continuación se muestran algunas de las pantallas que se van a crear.

Students_Index_page

Screenshots that show the sample Contoso University web application's Students search page and Create New Student page.

El estilo de la interfaz de usuario de este sitio se ha mantenido fiel a lo que generan las plantillas integradas, para que el tutorial se pueda centrar principalmente en cómo usar Entity Framework.

Requisitos previos

En las instrucciones y capturas de pantalla de este tutorial se supone que usa Visual Studio 2012 o Visual Studio 2012 Express para Web, con la actualización más reciente y el SDK de Azure para .NET instalados a partir de julio de 2013. Puede obtener todo esto con el siguiente vínculo:

SDK de Azure para .NET (Visual Studio 2012)

Si tiene Instalado Visual Studio, el vínculo anterior instalará los componentes que faltan. Si no tiene Visual Studio, el vínculo instalará Visual Studio 2012 Express para Web. Puede usar Visual Studio 2013, pero algunos de los procedimientos y pantallas necesarios variarán.

Creación de una aplicación web MVC

Abra Visual Studio y cree un proyecto de C# denominado "ContosoUniversity" mediante la plantilla Aplicación web ASP.NET MVC 4. Asegúrese de seleccionar como destino .NET Framework 4.5 (usará las propiedades enum, para lo que se necesita .NET 4.5).

New_project_dialog_box

En el cuadro de diálogo Nuevo proyecto de ASP.NET MVC 4, seleccione la plantilla Aplicación de Internet.

Deje seleccionado el motor de vistas Razor y deje desactivada la casilla Crear un proyecto de prueba unitaria.

Haga clic en OK.

Project_template_options

Configuración del estilo del sitio

Con algunos cambios sencillos se configura el menú del sitio, el diseño y la página principal.

Abra el archivo Views/Shared/_Layout.cshtml y reemplace el contenido por el código siguiente. Los cambios aparecen resaltados.

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <title>@ViewBag.Title - Contoso University</title>
        <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
        <meta name="viewport" content="width=device-width" />
        @Styles.Render("~/Content/css")
        @Scripts.Render("~/bundles/modernizr")
    </head>
    <body>
        <header>
            <div class="content-wrapper">
                <div class="float-left">
                    <p class="site-title">@Html.ActionLink("Contoso University", "Index", "Home")</p>
                </div>
                <div class="float-right">
                    <section id="login">
                        @Html.Partial("_LoginPartial")
                    </section>
                    <nav>
                        <ul id="menu">
                            <li>@Html.ActionLink("Home", "Index", "Home")</li>
                            <li>@Html.ActionLink("About", "About", "Home")</li>
                            <li>@Html.ActionLink("Students", "Index", "Student")</li>
                            <li>@Html.ActionLink("Courses", "Index", "Course")</li>
                            <li>@Html.ActionLink("Instructors", "Index", "Instructor")</li>
                            <li>@Html.ActionLink("Departments", "Index", "Department")</li>
                        </ul>
                    </nav>
                </div>
            </div>
        </header>
        <div id="body">
            @RenderSection("featured", required: false)
            <section class="content-wrapper main-content clear-fix">
                @RenderBody()
            </section>
        </div>
        <footer>
            <div class="content-wrapper">
                <div class="float-left">
                    <p>&copy; @DateTime.Now.Year - Contoso University</p>
                </div>
            </div>
        </footer>

        @Scripts.Render("~/bundles/jquery")
        @RenderSection("scripts", required: false)
    </body>
</html>

Este código realiza los cambios siguientes:

  • Reemplaza las instancias de plantilla de "Mi aplicación ASP.NET MVC" y "el logotipo aquí" por "Contoso University".
  • Agrega varios vínculos de acción que se usarán más adelante en el tutorial.

En Views\Home\Index.cshtml, reemplace el contenido del archivo por el código siguiente para eliminar los párrafos de plantilla sobre ASP.NET y MVC:

@{
    ViewBag.Title = "Home Page";
}
@section featured {
    <section class="featured">
        <div class="content-wrapper">
            <hgroup class="title">
                <h1>@ViewBag.Title.</h1>
                <h2>@ViewBag.Message</h2>
            </hgroup>
        </div>
    </section>
}

En Controllers\HomeController.cs, cambie el valor de ViewBag.Message en el método de acción Index por "Bienvenido a Contoso University", como se muestra en el ejemplo siguiente:

public ActionResult Index()
{
    ViewBag.Message = "Welcome to Contoso University";

    return View();
}

Presione CTRL+F5 para ejecutar el sitio. Verá la página principal con el menú principal.

Contoso_University_home_page

Creación del modelo de datos

A continuación podrá crear las clases de entidad para la aplicación Contoso University. Empezará por las tres entidades siguientes:

Class_diagram

Hay una relación uno a varios entre las entidades Student y Enrollment, y también entre las entidades Course y Enrollment. En otras palabras, un estudiante se puede inscribir en cualquier número de cursos y un curso puede tener cualquier número de alumnos inscritos.

En las secciones siguientes creará una clase para cada una de estas entidades.

Nota:

Si intenta compilar el proyecto antes de terminar de crear todas estas clases de entidad, obtendrá errores del compilador.

Entidad Student

Student_entity

En la carpeta Models, cree Student.cs y reemplace el código existente con el siguiente:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int StudentID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }
        
        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}

La propiedad StudentID se convertirá en la columna de clave principal de la tabla de base de datos que corresponde a esta clase. De manera predeterminada, Entity Framework interpreta como la clave principal una propiedad que se denomine ID o nombre_de_claseID.

La propiedad Enrollments es una propiedad de navegación. Las propiedades de navegación contienen otras entidades relacionadas con esta entidad. En este caso, la propiedad Enrollments de una entidad Student contendrá todas las entidades Enrollment que estén relacionadas con esa entidad Student. Es decir, si una fila Student determinada en la base de datos tiene dos filas Enrollment relacionadas (filas que contienen el valor de la clave principal de ese alumno en la columna de clave externa StudentID), la propiedad de navegación Enrollments de esa entidad Student contendrá esas dos entidades Enrollment.

Normalmente, las propiedades de navegación se definen como virtual para que puedan aprovechar ciertas funcionalidades de Entity Framework, como la de carga diferida. (La carga diferida se explicará más adelante, en el tutorial Lectura de datos relacionados más adelante en esta serie).

Si una propiedad de navegación puede contener varias entidades (como en las relaciones de varios a varios o uno a varios), su tipo debe ser una lista a la que se puedan agregar las entradas, eliminarlas y actualizarlas, como ICollection.

Entidad Enrollment

Enrollment_entity

En la carpeta Models, cree Enrollment.cs y reemplace el código existente con el código siguiente:

namespace ContosoUniversity.Models
{
    public enum Grade
    {
        A, B, C, D, F
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }
        
        public virtual Course Course { get; set; }
        public virtual Student Student { get; set; }
    }
}

La propiedad Grade es una enumeración. El signo de interrogación después de la declaración de tipo Grade indica que la propiedad Gradeacepta valores NULL. Una calificación que sea null es diferente de una calificación que sea cero; null significa que no se conoce una calificación o que todavía no se ha asignado.

La propiedad StudentID es una clave externa y la propiedad de navegación correspondiente es Student. Una entidad Enrollment está asociada con una entidad Student, por lo que la propiedad solo puede contener un única entidad Student (a diferencia de la propiedad de navegación Student.Enrollments que se vio anteriormente, que puede contener varias entidades Enrollment).

La propiedad CourseID es una clave externa y la propiedad de navegación correspondiente es Course. Una entidad Enrollment está asociada con una entidad Course.

La entidad Course

Course_entity

En la carpeta Models, cree Course.cs y reemplace el código de plantilla por el siguiente:

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;

namespace ContosoUniversity.Models
{
    public class Course
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }
        
        public virtual ICollection<Enrollment> Enrollments { get; set; }
    }
}

La propiedad Enrollments es una propiedad de navegación. Una entidad Course puede estar relacionada con cualquier número de entidades Enrollment.

En el siguiente tutorial se describirá con más detalle el atributo [DatabaseGenerated(DatabaseGeneratedOption.None)]. Básicamente, este atributo permite escribir la clave principal para el curso en lugar de hacer que la base de datos lo genere.

Crear el contexto de base de datos

La clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos determinado es la clase de contexto de base de datos. Para crear esta clase, derive de la clase System.Data.Entity.DbContext. En el código se especifica qué entidades se incluyen en el modelo de datos. También se puede personalizar determinado comportamiento de Entity Framework. En este proyecto, la clase se denomina SchoolContext.

Cree una carpeta denominada DAL (para Capa de acceso a datos). En esa carpeta, cree un archivo de clase denominado SchoolContext.cs y reemplace el código de plantilla con el siguiente:

using ContosoUniversity.Models;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace ContosoUniversity.DAL
{
    public class SchoolContext : DbContext
    {
        public DbSet<Student> Students { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Course> Courses { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }
}

Este código crea una propiedad DbSet para cada conjunto de entidades. En la terminología de Entity Framework, un conjunto de entidades se suele corresponder con una tabla de base de datos, mientras que una entidad lo hace con una fila de la tabla.

La instrucción modelBuilder.Conventions.Remove del método OnModelCreating impide que los nombres de tabla se pluralicen. Si no lo hiciera, las tablas generadas en la base de datos se denominarían Students, Courses y Enrollments. En su lugar, los nombres de tabla serán Student, Course y Enrollment. Los desarrolladores están en desacuerdo sobre si los nombres de tabla deben ser plurales o no. En este tutorial se usa el formato singular, pero lo importante es que puede seleccionar el formato que prefiera mediante la inclusión u omisión de esta línea de código.

SQL Server Express LocalDB

LocalDB es una versión ligera del Motor de base de datos de SQL Server Express que se inicia a petición y se ejecuta en modo de usuario. LocalDB se ejecuta en un modo de ejecución especial de SQL Server Express que permite trabajar con bases de datos como archivos .mdf. Normalmente, los archivos de base de datos LocalDB se conservan en la carpeta App_Data de un proyecto web. La característica de instancia de usuario de SQL Server Express también le permite trabajar con archivos .mdf, pero la característica de instancia de usuario está en desuso; por tanto, se recomienda LocalDB para trabajar con archivos .mdf.

Normalmente, SQL Server Express no se usa para aplicaciones web en producción. LocalDB en particular no se recomienda para su uso en producción con una aplicación web porque no está diseñado para trabajar con IIS.

En Visual Studio 2012 y versiones posteriores, LocalDB se instala de forma predeterminada con Visual Studio. En Visual Studio 2010 y versiones anteriores, SQL Server Express (sin LocalDB) se instala de forma predeterminada con Visual Studio; tiene que instalarlo manualmente si usa Visual Studio 2010.

En este tutorial trabajará con LocalDB para que la base de datos se pueda almacenar en la carpeta App_Data como un archivo .mdf. Abra el archivo Web.config raíz y agregue una nueva cadena de conexión a la colección connectionStrings, como se muestra en el ejemplo siguiente. (Asegúrese de actualizar el archivo Web.config en la carpeta del proyecto raíz. También hay un archivo Web.config en la subcarpeta Views que no es necesario actualizar).

<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=ContosoUniversity;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\ContosoUniversity.mdf" providerName="System.Data.SqlClient" />

De manera predeterminada, Entity Framework busca un cadena de conexión que tenga el mismo nombre que la clase DbContext (SchoolContext para este proyecto). La cadena de conexión que ha agregado especifica una base de datos LocalDB denominada ContosoUniversity.mdf ubicada en la carpeta App_Data. Para más información, vea Cadenas de conexión de SQL Server para aplicaciones web ASP.NET.

Realmente no es necesario especificar la cadena de conexión. Si no proporciona una cadena de conexión, Entity Framework creará una automáticamente; pero, es posible que la base de datos no esté en la carpeta App_data de la aplicación. Para obtener información sobre dónde se creará la base de datos, vea Code First en una base de datos nueva.

La colección connectionStrings también tiene una cadena de conexión denominada DefaultConnection que se usa para la base de datos de pertenencia. En este tutorial no va a usar la base de datos de pertenencia. La única diferencia entre las dos cadenas de conexión es el nombre de la base de datos y el valor del atributo name.

Configuración y ejecución de una migración de Code First

Al empezar a desarrollar una aplicación, el modelo de datos cambia con frecuencia y, cada vez que lo hace, se deja de sincronizar con la base de datos. Puede configurar Entity Framework para quitar y volver a crear automáticamente la base de datos cada vez que cambie el modelo de datos. Esto no es un problema en la fase inicial del desarrollo porque los datos de prueba se vuelven a crear fácilmente, pero en producción normalmente querrá actualizar el esquema de la base de datos sin quitar la base de datos. La característica Migraciones permite a Code First actualizar la base de datos sin quitarla ni volver a crearla. Al principio del ciclo de desarrollo de un proyecto nuevo, es posible que quiera usar DropCreateDatabaseIfModelChanges para quitar, volver a crear y volver a inicializar la base de datos cada vez que cambie el modelo. Cuando esté listo para implementar la aplicación, puede realizar la conversión al enfoque de migraciones. En este tutorial, solo usará migraciones. Para más información, vea Migraciones de Code First y Migrations Screencast Series.

Habilitación de migraciones de Code First

  1. En el menú Tools (Herramientas), haga clic en Administrador de paquetes NuGet y luego en Consola del Administrador de paquetes.

    Selecting_Package_Manager_Console

  2. En el símbolo del sistema PM>, escriba el comando siguiente:

    enable-migrations -contexttypename SchoolContext
    

    enable-migrations command

    Este comando crea una carpeta Migrations en el proyecto ContosoUniversity, en la que coloca un archivo Configuration.cs que puede editar para configurar Migraciones.

    Migrations folder

    La clase Configuration incluye un método Seed que se llama cuando se crea la base de datos y cada vez que se actualiza después de un cambio del modelo de datos.

    internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.Models.SchoolContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }
    
        protected override void Seed(ContosoUniversity.Models.SchoolContext context)
        {
            //  This method will be called after migrating to the latest version.
    
            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }
    

    El propósito de este método Seed es permitirle insertar o actualizar datos de prueba después de que Code First cree o actualice la base de datos.

Configuración del método Seed

El método Seed se ejecuta cuando Migraciones de Code First crea la base de datos y cada vez que actualiza la base de datos a la migración más reciente. El propósito del método Seed es permitirle insertar datos en las tablas antes de que la aplicación acceda a la base de datos por primera vez.

En versiones anteriores de Code First antes de que se publicara Migraciones, era habitual que los métodos Seed insertaran datos de prueba, ya que, con cada cambio del modelo durante el desarrollo, la base de datos tenía que eliminarse y volver a crearse desde cero. Con Migraciones de Code First, los datos de prueba se conservan tras los cambios en la base de datos, por lo que incluir datos de prueba en el método Seed no suele ser necesario. De hecho, no querrá que el método Seed inserte datos de prueba si va a usar Migraciones para implementar la base de datos en producción, porque el método Seed se ejecutará en producción. En ese caso, querrá que el método Seed inserte en la base de datos solo los datos que quiera que se inserten en producción. Por ejemplo, es posible que quiera que la base de datos incluya los nombres reales de los departamentos en la tabla Department cuando la aplicación esté disponible en producción.

En este tutorial, usará Migraciones para la implementación, pero el método Seed insertará datos de prueba de todos modos para que sea más fácil ver cómo funciona la funcionalidad de la aplicación sin tener que insertar manualmente un montón de datos.

  1. Reemplace el contenido del archivo Configuration.cs por el código siguiente, que carga datos de prueba en la nueva base de datos.

    namespace ContosoUniversity.Migrations
    {
       using System;
       using System.Collections.Generic;
       using System.Data.Entity.Migrations;
       using System.Linq;
       using ContosoUniversity.Models;
    
       internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.DAL.SchoolContext>
       {
          public Configuration()
          {
             AutomaticMigrationsEnabled = false;
          }
    
          protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
          {
             var students = new List<Student>
                {
                    new Student { FirstMidName = "Carson",   LastName = "Alexander", 
                        EnrollmentDate = DateTime.Parse("2010-09-01") },
                    new Student { FirstMidName = "Meredith", LastName = "Alonso",    
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Arturo",   LastName = "Anand",     
                        EnrollmentDate = DateTime.Parse("2013-09-01") },
                    new Student { FirstMidName = "Gytis",    LastName = "Barzdukas", 
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Yan",      LastName = "Li",        
                        EnrollmentDate = DateTime.Parse("2012-09-01") },
                    new Student { FirstMidName = "Peggy",    LastName = "Justice",   
                        EnrollmentDate = DateTime.Parse("2011-09-01") },
                    new Student { FirstMidName = "Laura",    LastName = "Norman",    
                        EnrollmentDate = DateTime.Parse("2013-09-01") },
                    new Student { FirstMidName = "Nino",     LastName = "Olivetto",  
                        EnrollmentDate = DateTime.Parse("2005-08-11") }
                };
             students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
             context.SaveChanges();
    
             var courses = new List<Course>
                {
                    new Course {CourseID = 1050, Title = "Chemistry",      Credits = 3, },
                    new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3, },
                    new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3, },
                    new Course {CourseID = 1045, Title = "Calculus",       Credits = 4, },
                    new Course {CourseID = 3141, Title = "Trigonometry",   Credits = 4, },
                    new Course {CourseID = 2021, Title = "Composition",    Credits = 3, },
                    new Course {CourseID = 2042, Title = "Literature",     Credits = 4, }
                };
             courses.ForEach(s => context.Courses.AddOrUpdate(p => p.Title, s));
             context.SaveChanges();
    
             var enrollments = new List<Enrollment>
                {
                    new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID, 
                        CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID, 
                        Grade = Grade.A 
                    },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
                        CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID, 
                        Grade = Grade.C 
                     },                            
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Alexander").StudentID,
                        CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID, 
                        Grade = Grade.B
                     },
                     new Enrollment { 
                         StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment { 
                         StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment {
                        StudentID = students.Single(s => s.LastName == "Alonso").StudentID,
                        CourseID = courses.Single(c => c.Title == "Composition" ).CourseID, 
                        Grade = Grade.B 
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Anand").StudentID,
                        CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Anand").StudentID,
                        CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
                        Grade = Grade.B         
                     },
                    new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Barzdukas").StudentID,
                        CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
                        Grade = Grade.B         
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Li").StudentID,
                        CourseID = courses.Single(c => c.Title == "Composition").CourseID,
                        Grade = Grade.B         
                     },
                     new Enrollment { 
                        StudentID = students.Single(s => s.LastName == "Justice").StudentID,
                        CourseID = courses.Single(c => c.Title == "Literature").CourseID,
                        Grade = Grade.B         
                     }
                };
    
             foreach (Enrollment e in enrollments)
             {
                var enrollmentInDataBase = context.Enrollments.Where(
                    s =>
                         s.Student.StudentID == e.StudentID &&
                         s.Course.CourseID == e.CourseID).SingleOrDefault();
                if (enrollmentInDataBase == null)
                {
                   context.Enrollments.Add(e);
                }
             }
             context.SaveChanges();
          }
       }
    }
    

    El método Seed toma el objeto de contexto de base de datos como parámetro de entrada y el código del método usa ese objeto para agregar nuevas entidades a la base de datos. Para cada tipo de entidad, el código crea una colección de entidades nuevas, las agrega a la propiedad DbSet adecuada y, después, guarda los cambios en la base de datos. No es necesario llamar al método SaveChanges después de cada grupo de entidades, como se hace aquí, pero hacerlo le ayuda a localizar el origen de un problema si se inicia una excepción mientras el código escribe en la base de datos.

    Algunas de las instrucciones que insertan datos usan el método AddOrUpdate para realizar una operación "upsert". Como el método Seed se ejecuta con cada migración, no puede insertar datos sin más, porque las filas que intenta agregar ya estarán ahí después de la primera migración que crea la base de datos. La operación "upsert" evita los errores que se producirían si intentara insertar una fila que ya existe, pero invalida cualquier cambio en los datos que haya podido realizar mientras probaba la aplicación. Con los datos de prueba de algunas tablas es posible que no quiera que eso ocurra: en algunos casos, cuando cambia los datos durante las pruebas, quiere que permanezcan tras las actualizaciones de la base de datos. En ese caso, querrá realizar una operación de inserción condicional: insertar una fila solo si aún no existe. El método Seed usa ambos enfoques.

    El primer parámetro que se pasa al método AddOrUpdate especifica la propiedad que se usará para comprobar si ya existe una fila. Para los datos de alumnos de prueba que proporciona, se puede usar la propiedad LastName para este fin, ya que cada apellido de la lista es único:

    context.Students.AddOrUpdate(p => p.LastName, s)
    

    En este código se asume que los apellidos son únicos. Si agrega manualmente un alumno con un apellido duplicado, se iniciará la siguiente excepción la próxima vez que realice una migración.

    La secuencia contiene más de un elemento

    Para más información sobre el método AddOrUpdate, vea Cuidado con el método AddOrUpdate de EF 4.3 en el blog de Julie Lerman.

    El código que agrega entidades Enrollment no usa el método AddOrUpdate. Comprueba si ya existe una entidad e inserta la entidad si no existe. Este enfoque conservará los cambios que realice en una calificación de inscripción al ejecutar las migraciones. El código recorre en bucle cada uno de los miembros de EnrollmentList y, si la inscripción no se encuentra en la base de datos, la agrega a la misma. La primera vez que actualice la base de datos, la base de datos estará vacía, por lo que agregará cada inscripción.

    foreach (Enrollment e in enrollments)
    {
        var enrollmentInDataBase = context.Enrollments.Where(
            s => s.Student.StudentID == e.Student.StudentID &&
                 s.Course.CourseID == e.Course.CourseID).SingleOrDefault();
        if (enrollmentInDataBase == null)
        {
            context.Enrollments.Add(e);
        }
    }
    

    Para obtener información sobre cómo depurar el método Seed y controlar datos redundantes como, por ejemplo, dos alumnos llamados "Alexander Carson", vea Inicialización y depuración de bases de datos de Entity Framework (EF) en el blog de Rick Anderson.

  2. Compile el proyecto.

Creación y ejecución de la primera migración

  1. En la ventana Consola del Administrador de paquetes, escriba los comandos siguientes:

    add-migration InitialCreate
    update-database
    

    Screenshot that shows the Package Manager Console window. The commands add hyphen migration underscore Initial Create and update hyphen database are highlighted.

    El comando add-migration agrega a la carpeta Migrations un archivo [Marca_De_Tiempo]_InitialCreate.cs que contiene código que crea la base de datos. El primer parámetro (InitialCreate) en el ejemplo) se usa para el nombre del archivo y puede ser lo que quiera; normalmente se elige una palabra o frase que resuma lo que se hace en la migración. Por ejemplo, podría denominar "AddDepartmentTable" a una migración posterior.

    Migrations folder with initial migration

    El método Up de la clase InitialCreate crea las tablas de base de datos que se corresponden a los conjuntos de entidades del modelo de datos y el método Down las elimina. Las migraciones llaman al método Up para implementar los cambios del modelo de datos para una migración. Cuando se escribe un comando para revertir la actualización, las migraciones llaman al método Down. En el código siguiente se muestra el contenido del archivo InitialCreate:

    namespace ContosoUniversity.Migrations
    {
        using System;
        using System.Data.Entity.Migrations;
        
        public partial class InitialCreate : DbMigration
        {
            public override void Up()
            {
                CreateTable(
                    "dbo.Student",
                    c => new
                        {
                            StudentID = c.Int(nullable: false, identity: true),
                            LastName = c.String(),
                            FirstMidName = c.String(),
                            EnrollmentDate = c.DateTime(nullable: false),
                        })
                    .PrimaryKey(t => t.StudentID);
                
                CreateTable(
                    "dbo.Enrollment",
                    c => new
                        {
                            EnrollmentID = c.Int(nullable: false, identity: true),
                            CourseID = c.Int(nullable: false),
                            StudentID = c.Int(nullable: false),
                            Grade = c.Int(),
                        })
                    .PrimaryKey(t => t.EnrollmentID)
                    .ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
                    .ForeignKey("dbo.Student", t => t.StudentID, cascadeDelete: true)
                    .Index(t => t.CourseID)
                    .Index(t => t.StudentID);
                
                CreateTable(
                    "dbo.Course",
                    c => new
                        {
                            CourseID = c.Int(nullable: false),
                            Title = c.String(),
                            Credits = c.Int(nullable: false),
                        })
                    .PrimaryKey(t => t.CourseID);
                
            }
            
            public override void Down()
            {
                DropIndex("dbo.Enrollment", new[] { "StudentID" });
                DropIndex("dbo.Enrollment", new[] { "CourseID" });
                DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
                DropForeignKey("dbo.Enrollment", "CourseID", "dbo.Course");
                DropTable("dbo.Course");
                DropTable("dbo.Enrollment");
                DropTable("dbo.Student");
            }
        }
    }
    

    El comando update-database ejecuta el método Up para crear la base de datos y, después, ejecuta el método Seed para rellenar la base de datos.

Ahora se ha creado una base de datos de SQL Server para el modelo de datos. El nombre de la base de datos es ContosoUniversity y el archivo .mdf se encuentra en la carpeta App_Data del proyecto porque es lo que ha especificado en la cadena de conexión.

Puede usar el Explorador de servidores o el Explorador de objetos de SQL Server (SSOX) para ver la base de datos en Visual Studio. En este tutorial, usará el Explorador de servidores. En Visual Studio Express 2012 para Web, el Explorador de servidores se denomina Explorador de bases de datos.

  1. En el menú Ver, haga clic en Explorador de servidores.

  2. Haga clic en el icono Agregar conexión.

    Screenshot that shows the Database Explorer window. The Add Connection icon is highlighted.

  3. Si se abre el cuadro de diálogo Elegir origen de datos, haga clic en Microsoft SQL Server y después en Continuar.

    Screenshot that shows the Choose Data Source dialog box. The Microsoft S Q L Server data source is selected.

  4. En el cuadro de diálogo Agregar conexión, escriba (localdb)\v11.0 como Nombre del servidor. En Seleccione o escriba un nombre de base de datos, seleccione ContosoUniversity.

    Screenshot that shows the Add Connection dialog box. The sample Server name and Contoso University database are highlighted.

  5. Haga clic en OK.

  6. Expanda SchoolContext y después Tablas.

    Screenshot that shows the Server Explorer page. The School Context and Tables tabs are expanded.

  7. Haga clic con el botón derecho en la tabla Student y haga clic en Mostrar datos de la tabla para ver las columnas que se han creado y las filas que se han insertado en la tabla.

    Student table

Creación de un controlador de alumnos y vistas

El siguiente paso consiste en crear un controlador y vistas de ASP.NET MVC en la aplicación que puedan funcionar con una de estas tablas.

  1. Para crear un controlador Student, haga clic con el botón derecho en la carpeta Controladores en el Explorador de soluciones, seleccione Agregar y, después, haga clic en Controlador. En el cuadro de diálogo Agregar controlador, realice las siguientes selecciones y, después, haga clic enAgregar:

    • Nombre del controlador: StudentController.

    • Plantilla: Controlador de MVC con acciones de lectura o escritura y vistas que usan Entity Framework.

    • Clase de modelo: Student (ContosoUniversity.Models). (Si no ve esta opción en la lista desplegable, compile el proyecto e inténtelo de nuevo).

    • Clase de contexto de datos: SchoolContext (ContosoUniversity.Models).

    • Vistas: Razor (CSHTML). (El valor predeterminado).

      Add_Controller_dialog_box_for_Student_controller

  2. Visual Studio abre el archivo Controllers\StudentController.cs. Verá que se ha creado una variable de clase que crea una instancia de un objeto de contexto de base de datos:

    private SchoolContext db = new SchoolContext();
    

    El método de acción Index obtiene una lista de alumnos de la entidad Students, que se establece al leer la propiedad Students de la instancia del contexto de base de datos:

    public ViewResult Index()
    {
        return View(db.Students.ToList());
    }
    

    En la vista Student\Index.cshtml se muestra esta lista en una tabla:

    <table>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.LastName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.FirstMidName)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.EnrollmentDate)
            </th>
            <th></th>
        </tr>
    
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { id=item.StudentID }) |
                @Html.ActionLink("Details", "Details", new { id=item.StudentID }) |
                @Html.ActionLink("Delete", "Delete", new { id=item.StudentID })
            </td>
        </tr>
    }
    
  3. Presione CTRL+F5 para ejecutar el proyecto.

    Haga clic en la pestaña Students para ver los datos de prueba insertados por el método Seed.

    Student Index page

Convenciones

La cantidad de código que tendría que escribir para que Entity Framework pudiera crear una base de datos completa automáticamente es mínima debido al uso de convenciones, las suposiciones que hace Entity Framework. Algunas de ellas ya se han mencionado:

  • Las formas pluralizadas de los nombres de clase de entidad se usan como nombres de tabla.
  • Los nombres de propiedad de entidad se usan para los nombres de columna.
  • Las propiedades de entidad que se denominan ID o nombre_de_claseID se reconocen como propiedades de clave principal.

Ha visto que las convenciones se pueden invalidar (por ejemplo, ha especificado que los nombres de tabla no se deben pluralizar) y obtendrá más información sobre las convenciones y cómo reemplazarlas en el tutorial Creación de un modelo de datos más complejo más adelante en esta serie. Para más información, vea Convenciones de Code First.

Resumen

Ya ha creado una aplicación simple en la que se usa Entity Framework y SQL Server Express para almacenar y mostrar datos. En el tutorial siguiente, obtendrá información sobre cómo realizar operaciones CRUD (crear, leer, actualizar y eliminar) básicas. Puede dejar comentarios en la parte inferior de esta página. Háganos saber si le ha gustado esta parte del tutorial y cómo podríamos mejorarla.

En el mapa de contenido de acceso a datos de ASP.NET pueden encontrar vínculos a otros recursos de Entity Framework.