Razor Pages con Entity Framework Core en ASP.NET Core: Tutorial 1 de 8Razor Pages with Entity Framework Core in ASP.NET Core - Tutorial 1 of 8

Por Tom Dykstra y Rick AndersonBy Tom Dykstra and Rick Anderson

Este es el primero de una serie de tutoriales en los que se muestra cómo usar Entity Framework (EF) Core en una aplicación Razor Pages en ASP.NET Core.This is the first in a series of tutorials that show how to use Entity Framework (EF) Core in an ASP.NET Core Razor Pages app. En el tutorial se crea un sitio web de una universidad ficticia, Contoso University.The tutorials build a web site for a fictional Contoso University. El sitio incluye funciones como la admisión de alumnos, la creación de cursos y las asignaciones de instructores.The site includes functionality such as student admission, course creation, and instructor assignments. En el tutorial se usa el enfoque de Code First.The tutorial uses the code first approach. Para obtener información sobre cómo seguir este tutorial mediante el enfoque de Database First, consulte este problema de GitHub.For information on following this tutorial using the database first approach, see this Github issue.

Descargue o vea la aplicación completa.Download or view the completed app. Instrucciones de descarga.Download instructions.

Requisitos previosPrerequisites

Motores de bases de datosDatabase engines

En las instrucciones de Visual Studio se usa SQL Server LocalDB, una versión de SQL Server Express que solo se ejecuta en Windows.The Visual Studio instructions use SQL Server LocalDB, a version of SQL Server Express that runs only on Windows.

En las instrucciones de Visual Studio Code se usa SQLite, un motor de base de datos multiplataforma.The Visual Studio Code instructions use SQLite, a cross-platform database engine.

Si decide usar SQLite, descargue e instale una herramienta de terceros para administrar y ver una base de datos de SQLite, como DB Browser for SQLite.If you choose to use SQLite, download and install a third-party tool for managing and viewing a SQLite database, such as DB Browser for SQLite.

Solución de problemasTroubleshooting

Si experimenta un problema que no puede resolver, compare el código con el proyecto completado.If you run into a problem you can't resolve, compare your code to the completed project. Una buena forma de obtener ayuda consiste en publicar una pregunta en StackOverflow.com con la etiqueta ASP.NET Core o la etiqueta EF Core.A good way to get help is by posting a question to StackOverflow.com, using the ASP.NET Core tag or the EF Core tag.

La aplicación de ejemploThe sample app

La aplicación compilada en estos tutoriales es un sitio web básico de una universidad.The app built in these tutorials is a basic university web site. Los usuarios pueden ver y actualizar la información de estudiantes, cursos e instructores.Users can view and update student, course, and instructor information. Estas son algunas de las pantallas que se crean en el tutorial.Here are a few of the screens created in the tutorial.

Página de índice de Students

Página de edición de estudiantes

El estilo de la interfaz de usuario de este sitio se basa en las plantillas de proyecto integradas.The UI style of this site is based on the built-in project templates. El enfoque del tutorial es cómo usar EF Core con ASP.NET Core, no cómo personalizar la interfaz de usuario.The tutorial's focus is on how to use EF Core with ASP.NET Core, not how to customize the UI.

Creación del proyecto de aplicación webCreate the web app project

  1. Inicie Visual Studio y seleccione Crear un proyecto.Start Visual Studio and select Create a new project.
  2. En el cuadro de diálogo Crear un proyecto, seleccione Aplicación web ASP.NET Core > Siguiente.In the Create a new project dialog, select ASP.NET Core Web Application > Next.
  3. En el cuadro de diálogo Configurar su nuevo proyecto, escriba ContosoUniversity en Nombre del proyecto.In the Configure your new project dialog, enter ContosoUniversity for Project name. Es importante usar este nombre exacto, incluido el uso de mayúsculas, para que cada namespace coincida cuando se copie el código.It's important to use this exact name including capitalization, so each namespace matches when code is copied.
  4. Seleccione Crear.Select Create.
  5. En el cuadro de diálogo Crear una aplicación web ASP.NET Core, seleccione:In the Create a new ASP.NET Core web application dialog, select:
    1. .NET Core y ASP.NET Core 5.0 en los menús desplegables..NET Core and ASP.NET Core 5.0 in the dropdowns.
    2. Aplicación web de ASP.NET Core.ASP.NET Core Web App.
    3. Crear Cuadro de diálogo Nuevo proyecto de ASP.NET CoreCreate New ASP.NET Core Project dialog

Configurar el estilo del sitioSet up the site style

Copie el código siguiente y péguelo en el archivo Pages/Shared/_Layout.cshtml: .Copy and paste the following code into the Pages/Shared/_Layout.cshtml file:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Contoso University</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                                                                                                                    aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Departments/Index">Departments</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2020 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.min.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @await RenderSectionAsync("Scripts", required: false)
</body>
</html>

El archivo de diseño establece el encabezado, el pie de página y el menú del sitio.The layout file sets the site header, footer, and menu. En el código anterior se realizan los cambios siguientes:The preceding code makes the following changes:

  • Todas las repeticiones de "ContosoUniversity" por "Contoso University".Each occurrence of "ContosoUniversity" to "Contoso University". Hay tres repeticiones.There are three occurrences.
  • Se eliminan las entradas del menú de Inicio y Privacidad.The Home and Privacy menu entries are deleted.
  • Se han agregado entradas para Acerca de, Estudiantes, Cursos, Instructores y Departamentos.Entries are added for About, Students, Courses, Instructors, and Departments.

En Pages/Index.cshtml, reemplace el contenido del archivo por el código siguiente:In Pages/Index.cshtml, replace the contents of the file with the following code:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="row mb-auto">
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 mb-4 ">
                <p class="card-text">
                    Contoso University is a sample application that
                    demonstrates how to use Entity Framework Core in an
                    ASP.NET Core Razor Pages web app.
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column position-static">
                <p class="card-text mb-auto">
                    You can build the application by following the steps in a series of tutorials.
                </p>
                <p>
                    <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column">
                <p class="card-text mb-auto">
                    You can download the completed project from GitHub.
                </p>
                <p>
                    <a href="https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
                </p>
            </div>
        </div>
    </div>
</div>

El código anterior reemplaza el texto sobre ASP.NET Core con texto sobre esta aplicación.The preceding code replaces the text about ASP.NET Core with text about this app.

Ejecute la aplicación para comprobar que aparece la página principal.Run the app to verify that the home page appears.

El modelo de datosThe data model

En las secciones siguientes se crea un modelo de datos:The following sections create a data model:

Diagrama del modelo de datos Course-Enrollment-Student

Un alumno se puede inscribir en cualquier número de cursos y un curso puede tener cualquier número de alumnos inscritos.A student can enroll in any number of courses, and a course can have any number of students enrolled in it.

La entidad StudentThe Student entity

Diagrama de la entidad Student

  • Cree una carpeta Models en la carpeta del proyecto.Create a Models folder in the project folder.

  • Cree Models/Student.cs con el código siguiente:Create Models/Student.cs with the following code:

    using System;
    using System.Collections.Generic;
    
    namespace ContosoUniversity.Models
    {
        public class Student
        {
            public int ID { get; set; }
            public string LastName { get; set; }
            public string FirstMidName { get; set; }
            public DateTime EnrollmentDate { get; set; }
    
            public ICollection<Enrollment> Enrollments { get; set; }
        }
    }
    

La propiedad ID se convierte en la columna de clave principal de la tabla de base de datos que se corresponde a esta clase.The ID property becomes the primary key column of the database table that corresponds to this class. De forma predeterminada, EF Core interpreta como la clave principal una propiedad que se denomine ID o classnameID.By default, EF Core interprets a property that's named ID or classnameID as the primary key. Por tanto, el nombre que se reconoce de forma automática para la clave principal de la clase Student es StudentID.So the alternative automatically recognized name for the Student class primary key is StudentID. Para más información, consulte EF Core: claves.For more information, see EF Core - Keys.

La propiedad Enrollments es una propiedad de navegación.The Enrollments property is a navigation property. Las propiedades de navegación contienen otras entidades relacionadas con esta entidad.Navigation properties hold other entities that are related to this entity. En este caso, la propiedad Enrollments de una entidad Student contiene todas las entidades Enrollment que están relacionadas con esa instancia de Student.In this case, the Enrollments property of a Student entity holds all of the Enrollment entities that are related to that Student. Por ejemplo, si una fila Student de la base de datos tiene dos filas Enrollment relacionadas, la propiedad de navegación Enrollments contiene esas dos entidades Enrollment.For example, if a Student row in the database has two related Enrollment rows, the Enrollments navigation property contains those two Enrollment entities.

En la base de datos, una fila Enrollment se relaciona con una fila Student si su columna StudentID contiene el valor de identificador del alumno.In the database, an Enrollment row is related to a Student row if its StudentID column contains the student's ID value. Por ejemplo, imagine que una fila Student tiene el identificador 1.For example, suppose a Student row has ID=1. Las filas Enrollment relacionadas tendrán StudentID = 1.Related Enrollment rows will have StudentID = 1. StudentID es una clave externa de la tabla Enrollment.StudentID is a foreign key in the Enrollment table.

La propiedad Enrollments se define como ICollection<Enrollment> porque puede haber varias entidades Enrollment relacionadas.The Enrollments property is defined as ICollection<Enrollment> because there may be multiple related Enrollment entities. Puede usar otros tipos de colección, como List<Enrollment> o HashSet<Enrollment>.You can use other collection types, such as List<Enrollment> or HashSet<Enrollment>. Cuando se usa ICollection<Enrollment>, EF Core crea una colección HashSet<Enrollment> de forma predeterminada.When ICollection<Enrollment> is used, EF Core creates a HashSet<Enrollment> collection by default.

La entidad EnrollmentThe Enrollment entity

Diagrama de la entidad Enrollment

Cree Models/Enrollment.cs con el código siguiente:Create Models/Enrollment.cs with the following code:

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 Course Course { get; set; }
        public Student Student { get; set; }
    }
}

La propiedad EnrollmentID es la clave principal; esta entidad usa el patrón classnameID en lugar de ID por sí solo.The EnrollmentID property is the primary key; this entity uses the classnameID pattern instead of ID by itself. Para un modelo de datos de producción, elija un patrón y úselo de forma coherente.For a production data model, choose one pattern and use it consistently. En este tutorial se usan los dos simplemente para ilustrar el trabajo.This tutorial uses both just to illustrate that both work. El uso de ID sin classname facilita la implementación de algunos tipos de cambios del modelo de datos.Using ID without classname makes it easier to implement some kinds of data model changes.

La propiedad Grade es una enum.The Grade property is an enum. El signo de interrogación después de la declaración de tipo Grade indica que la propiedad Gradeacepta valores NULL.The question mark after the Grade type declaration indicates that the Grade property is nullable. 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.A grade that's null is different from a zero grade—null means a grade isn't known or hasn't been assigned yet.

La propiedad StudentID es una clave externa y la propiedad de navegación correspondiente es Student.The StudentID property is a foreign key, and the corresponding navigation property is Student. Una entidad Enrollment está asociada con una entidad Student, por lo que la propiedad contiene una única entidad Student.An Enrollment entity is associated with one Student entity, so the property contains a single Student entity.

La propiedad CourseID es una clave externa y la propiedad de navegación correspondiente es Course.The CourseID property is a foreign key, and the corresponding navigation property is Course. Una entidad Enrollment está asociada con una entidad Course.An Enrollment entity is associated with one Course entity.

EF Core interpreta una propiedad como una clave externa si se denomina <navigation property name><primary key property name>.EF Core interprets a property as a foreign key if it's named <navigation property name><primary key property name>. Por ejemplo, StudentID es la clave externa para la propiedad de navegación Student, ya que la clave principal de la entidad Student es ID.For example,StudentID is the foreign key for the Student navigation property, since the Student entity's primary key is ID. Las propiedades de clave externa también se pueden denominar <primary key property name>.Foreign key properties can also be named <primary key property name>. Por ejemplo CourseID, dado que la clave principal de la entidad Course es CourseID.For example, CourseID since the Course entity's primary key is CourseID.

La entidad CourseThe Course entity

Diagrama de la entidad Course

Cree Models/Course.cs con el código siguiente:Create Models/Course.cs with the following code:

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 ICollection<Enrollment> Enrollments { get; set; }
    }
}

La propiedad Enrollments es una propiedad de navegación.The Enrollments property is a navigation property. Una entidad Course puede estar relacionada con cualquier número de entidades Enrollment.A Course entity can be related to any number of Enrollment entities.

El atributo DatabaseGenerated permite que la aplicación especifique la clave principal en lugar de hacer que la base de datos la genere.The DatabaseGenerated attribute allows the app to specify the primary key rather than having the database generate it.

Compile el proyecto para comprobar que no hay errores de compilación.Build the project to validate that there are no compiler errors.

Scaffolding de las páginas StudentScaffold Student pages

En esta sección, se usa la herramienta de scaffolding de ASP.NET Core para generar lo siguiente:In this section, you use the ASP.NET Core scaffolding tool to generate:

  • Una clase de DbContext de EF Core.An EF Core DbContext class. El contexto es la clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos determinado.The context is the main class that coordinates Entity Framework functionality for a given data model. Se deriva de la clase Microsoft.EntityFrameworkCore.DbContext.It derives from the Microsoft.EntityFrameworkCore.DbContext class.
  • Razor Pages que controlan las operaciones de creación, lectura, actualización y eliminación (CRUD) de la entidad Student.Razor pages that handle Create, Read, Update, and Delete (CRUD) operations for the Student entity.
  • Cree una carpeta Pages/Students.Create a Pages/Students folder.
  • En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Pages/Students y seleccione Agregar > Nuevo elemento con scaffold.In Solution Explorer, right-click the Pages/Students folder and select Add > New Scaffolded Item.
  • En el cuadro de diálogo Agregar nuevo elemento scaffold:In the Add New Scaffold Item dialog:
    • En la pestaña de la izquierda, seleccione Instalado > Común > Páginas Razor .In the left tab, select Installed > Common > Razor Pages
    • Seleccione Páginas de Razor que usan Entity Framework (CRUD) > Agregar.Select Razor Pages using Entity Framework (CRUD) > ADD.
  • En el cuadro de diálogo para agregar instancias de Razor Pages que usan Entity Framework (CRUD) :In the Add Razor Pages using Entity Framework (CRUD) dialog:
    • En la lista desplegable Clase de modelo, seleccione Student (ContosoUniversity.Models) .In the Model class drop-down, select Student (ContosoUniversity.Models).
    • En la fila Clase de contexto de datos, seleccione el signo + (más).In the Data context class row, select the + (plus) sign.
      • Cambie el nombre del contexto de datos para que acabe en SchoolContext en lugar de ContosoUniversityContext.Change the data context name to end in SchoolContext rather than ContosoUniversityContext. Nombre del contexto actualizado: ContosoUniversity.Data.SchoolContext.The updated context name: ContosoUniversity.Data.SchoolContext
      • Seleccione Agregar para terminar de agregar la clase de contexto de datos.Select Add to finish adding the data context class.
    • Seleccione Agregar para finalizar el cuadro de diálogo Agregar Razor Pages.Select Add to finish the Add Razor Pages dialog.

Los paquetes siguientes se instalan de forma automática:The following packages are automatically installed:

  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.EntityFrameworkCore.Tools
  • Microsoft.VisualStudio.Web.CodeGeneration.Design

Si el paso anterior falla, compile el proyecto y vuelva a intentar el paso de scaffolding.If the preceding step fails, build the project and retry the scaffold step.

El proceso de scaffolding:The scaffolding process:

  • Crea Razor Pages en la carpeta Pages/Students:Creates Razor pages in the Pages/Students folder:
    • Create.cshtml y Create.cshtml.csCreate.cshtml and Create.cshtml.cs
    • Delete.cshtml y Delete.cshtml.csDelete.cshtml and Delete.cshtml.cs
    • Details.cshtml y Details.cshtml.csDetails.cshtml and Details.cshtml.cs
    • Edit.cshtml y Edit.cshtml.csEdit.cshtml and Edit.cshtml.cs
    • Index.cshtml e Index.cshtml.csIndex.cshtml and Index.cshtml.cs
  • Crea Data/SchoolContext.cs.Creates Data/SchoolContext.cs.
  • Agrega el contexto a la inserción de dependencias en Startup.cs.Adds the context to dependency injection in Startup.cs.
  • Agrega una cadena de conexión de la base de datos a appsettings.json .Adds a database connection string to appsettings.json.

Cadena de conexión de base de datosDatabase connection string

La herramienta de scaffolding genera una cadena de conexión en el archivo appsettings.json .The scaffolding tool generates a connection string in the appsettings.json file.

La cadena de conexión especifica SQL Server LocalDB:The connection string specifies SQL Server LocalDB:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversityContext-1;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

LocalDB es una versión ligera del motor de base de datos de SQL Server Express y está dirigida al desarrollo de aplicaciones, no al uso en producción.LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for app development, not production use. De forma predeterminada, LocalDB crea archivos .mdf en el directorio C:/Users/<user>.By default, LocalDB creates .mdf files in the C:/Users/<user> directory.

Actualización de la clase de contexto de base de datosUpdate the database context class

La clase principal que coordina la funcionalidad de EF Core para un modelo de datos determinado es la clase de contexto de base de datos.The main class that coordinates EF Core functionality for a given data model is the database context class. El contexto se deriva de Microsoft.EntityFrameworkCore.DbContext.The context is derived from Microsoft.EntityFrameworkCore.DbContext. En el contexto se especifica qué entidades se incluyen en el modelo de datos.The context specifies which entities are included in the data model. En este proyecto, la clase se denomina SchoolContext.In this project, the class is named SchoolContext.

Actualice Data/SchoolContext.cs con el código siguiente:Update Data/SchoolContext.cs with the following code:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext (DbContextOptions<SchoolContext> options)
            : base(options)
        {
        }

        public DbSet<Student> Students { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Course> Courses { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }
}

El código anterior cambia del elemento DbSet<Student> Student singular al DbSet<Student> Students plural.The preceding code changes from the singular DbSet<Student> Student to the plural DbSet<Student> Students. Para que el código de las páginas Razor coincida con el nombre nuevo de DBSet, realice un cambio global de: _context.Student.To make the Razor Pages code match the new DBSet name, make a global change from: _context.Student. a: _context.Students..to: _context.Students.

Hay ocho repeticiones.There are 8 occurrences.

Dado que un conjunto de entidades contiene varias entidades, muchos desarrolladores prefieren que los nombres de la propiedad DBSet sean plurales.Because an entity set contains multiple entities, many developers prefer the DBSet property names should be plural.

El código resaltado:The highlighted code:

  • Crea una propiedad DbSet<TEntity> para cada conjunto de entidades.Creates a DbSet<TEntity> property for each entity set. En la terminología de EF Core:In EF Core terminology:
    • Un conjunto de entidades normalmente se corresponde a una tabla de base de datos.An entity set typically corresponds to a database table.
    • Una entidad se corresponde con una fila de la tabla.An entity corresponds to a row in the table.
  • Llama a OnModelCreating.Calls OnModelCreating. OnModelCreating:OnModelCreating:
    • se llama cuando SchoolContext se ha inicializado, pero antes de que el modelo se haya bloqueado y utilizado para inicializar el contexto.Is called when SchoolContext has been initialized, but before the model has been locked down and used to initialize the context.
    • Es necesario porque, más adelante en el tutorial, la entidad Student tendrá referencias a las demás entidades.Is required because later in the tutorial The Student entity will have references to the other entities.

Compile el proyecto para comprobar que no haya errores del compilador.Build the project to verify there are no compiler errors.

Startup.csStartup.cs

ASP.NET Core integra la inserción de dependencias.ASP.NET Core is built with dependency injection. Los servicios como SchoolContext se registran con inserción de dependencias durante el inicio de la aplicación.Services such as the SchoolContext are registered with dependency injection during app startup. Estos servicios se proporcionan a los componentes que los necesitan, como páginas Razor, a través de parámetros de constructor.Components that require these services, such as Razor Pages, are provided these services via constructor parameters. El código de constructor que obtiene una instancia de contexto de base de datos se muestra más adelante en el tutorial.The constructor code that gets a database context instance is shown later in the tutorial.

La herramienta de scaffolding ha registrado de forma automática la clase de contexto con el contenedor de inserción de dependencias.The scaffolding tool automatically registered the context class with the dependency injection container.

El proveedor de scaffolding ha agregado las líneas resaltadas siguientes:The following highlighted lines were added by the scaffolder:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    services.AddDbContext<SchoolContext>(options =>
            options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
}

Tener Visual Studio CodeVisual Studio Code

Compruebe que el código que agrega el proveedor de scaffolding llama a UseSqlite.Verify the code added by the scaffolder calls UseSqlite.

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    services.AddDbContext<SchoolContext>(options =>
         options.UseSqlite(Configuration.GetConnectionString("SchoolContext")));
}

Para obtener información sobre el uso de una base de datos de producción, vea Uso de SQLite para desarrollo, SQL Server para producción.See Use SQLite for development, SQL Server for production for information on using a production database.

El nombre de la cadena de conexión se pasa al contexto mediante una llamada a un método en un objeto DbContextOptions.The name of the connection string is passed in to the context by calling a method on a DbContextOptions object. Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la cadena de conexión desde el archivo appsettings.json .For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.

Incorporación del filtro de excepción de base de datosAdd the database exception filter

Agregue AddDatabaseDeveloperPageExceptionFilter a ConfigureServices, tal como se muestra en el código siguiente:Add AddDatabaseDeveloperPageExceptionFilter to ConfigureServices as shown in the following code:

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    services.AddDbContext<SchoolContext>(options =>
       options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));

    services.AddDatabaseDeveloperPageExceptionFilter();
}

Agregue el paquete NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.Add the Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package.

En la PMC, escriba lo siguiente para agregar el paquete NuGet:In the PMC, enter the following to add the NuGet package:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore -Version 5.0.0-rc.2.20475.17

Tener Visual Studio CodeVisual Studio Code

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();

    services.AddDbContext<SchoolContext>(options =>
       options.UseSqlite(Configuration.GetConnectionString("SchoolContext")));

    services.AddDatabaseDeveloperPageExceptionFilter();
}

El paquete NuGet de Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore proporciona middleware de ASP.NET Core para páginas de error de Entity Framework Core.The Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore NuGet package provides ASP.NET Core middleware for Entity Framework Core error pages. Este middleware ayuda a detectar y diagnosticar errores con migraciones de Entity Framework Core.This middleware helps to detect and diagnose errors with Entity Framework Core migrations.

AddDatabaseDeveloperPageExceptionFilter proporciona información de error útil en el entorno de desarrollo.The AddDatabaseDeveloperPageExceptionFilter provides helpful error information in the development environment.

Creación de la base de datosCreate the database

Actualice Program.cs para crear la base de datos si no existe:Update Program.cs to create the database if it doesn't exist:

using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            CreateDbIfNotExists(host);

            host.Run();
        }

        private static void CreateDbIfNotExists(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    context.Database.EnsureCreated();
                    // DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

El método EnsureCreated no realiza ninguna acción si existe una base de datos para el contexto.The EnsureCreated method takes no action if a database for the context exists. Si no existe ninguna base de datos, se crean la base de datos y el esquema.If no database exists, it creates the database and schema. EnsureCreated habilita el flujo de trabajo siguiente para controlar los cambios del modelo de datos:EnsureCreated enables the following workflow for handling data model changes:

  • Se elimina la base de datos.Delete the database. Se pierden los datos existentes.Any existing data is lost.
  • Se cambia el modelo de datos.Change the data model. Por ejemplo, se agrega un campo EmailAddress.For example, add an EmailAddress field.
  • Ejecutar la aplicación.Run the app.
  • EnsureCreated crea una base de datos con el esquema nuevo.EnsureCreated creates a database with the new schema.

Este flujo de trabajo funciona bien al principio de la fase de desarrollo cuando el esquema evoluciona rápidamente, siempre y cuando no sea necesario conservar los datos.This workflow works well early in development when the schema is rapidly evolving, as long as you don't need to preserve data. La situación es distinta cuando es necesario conservar los datos introducidos en la base de datos.The situation is different when data that has been entered into the database needs to be preserved. En ese caso, use las migraciones.When that is the case, use migrations.

Más adelante en la serie de tutoriales, eliminará la base de datos creada por EnsureCreated y, en su lugar, usará las migraciones.Later in the tutorial series, you delete the database that was created by EnsureCreated and use migrations instead. Una base de datos creada por EnsureCreated no se puede actualizar mediante migraciones.A database that is created by EnsureCreated can't be updated by using migrations.

Prueba de la aplicaciónTest the app

  • Ejecutar la aplicación.Run the app.
  • Haga clic en el vínculo Students y, después, en Crear nuevo.Select the Students link and then Create New.
  • Pruebe los vínculos Edit, Details y Delete.Test the Edit, Details, and Delete links.

Inicializar la base de datosSeed the database

El método EnsureCreated crea una base de datos vacía.The EnsureCreated method creates an empty database. En esta sección se agrega código que rellena la base de datos con datos de prueba.This section adds code that populates the database with test data.

Cree Data/DbInitializer.cs con el código siguiente:Create Data/DbInitializer.cs with the following code:

using ContosoUniversity.Data;
using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
                new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2019-09-01")},
                new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
                new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
            };

            context.Students.AddRange(students);
            context.SaveChanges();

            var courses = new 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}
            };

            context.Courses.AddRange(courses);
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
                new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
                new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
                new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
                new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
                new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
                new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
                new Enrollment{StudentID=3,CourseID=1050},
                new Enrollment{StudentID=4,CourseID=1050},
                new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
                new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
                new Enrollment{StudentID=6,CourseID=1045},
                new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };

            context.Enrollments.AddRange(enrollments);
            context.SaveChanges();
        }
    }
}

El código comprueba si hay alumnos en la base de datos.The code checks if there are any students in the database. Si no hay ningún alumno, agrega datos de prueba a la base de datos.If there are no students, it adds test data to the database. Crea los datos de prueba en matrices en lugar de colecciones List<T> para optimizar el rendimiento.It creates the test data in arrays rather than List<T> collections to optimize performance.

En Program.cs, reemplace la llamada a EnsureCreated con una llamada a DbInitializer.Initialize:In Program.cs, replace the EnsureCreated call with a DbInitializer.Initialize call:

// context.Database.EnsureCreated();
DbInitializer.Initialize(context);

Detenga la aplciación si se está ejecutando y ejecute el comando siguiente en la Consola del Administrador de paquetes (PMC):Stop the app if it's running, and run the following command in the Package Manager Console (PMC):

Drop-Database -Confirm

Responda con Y para eliminar la base de datos.Respond with Y to delete the database.

  • Reinicie la aplicación.Restart the app.
  • Seleccione la página Students para ver los datos inicializados.Select the Students page to see the seeded data.

Consulta la base de datosView the database

  • Abra el Explorador de objetos de SQL Server (SSOX) desde el menú Vista en Visual Studio.Open SQL Server Object Explorer (SSOX) from the View menu in Visual Studio.
  • En SSOX, seleccione (localdb)\MSSQLLocalDB > Bases de datos > SchoolContext-{GUID} .In SSOX, select (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. El nombre de la base de datos se genera a partir del nombre de contexto proporcionado anteriormente, más un guión y un GUID.The database name is generated from the context name you provided earlier plus a dash and a GUID.
  • Expanda el nodo Tablas.Expand the Tables node.
  • Haga clic con el botón derecho en la tabla Student y haga clic en Ver datos para ver las columnas que se crearon y las filas que se insertaron en la tabla.Right-click the Student table and click View Data to see the columns created and the rows inserted into the table.
  • Haga clic con el botón derecho en la tabla Student y haga clic en Ver código para ver cómo el modelo Student se asigna al esquema de tabla Student.Right-click the Student table and click View Code to see how the Student model maps to the Student table schema.

Código asincrónicoAsynchronous code

La programación asincrónica es el modo predeterminado de ASP.NET Core y EF Core.Asynchronous programming is the default mode for ASP.NET Core and EF Core.

Un servidor web tiene un número limitado de subprocesos disponibles y, en situaciones de carga alta, es posible que todos los subprocesos disponibles estén en uso.A web server has a limited number of threads available, and in high load situations all of the available threads might be in use. Cuando esto ocurre, el servidor no puede procesar nuevas solicitudes hasta que los subprocesos se liberen.When that happens, the server can't process new requests until the threads are freed up. Con el código sincrónico, se pueden acumular muchos subprocesos mientras no estén realizando ningún trabajo porque están a la espera de que finalice la E/S.With synchronous code, many threads may be tied up while they aren't doing work because they're waiting for I/O to complete. Con el código asincrónico, cuando un proceso está a la espera de que finalice la E/S, se libera su subproceso para el que el servidor lo use para el procesamiento de otras solicitudes.With asynchronous code, when a process is waiting for I/O to complete, its thread is freed up for the server to use for processing other requests. Como resultado, el código asincrónico permite que los recursos de servidor se usen de forma más eficaz y el servidor pueda administrar más tráfico sin retrasos.As a result, asynchronous code enables server resources to be used more efficiently, and the server can handle more traffic without delays.

El código asincrónico introduce una pequeña cantidad de sobrecarga en tiempo de ejecución.Asynchronous code does introduce a small amount of overhead at run time. En situaciones de poco tráfico, la disminución del rendimiento es insignificante, mientras que en situaciones de tráfico elevado, la posible mejora del rendimiento es importante.For low traffic situations, the performance hit is negligible, while for high traffic situations, the potential performance improvement is substantial.

En el código siguiente, la palabra clave async, el valor devuelto Task, la palabra clave await y el método ToListAsync hacen que el código se ejecute de forma asincrónica.In the following code, the async keyword, Task return value, await keyword, and ToListAsync method make the code execute asynchronously.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • La palabra clave async indica al compilador que:The async keyword tells the compiler to:
    • Genere devoluciones de llamada para partes del cuerpo del método.Generate callbacks for parts of the method body.
    • Cree el objeto Task que se devuelve.Create the Task object that's returned.
  • El tipo devuelto Task representa el trabajo en curso.The Task return type represents ongoing work.
  • La palabra clave await hace que el compilador divida el método en dos partes.The await keyword causes the compiler to split the method into two parts. La primera parte termina con la operación que se inició de forma asincrónica.The first part ends with the operation that's started asynchronously. La segunda parte se coloca en un método de devolución de llamada que se llama cuando finaliza la operación.The second part is put into a callback method that's called when the operation completes.
  • ToListAsync es la versión asincrónica del método de extensión ToList.ToListAsync is the asynchronous version of the ToList extension method.

Algunos aspectos que tener en cuenta al escribir código asincrónico en el que se usa EF Core son los siguientes:Some things to be aware of when writing asynchronous code that uses EF Core:

  • Solo se ejecutan de forma asincrónica las instrucciones que hacen que las consultas o los comandos se envíen a la base de datos.Only statements that cause queries or commands to be sent to the database are executed asynchronously. Esto incluye ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync y SaveChangesAsync.That includes ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync, and SaveChangesAsync. No incluye las instrucciones que solo cambian una IQueryable, como var students = context.Students.Where(s => s.LastName == "Davolio").It doesn't include statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").
  • Un contexto de EF Core no es seguro para subprocesos: no intente realizar varias operaciones en paralelo.An EF Core context isn't thread safe: don't try to do multiple operations in parallel.
  • Para aprovechar las ventajas de rendimiento del código asincrónico, compruebe que en los paquetes de biblioteca (por ejemplo para la paginación) se usa async si llaman a métodos de EF Core que envían consultas a la base de datos.To take advantage of the performance benefits of async code, verify that library packages (such as for paging) use async if they call EF Core methods that send queries to the database.

Para obtener más información sobre la programación asincrónica en .NET, vea Programación asincrónica y Programación asincrónica con async y await.For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.

Consideraciones de rendimientoPerformance considerations

En general, una página web no debe cargar un número arbitrario de filas.In general, a web page shouldn't be loading an arbitrary number of rows. Una consulta debe utilizar la paginación o un enfoque de limitación.A query should use paging or a limiting approach. Por ejemplo, la consulta anterior podría usar Take para limitar las filas devueltas:For example, the preceding query could use Take to limit the rows returned:

public class IndexModel : PageModel
{
    private readonly SchoolContext _context;
    private readonly MvcOptions _mvcOptions;

    public IndexModel(SchoolContext context, IOptions<MvcOptions> mvcOptions)
    {
        _context = context;
        _mvcOptions = mvcOptions.Value;
    }

    public IList<Student> Student { get;set; }

    public async Task OnGetAsync()
    {
        Student = await _context.Students.Take(
            _mvcOptions.MaxModelBindingCollectionSize).ToListAsync();
    }
}

Enumerar una tabla grande en una vista podría devolver una respuesta HTTP 200 parcialmente construida si se produce una excepción de base de datos en mitad de la enumeración.Enumerating a large table in a view could return a partially constructed HTTP 200 response if a database exception occurs part way through the enumeration.

MaxModelBindingCollectionSize tiene como valor predeterminado 1024.MaxModelBindingCollectionSize defaults to 1024. El código siguiente establece MaxModelBindingCollectionSize:The following code sets MaxModelBindingCollectionSize:

public void ConfigureServices(IServiceCollection services)
{
    int MyMaxModelBindingCollectionSize = 100;
    Int32.TryParse(Configuration["MyMaxModelBindingCollectionSize"],
                               out MyMaxModelBindingCollectionSize);
    
    services.Configure<MvcOptions>(options => 
           options.MaxModelBindingCollectionSize = MyMaxModelBindingCollectionSize);

    services.AddRazorPages();

    services.AddDbContext<SchoolContext>(options =>
          options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));

    services.AddDatabaseDeveloperPageExceptionFilter();
}

La paginación se trata más adelante en el tutorial.Paging is covered later in the tutorial.

Pasos siguientesNext steps

Este es el primero de una serie de tutoriales en los que se muestra cómo usar Entity Framework (EF) Core en una aplicación Razor Pages en ASP.NET Core.This is the first in a series of tutorials that show how to use Entity Framework (EF) Core in an ASP.NET Core Razor Pages app. En el tutorial se crea un sitio web de una universidad ficticia, Contoso University.The tutorials build a web site for a fictional Contoso University. El sitio incluye funciones como la admisión de alumnos, la creación de cursos y las asignaciones de instructores.The site includes functionality such as student admission, course creation, and instructor assignments. En el tutorial se usa el enfoque de Code First.The tutorial uses the code first approach. Para obtener información sobre cómo seguir este tutorial mediante el enfoque de Database First, consulte este problema de GitHub.For information on following this tutorial using the database first approach, see this Github issue.

Descargue o vea la aplicación completa.Download or view the completed app. Instrucciones de descarga.Download instructions.

Requisitos previosPrerequisites

Motores de bases de datosDatabase engines

En las instrucciones de Visual Studio se usa SQL Server LocalDB, una versión de SQL Server Express que solo se ejecuta en Windows.The Visual Studio instructions use SQL Server LocalDB, a version of SQL Server Express that runs only on Windows.

En las instrucciones de Visual Studio Code se usa SQLite, un motor de base de datos multiplataforma.The Visual Studio Code instructions use SQLite, a cross-platform database engine.

Si decide usar SQLite, descargue e instale una herramienta de terceros para administrar y ver una base de datos de SQLite, como DB Browser for SQLite.If you choose to use SQLite, download and install a third-party tool for managing and viewing a SQLite database, such as DB Browser for SQLite.

Solución de problemasTroubleshooting

Si experimenta un problema que no puede resolver, compare el código con el proyecto completado.If you run into a problem you can't resolve, compare your code to the completed project. Una buena forma de obtener ayuda consiste en publicar una pregunta en StackOverflow.com con la etiqueta ASP.NET Core o la etiqueta EF Core.A good way to get help is by posting a question to StackOverflow.com, using the ASP.NET Core tag or the EF Core tag.

La aplicación de ejemploThe sample app

La aplicación compilada en estos tutoriales es un sitio web básico de una universidad.The app built in these tutorials is a basic university web site. Los usuarios pueden ver y actualizar la información de estudiantes, cursos e instructores.Users can view and update student, course, and instructor information. Estas son algunas de las pantallas que se crean en el tutorial.Here are a few of the screens created in the tutorial.

Página de índice de Students

Página de edición de estudiantes

El estilo de la interfaz de usuario de este sitio se basa en las plantillas de proyecto integradas.The UI style of this site is based on the built-in project templates. El enfoque del tutorial es cómo usar EF Core, no cómo personalizar la interfaz de usuario.The tutorial's focus is on how to use EF Core, not how to customize the UI.

Siga el vínculo de la parte superior de la página para obtener el código fuente para el proyecto completado.Follow the link at the top of the page to get the source code for the completed project. La carpeta cu30 contiene el código para la versión ASP.NET Core 3.0 del tutorial.The cu30 folder has the code for the ASP.NET Core 3.0 version of the tutorial. Los archivos que reflejan el estado del código para los tutoriales 1-7 se pueden encontrar en la carpeta cu30snapshots.Files that reflect the state of the code for tutorials 1-7 can be found in the cu30snapshots folder.

Para ejecutar la aplicación después de descargar el proyecto completado:To run the app after downloading the completed project:

  • Compile el proyecto.Build the project.

  • En la Consola del administrador de paquetes (PMC), ejecute el comando siguiente:In Package Manager Console (PMC) run the following command:

    Update-Database
    
  • Ejecute el proyecto para inicializar la base de datos.Run the project to seed the database.

Creación del proyecto de aplicación webCreate the web app project

  • En el menú Archivo de Visual Studio, seleccione Nuevo > Proyecto.From the Visual Studio File menu, select New > Project.
  • Seleccione Aplicación web de ASP.NET Core.Select ASP.NET Core Web Application.
  • Asigne el nombre ContosoUniversity al proyecto.Name the project ContosoUniversity. Es importante usar este nombre exacto incluido el uso de mayúsculas, para que los espacios de nombres coincidan cuando se copie y pegue el código.It's important to use this exact name including capitalization, so the namespaces match when code is copied and pasted.
  • Seleccione .NET Core y ASP.NET Core 3.0 en las listas desplegables y, luego, Aplicación web.Select .NET Core and ASP.NET Core 3.0 in the dropdowns, and then select Web Application.

Configurar el estilo del sitioSet up the site style

Configure el encabezado, el pie de página y el menú del sitio mediante la actualización de Pages/Shared/_Layout.cshtml:Set up the site header, footer, and menu by updating Pages/Shared/_Layout.cshtml:

  • Cambie todas las repeticiones de "ContosoUniversity" por "Contoso University".Change each occurrence of "ContosoUniversity" to "Contoso University". Hay tres repeticiones.There are three occurrences.

  • Elimine las entradas de menú Home y Privacy, y agregue entradas para About, Students, Courses, Instructors y Departments.Delete the Home and Privacy menu entries, and add entries for About, Students, Courses, Instructors, and Departments.

Los cambios aparecen resaltados.The changes are highlighted.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Contoso University</title>
    <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
    <link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
    <header>
        <nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
            <div class="container">
                <a class="navbar-brand" asp-area="" asp-page="/Index">Contoso University</a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
                        aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>
                <div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
                    <ul class="navbar-nav flex-grow-1">
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/About">About</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Students/Index">Students</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Courses/Index">Courses</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Instructors/Index">Instructors</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-dark" asp-area="" asp-page="/Departments/Index">Departments</a>
                        </li>
                    </ul>
                </div>
            </div>
        </nav>
    </header>
    <div class="container">
        <main role="main" class="pb-3">
            @RenderBody()
        </main>
    </div>

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 2019 - Contoso University - <a asp-area="" asp-page="/Privacy">Privacy</a>
        </div>
    </footer>

    <script src="~/lib/jquery/dist/jquery.js"></script>
    <script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
    <script src="~/js/site.js" asp-append-version="true"></script>

    @RenderSection("Scripts", required: false)
</body>
</html>

En Pages/Index.cshtml, reemplace el contenido del archivo con el código siguiente para reemplazar el texto sobre ASP.NET Core con texto sobre esta aplicación:In Pages/Index.cshtml, replace the contents of the file with the following code to replace the text about ASP.NET Core with text about this app:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="row mb-auto">
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 mb-4 ">
                <p class="card-text">
                    Contoso University is a sample application that
                    demonstrates how to use Entity Framework Core in an
                    ASP.NET Core Razor Pages web app.
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column position-static">
                <p class="card-text mb-auto">
                    You can build the application by following the steps in a series of tutorials.
                </p>
                <p>
                    <a href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro" class="stretched-link">See the tutorial</a>
                </p>
            </div>
        </div>
    </div>
    <div class="col-md-4">
        <div class="row no-gutters border mb-4">
            <div class="col p-4 d-flex flex-column">
                <p class="card-text mb-auto">
                    You can download the completed project from GitHub.
                </p>
                <p>
                    <a href="https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/data/ef-rp/intro/samples" class="stretched-link">See project source code</a>
                </p>
            </div>
        </div>
    </div>
</div>

Ejecute la aplicación para comprobar que aparece la página principal.Run the app to verify that the home page appears.

El modelo de datosThe data model

En las secciones siguientes se crea un modelo de datos:The following sections create a data model:

Diagrama del modelo de datos Course-Enrollment-Student

Un alumno se puede inscribir en cualquier número de cursos y un curso puede tener cualquier número de alumnos inscritos.A student can enroll in any number of courses, and a course can have any number of students enrolled in it.

La entidad StudentThe Student entity

Diagrama de la entidad Student

  • Cree una carpeta Models en la carpeta del proyecto.Create a Models folder in the project folder.

  • Cree Models/Student.cs con el código siguiente:Create Models/Student.cs with the following code:

    using System;
    using System.Collections.Generic;
    
    namespace ContosoUniversity.Models
    {
        public class Student
        {
            public int ID { get; set; }
            public string LastName { get; set; }
            public string FirstMidName { get; set; }
            public DateTime EnrollmentDate { get; set; }
    
            public ICollection<Enrollment> Enrollments { get; set; }
        }
    }
    

La propiedad ID se convierte en la columna de clave principal de la tabla de base de datos que se corresponde a esta clase.The ID property becomes the primary key column of the database table that corresponds to this class. De forma predeterminada, EF Core interpreta como la clave principal una propiedad que se denomine ID o classnameID.By default, EF Core interprets a property that's named ID or classnameID as the primary key. Por tanto, el nombre que se reconoce de forma automática para la clave principal de la clase Student es StudentID.So the alternative automatically recognized name for the Student class primary key is StudentID. Para más información, consulte EF Core: claves.For more information, see EF Core - Keys.

La propiedad Enrollments es una propiedad de navegación.The Enrollments property is a navigation property. Las propiedades de navegación contienen otras entidades relacionadas con esta entidad.Navigation properties hold other entities that are related to this entity. En este caso, la propiedad Enrollments de una entidad Student contiene todas las entidades Enrollment que están relacionadas con esa instancia de Student.In this case, the Enrollments property of a Student entity holds all of the Enrollment entities that are related to that Student. Por ejemplo, si una fila Student de la base de datos tiene dos filas Enrollment relacionadas, la propiedad de navegación Enrollments contiene esas dos entidades Enrollment.For example, if a Student row in the database has two related Enrollment rows, the Enrollments navigation property contains those two Enrollment entities.

En la base de datos, una fila Enrollment se relaciona con una fila Student si su columna StudentID contiene el valor de identificador del alumno.In the database, an Enrollment row is related to a Student row if its StudentID column contains the student's ID value. Por ejemplo, imagine que una fila Student tiene el identificador 1.For example, suppose a Student row has ID=1. Las filas Enrollment relacionadas tendrán StudentID = 1.Related Enrollment rows will have StudentID = 1. StudentID es una clave externa de la tabla Enrollment.StudentID is a foreign key in the Enrollment table.

La propiedad Enrollments se define como ICollection<Enrollment> porque puede haber varias entidades Enrollment relacionadas.The Enrollments property is defined as ICollection<Enrollment> because there may be multiple related Enrollment entities. Puede usar otros tipos de colección, como List<Enrollment> o HashSet<Enrollment>.You can use other collection types, such as List<Enrollment> or HashSet<Enrollment>. Cuando se usa ICollection<Enrollment>, EF Core crea una colección HashSet<Enrollment> de forma predeterminada.When ICollection<Enrollment> is used, EF Core creates a HashSet<Enrollment> collection by default.

La entidad EnrollmentThe Enrollment entity

Diagrama de la entidad Enrollment

Cree Models/Enrollment.cs con el código siguiente:Create Models/Enrollment.cs with the following code:

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 Course Course { get; set; }
        public Student Student { get; set; }
    }
}

La propiedad EnrollmentID es la clave principal; esta entidad usa el patrón classnameID en lugar de ID por sí solo.The EnrollmentID property is the primary key; this entity uses the classnameID pattern instead of ID by itself. Para un modelo de datos de producción, elija un patrón y úselo de forma coherente.For a production data model, choose one pattern and use it consistently. En este tutorial se usan los dos simplemente para ilustrar el trabajo.This tutorial uses both just to illustrate that both work. El uso de ID sin classname facilita la implementación de algunos tipos de cambios del modelo de datos.Using ID without classname makes it easier to implement some kinds of data model changes.

La propiedad Grade es una enum.The Grade property is an enum. El signo de interrogación después de la declaración de tipo Grade indica que la propiedad Gradeacepta valores NULL.The question mark after the Grade type declaration indicates that the Grade property is nullable. 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.A grade that's null is different from a zero grade—null means a grade isn't known or hasn't been assigned yet.

La propiedad StudentID es una clave externa y la propiedad de navegación correspondiente es Student.The StudentID property is a foreign key, and the corresponding navigation property is Student. Una entidad Enrollment está asociada con una entidad Student, por lo que la propiedad contiene una única entidad Student.An Enrollment entity is associated with one Student entity, so the property contains a single Student entity.

La propiedad CourseID es una clave externa y la propiedad de navegación correspondiente es Course.The CourseID property is a foreign key, and the corresponding navigation property is Course. Una entidad Enrollment está asociada con una entidad Course.An Enrollment entity is associated with one Course entity.

EF Core interpreta una propiedad como una clave externa si se denomina <navigation property name><primary key property name>.EF Core interprets a property as a foreign key if it's named <navigation property name><primary key property name>. Por ejemplo, StudentID es la clave externa para la propiedad de navegación Student, ya que la clave principal de la entidad Student es ID.For example,StudentID is the foreign key for the Student navigation property, since the Student entity's primary key is ID. Las propiedades de clave externa también se pueden denominar <primary key property name>.Foreign key properties can also be named <primary key property name>. Por ejemplo CourseID, dado que la clave principal de la entidad Course es CourseID.For example, CourseID since the Course entity's primary key is CourseID.

La entidad CourseThe Course entity

Diagrama de la entidad Course

Cree Models/Course.cs con el código siguiente:Create Models/Course.cs with the following code:

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 ICollection<Enrollment> Enrollments { get; set; }
    }
}

La propiedad Enrollments es una propiedad de navegación.The Enrollments property is a navigation property. Una entidad Course puede estar relacionada con cualquier número de entidades Enrollment.A Course entity can be related to any number of Enrollment entities.

El atributo DatabaseGenerated permite que la aplicación especifique la clave principal en lugar de hacer que la base de datos la genere.The DatabaseGenerated attribute allows the app to specify the primary key rather than having the database generate it.

Compile el proyecto para comprobar que no hay errores de compilación.Build the project to validate that there are no compiler errors.

Scaffolding de las páginas StudentScaffold Student pages

En esta sección, se usa la herramienta de scaffolding de ASP.NET Core para generar lo siguiente:In this section, you use the ASP.NET Core scaffolding tool to generate:

  • Una clase de contexto de EF Core.An EF Core context class. El contexto es la clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos determinado.The context is the main class that coordinates Entity Framework functionality for a given data model. Se deriva de la clase Microsoft.EntityFrameworkCore.DbContext.It derives from the Microsoft.EntityFrameworkCore.DbContext class.
  • Razor Pages que controlan las operaciones de creación, lectura, actualización y eliminación (CRUD) de la entidad Student.Razor pages that handle Create, Read, Update, and Delete (CRUD) operations for the Student entity.
  • Cree una carpeta Students en la carpeta Pages.Create a Students folder in the Pages folder.
  • En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Pages/Students y seleccione Agregar > Nuevo elemento con scaffold.In Solution Explorer, right-click the Pages/Students folder and select Add > New Scaffolded Item.
  • En el cuadro de diálogo Agregar scaffold, seleccione Páginas de Razor que usan Entity Framework (CRUD) > AGREGAR.In the Add Scaffold dialog, select Razor Pages using Entity Framework (CRUD) > ADD.
  • En el cuadro de diálogo para agregar instancias de Razor Pages que usan Entity Framework (CRUD) :In the Add Razor Pages using Entity Framework (CRUD) dialog:
    • En la lista desplegable Clase de modelo, seleccione Student (ContosoUniversity.Models) .In the Model class drop-down, select Student (ContosoUniversity.Models).
    • En la fila Clase de contexto de datos, seleccione el signo + (más).In the Data context class row, select the + (plus) sign.
    • Cambie el nombre del contexto de datos ContosoUniversity.Models.ContosoUniversityContext por ContosoUniversity.Data.SchoolContext.Change the data context name from ContosoUniversity.Models.ContosoUniversityContext to ContosoUniversity.Data.SchoolContext.
    • Seleccione Agregar.Select Add.

Los paquetes siguientes se instalan de forma automática:The following packages are automatically installed:

  • Microsoft.VisualStudio.Web.CodeGeneration.Design
  • Microsoft.EntityFrameworkCore.SqlServer
  • Microsoft.Extensions.Logging.Debug
  • Microsoft.EntityFrameworkCore.Tools

Si tiene un problema con el paso anterior, compile el proyecto y vuelva a intentar el paso de scaffolding.If you have a problem with the preceding step, build the project and retry the scaffold step.

El proceso de scaffolding:The scaffolding process:

  • Crea Razor Pages en la carpeta Pages/Students:Creates Razor pages in the Pages/Students folder:
    • Create.cshtml y Create.cshtml.csCreate.cshtml and Create.cshtml.cs
    • Delete.cshtml y Delete.cshtml.csDelete.cshtml and Delete.cshtml.cs
    • Details.cshtml y Details.cshtml.csDetails.cshtml and Details.cshtml.cs
    • Edit.cshtml y Edit.cshtml.csEdit.cshtml and Edit.cshtml.cs
    • Index.cshtml e Index.cshtml.csIndex.cshtml and Index.cshtml.cs
  • Crea Data/SchoolContext.cs.Creates Data/SchoolContext.cs.
  • Agrega el contexto a la inserción de dependencias en Startup.cs.Adds the context to dependency injection in Startup.cs.
  • Agrega una cadena de conexión de la base de datos a appsettings.json .Adds a database connection string to appsettings.json.

Cadena de conexión de base de datosDatabase connection string

El archivo appsettings.json especifica la cadena de conexión SQL Server LocalDB.The appsettings.json file specifies the connection string SQL Server LocalDB.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext6;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}

LocalDB es una versión ligera del motor de base de datos de SQL Server Express y está dirigida al desarrollo de aplicaciones, no al uso en producción.LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for app development, not production use. De forma predeterminada, LocalDB crea archivos .mdf en el directorio C:/Users/<user>.By default, LocalDB creates .mdf files in the C:/Users/<user> directory.

Actualización de la clase de contexto de base de datosUpdate the database context class

La clase principal que coordina la funcionalidad de EF Core para un modelo de datos determinado es la clase de contexto de base de datos.The main class that coordinates EF Core functionality for a given data model is the database context class. El contexto se deriva de Microsoft.EntityFrameworkCore.DbContext.The context is derived from Microsoft.EntityFrameworkCore.DbContext. En el contexto se especifica qué entidades se incluyen en el modelo de datos.The context specifies which entities are included in the data model. En este proyecto, la clase se denomina SchoolContext.In this project, the class is named SchoolContext.

Actualice Data/SchoolContext.cs con el código siguiente:Update Data/SchoolContext.cs with the following code:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Models;

namespace ContosoUniversity.Data
{
    public class SchoolContext : DbContext
    {
        public SchoolContext (DbContextOptions<SchoolContext> options)
            : base(options)
        {
        }

        public DbSet<Student> Students { get; set; }
        public DbSet<Enrollment> Enrollments { get; set; }
        public DbSet<Course> Courses { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Course>().ToTable("Course");
            modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
            modelBuilder.Entity<Student>().ToTable("Student");
        }
    }
}

El código resaltado crea una propiedad DbSet<TEntity> para cada conjunto de entidades.The highlighted code creates a DbSet<TEntity> property for each entity set. En la terminología de EF Core:In EF Core terminology:

  • Un conjunto de entidades normalmente se corresponde a una tabla de base de datos.An entity set typically corresponds to a database table.
  • Una entidad se corresponde con una fila de la tabla.An entity corresponds to a row in the table.

Puesto que un conjunto de entidades contiene varias entidades, las propiedades DBSet deben ser nombres en plural.Since an entity set contains multiple entities, the DBSet properties should be plural names. Como la herramienta de scaffolding ha creado una instancia Student de DBSet, en este paso se cambia a Students en plural.Since the scaffolding tool created aStudent DBSet, this step changes it to plural Students.

Para que el código de Razor Pages coincida con el nuevo nombre de DBSet, realice un cambio global de _context.Student a _context.Students en todo el proyecto.To make the Razor Pages code match the new DBSet name, make a global change across the whole project of _context.Student to _context.Students. Hay ocho repeticiones.There are 8 occurrences.

Compile el proyecto para comprobar que no haya errores del compilador.Build the project to verify there are no compiler errors.

Startup.csStartup.cs

ASP.NET Core integra la inserción de dependencias.ASP.NET Core is built with dependency injection. Los servicios (como el contexto de base de datos de EF Core) se registran con la inserción de dependencias durante el inicio de la aplicación.Services (such as the EF Core database context) are registered with dependency injection during application startup. Estos servicios se proporcionan a los componentes que los necesitan (como Razor Pages) a través de parámetros de constructor.Components that require these services (such as Razor Pages) are provided these services via constructor parameters. El código de constructor que obtiene una instancia de contexto de base de datos se muestra más adelante en el tutorial.The constructor code that gets a database context instance is shown later in the tutorial.

La herramienta de scaffolding ha registrado de forma automática la clase de contexto con el contenedor de inserción de dependencias.The scaffolding tool automatically registered the context class with the dependency injection container.

  • En ConfigureServices, el proveedor de scaffolding ha agregado la línea resaltada:In ConfigureServices, the highlighted lines were added by the scaffolder:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    
        services.AddDbContext<SchoolContext>(options =>
                options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
    }
    

El nombre de la cadena de conexión se pasa al contexto mediante una llamada a un método en un objeto DbContextOptions.The name of the connection string is passed in to the context by calling a method on a DbContextOptions object. Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la cadena de conexión desde el archivo appsettings.json .For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.

Creación de la base de datosCreate the database

Actualice Program.cs para crear la base de datos si no existe:Update Program.cs to create the database if it doesn't exist:

using ContosoUniversity.Data;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args).Build();

            CreateDbIfNotExists(host);

            host.Run();
        }

        private static void CreateDbIfNotExists(IHost host)
        {
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    context.Database.EnsureCreated();
                    // DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

El método EnsureCreated no realiza ninguna acción si existe una base de datos para el contexto.The EnsureCreated method takes no action if a database for the context exists. Si no existe ninguna base de datos, se crean la base de datos y el esquema.If no database exists, it creates the database and schema. EnsureCreated habilita el flujo de trabajo siguiente para controlar los cambios del modelo de datos:EnsureCreated enables the following workflow for handling data model changes:

  • Se elimina la base de datos.Delete the database. Se pierden los datos existentes.Any existing data is lost.
  • Se cambia el modelo de datos.Change the data model. Por ejemplo, se agrega un campo EmailAddress.For example, add an EmailAddress field.
  • Ejecutar la aplicación.Run the app.
  • EnsureCreated crea una base de datos con el esquema nuevo.EnsureCreated creates a database with the new schema.

Este flujo de trabajo funciona bien al principio de la fase de desarrollo cuando el esquema evoluciona rápidamente, siempre y cuando no sea necesario conservar los datos.This workflow works well early in development when the schema is rapidly evolving, as long as you don't need to preserve data. La situación es distinta cuando es necesario conservar los datos introducidos en la base de datos.The situation is different when data that has been entered into the database needs to be preserved. En ese caso, use las migraciones.When that is the case, use migrations.

Más adelante en la serie de tutoriales, eliminará la base de datos creada por EnsureCreated y, en su lugar, usará las migraciones.Later in the tutorial series, you delete the database that was created by EnsureCreated and use migrations instead. Una base de datos creada por EnsureCreated no se puede actualizar mediante migraciones.A database that is created by EnsureCreated can't be updated by using migrations.

Prueba de la aplicaciónTest the app

  • Ejecutar la aplicación.Run the app.
  • Haga clic en el vínculo Students y, después, en Crear nuevo.Select the Students link and then Create New.
  • Pruebe los vínculos Edit, Details y Delete.Test the Edit, Details, and Delete links.

Inicializar la base de datosSeed the database

El método EnsureCreated crea una base de datos vacía.The EnsureCreated method creates an empty database. En esta sección se agrega código que rellena la base de datos con datos de prueba.This section adds code that populates the database with test data.

Cree Data/DbInitializer.cs con el código siguiente:Create Data/DbInitializer.cs with the following code:

using ContosoUniversity.Data;
using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
                new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2019-09-01")},
                new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2017-09-01")},
                new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2016-09-01")},
                new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2018-09-01")},
                new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2019-09-01")}
            };

            context.Students.AddRange(students);
            context.SaveChanges();

            var courses = new 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}
            };

            context.Courses.AddRange(courses);
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
                new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
                new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
                new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
                new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
                new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
                new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
                new Enrollment{StudentID=3,CourseID=1050},
                new Enrollment{StudentID=4,CourseID=1050},
                new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
                new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
                new Enrollment{StudentID=6,CourseID=1045},
                new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };

            context.Enrollments.AddRange(enrollments);
            context.SaveChanges();
        }
    }
}

El código comprueba si hay alumnos en la base de datos.The code checks if there are any students in the database. Si no hay ningún alumno, agrega datos de prueba a la base de datos.If there are no students, it adds test data to the database. Crea los datos de prueba en matrices en lugar de colecciones List<T> para optimizar el rendimiento.It creates the test data in arrays rather than List<T> collections to optimize performance.

  • En Program.cs, reemplace la llamada a EnsureCreated con una llamada a DbInitializer.Initialize:In Program.cs, replace the EnsureCreated call with a DbInitializer.Initialize call:

    // context.Database.EnsureCreated();
    DbInitializer.Initialize(context);
    

Detenga la aplciación si se está ejecutando y ejecute el comando siguiente en la Consola del Administrador de paquetes (PMC):Stop the app if it's running, and run the following command in the Package Manager Console (PMC):

Drop-Database
  • Reinicie la aplicación.Restart the app.

  • Seleccione la página Students para ver los datos inicializados.Select the Students page to see the seeded data.

Consulta la base de datosView the database

  • Abra el Explorador de objetos de SQL Server (SSOX) desde el menú Vista en Visual Studio.Open SQL Server Object Explorer (SSOX) from the View menu in Visual Studio.
  • En SSOX, seleccione (localdb)\MSSQLLocalDB > Bases de datos > SchoolContext-{GUID} .In SSOX, select (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. El nombre de la base de datos se genera a partir del nombre de contexto proporcionado anteriormente, más un guión y un GUID.The database name is generated from the context name you provided earlier plus a dash and a GUID.
  • Expanda el nodo Tablas.Expand the Tables node.
  • Haga clic con el botón derecho en la tabla Student y haga clic en Ver datos para ver las columnas que se crearon y las filas que se insertaron en la tabla.Right-click the Student table and click View Data to see the columns created and the rows inserted into the table.
  • Haga clic con el botón derecho en la tabla Student y haga clic en Ver código para ver cómo el modelo Student se asigna al esquema de tabla Student.Right-click the Student table and click View Code to see how the Student model maps to the Student table schema.

Código asincrónicoAsynchronous code

La programación asincrónica es el modo predeterminado de ASP.NET Core y EF Core.Asynchronous programming is the default mode for ASP.NET Core and EF Core.

Un servidor web tiene un número limitado de subprocesos disponibles y, en situaciones de carga alta, es posible que todos los subprocesos disponibles estén en uso.A web server has a limited number of threads available, and in high load situations all of the available threads might be in use. Cuando esto ocurre, el servidor no puede procesar nuevas solicitudes hasta que los subprocesos se liberen.When that happens, the server can't process new requests until the threads are freed up. Con el código sincrónico, se pueden acumular muchos subprocesos mientras no estén realizando ningún trabajo porque están a la espera de que finalice la E/S.With synchronous code, many threads may be tied up while they aren't actually doing any work because they're waiting for I/O to complete. Con el código asincrónico, cuando un proceso está a la espera de que finalice la E/S, se libera su subproceso para el que el servidor lo use para el procesamiento de otras solicitudes.With asynchronous code, when a process is waiting for I/O to complete, its thread is freed up for the server to use for processing other requests. Como resultado, el código asincrónico permite que los recursos de servidor se usen de forma más eficaz y el servidor pueda administrar más tráfico sin retrasos.As a result, asynchronous code enables server resources to be used more efficiently, and the server can handle more traffic without delays.

El código asincrónico introduce una pequeña cantidad de sobrecarga en tiempo de ejecución.Asynchronous code does introduce a small amount of overhead at run time. En situaciones de poco tráfico, la disminución del rendimiento es insignificante, mientras que en situaciones de tráfico elevado, la posible mejora del rendimiento es importante.For low traffic situations, the performance hit is negligible, while for high traffic situations, the potential performance improvement is substantial.

En el código siguiente, la palabra clave async, el valor devuelto Task<T>, la palabra clave await y el método ToListAsync hacen que el código se ejecute de forma asincrónica.In the following code, the async keyword, Task<T> return value, await keyword, and ToListAsync method make the code execute asynchronously.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • La palabra clave async indica al compilador que:The async keyword tells the compiler to:
    • Genere devoluciones de llamada para partes del cuerpo del método.Generate callbacks for parts of the method body.
    • Cree el objeto Task que se devuelve.Create the Task object that's returned.
  • El tipo devuelto Task<T> representa el trabajo en curso.The Task<T> return type represents ongoing work.
  • La palabra clave await hace que el compilador divida el método en dos partes.The await keyword causes the compiler to split the method into two parts. La primera parte termina con la operación que se inició de forma asincrónica.The first part ends with the operation that's started asynchronously. La segunda parte se coloca en un método de devolución de llamada que se llama cuando finaliza la operación.The second part is put into a callback method that's called when the operation completes.
  • ToListAsync es la versión asincrónica del método de extensión ToList.ToListAsync is the asynchronous version of the ToList extension method.

Algunos aspectos que tener en cuenta al escribir código asincrónico en el que se usa EF Core son los siguientes:Some things to be aware of when writing asynchronous code that uses EF Core:

  • Solo se ejecutan de forma asincrónica las instrucciones que hacen que las consultas o los comandos se envíen a la base de datos.Only statements that cause queries or commands to be sent to the database are executed asynchronously. Esto incluye ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync y SaveChangesAsync.That includes ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync, and SaveChangesAsync. No incluye las instrucciones que solo cambian una IQueryable, como var students = context.Students.Where(s => s.LastName == "Davolio").It doesn't include statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").
  • Un contexto de EF Core no es seguro para subprocesos: no intente realizar varias operaciones en paralelo.An EF Core context isn't thread safe: don't try to do multiple operations in parallel.
  • Para aprovechar las ventajas de rendimiento del código asincrónico, compruebe que en los paquetes de biblioteca (por ejemplo para la paginación) se usa async si llaman a métodos de EF Core que envían consultas a la base de datos.To take advantage of the performance benefits of async code, verify that library packages (such as for paging) use async if they call EF Core methods that send queries to the database.

Para obtener más información sobre la programación asincrónica en .NET, vea Programación asincrónica y Programación asincrónica con async y await.For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.

Pasos siguientesNext steps

En la aplicación web de ejemplo Contoso University se muestra cómo crear una aplicación web de Razor Pages de ASP.NET Core con Entity Framework (EF) Core.The Contoso University sample web app demonstrates how to create an ASP.NET Core Razor Pages app using Entity Framework (EF) Core.

La aplicación de ejemplo es un sitio web de una universidad ficticia, Contoso University.The sample app is a web site for a fictional Contoso University. Incluye funciones como la admisión de estudiantes, la creación de cursos y asignaciones de instructores.It includes functionality such as student admission, course creation, and instructor assignments. Esta página es la primera de una serie de tutoriales en los que se explica cómo crear la aplicación de ejemplo Contoso University.This page is the first in a series of tutorials that explain how to build the Contoso University sample app.

Descargue o vea la aplicación completa.Download or view the completed app. Instrucciones de descarga.Download instructions.

Requisitos previosPrerequisites

Visual Studio 2019 con las cargas de trabajo siguientes:Visual Studio 2019 with the following workloads:

  • Desarrollo de ASP.NET y webASP.NET and web development
  • Desarrollo multiplataforma de .NET Core.NET Core cross-platform development

Familiaridad con Razor Pages.Familiarity with Razor Pages. Los programadores nuevos deben completar Introducción a Razor Pages en ASP.NET Core antes de empezar esta serie.New programmers should complete Get started with Razor Pages before starting this series.

Solución de problemasTroubleshooting

Si experimenta un problema que no puede resolver, por lo general podrá encontrar la solución si compara el código con el proyecto completado.If you run into a problem you can't resolve, you can generally find the solution by comparing your code to the completed project. Una buena forma de obtener ayuda consiste en publicar una pregunta en StackOverflow.com para ASP.NET Core o EF Core.A good way to get help is by posting a question to StackOverflow.com for ASP.NET Core or EF Core.

La aplicación web Contoso UniversityThe Contoso University web app

La aplicación compilada en estos tutoriales es un sitio web básico de una universidad.The app built in these tutorials is a basic university web site.

Los usuarios pueden ver y actualizar la información de estudiantes, cursos e instructores.Users can view and update student, course, and instructor information. Estas son algunas de las pantallas que se crean en el tutorial.Here are a few of the screens created in the tutorial.

Página de índice de Students

Página de edición de estudiantes

El estilo de la interfaz de usuario de este sitio se mantiene fiel a lo que generan las plantillas integradas.The UI style of this site is close to what's generated by the built-in templates. El tutorial se centra en EF Core con Razor Pages, no en la interfaz de usuario.The tutorial focus is on EF Core with Razor Pages, not the UI.

Creación de la aplicación web de Razor Pages ContosoUniversityCreate the ContosoUniversity Razor Pages web app

  • En el menú Archivo de Visual Studio, seleccione Nuevo > Proyecto.From the Visual Studio File menu, select New > Project.
  • Cree una aplicación web de ASP.NET Core.Create a new ASP.NET Core Web Application. Asigne el nombre ContosoUniversity al proyecto.Name the project ContosoUniversity. Es importante que el nombre del proyecto sea ContosoUniversity para que coincidan los espacios de nombres al copiar y pegar el código.It's important to name the project ContosoUniversity so the namespaces match when code is copy/pasted.
  • Seleccione ASP.NET Core 2.1 en la lista desplegable y, luego, Aplicación web.Select ASP.NET Core 2.1 in the dropdown, and then select Web Application.

Para ver las imágenes de los pasos anteriores, consulte Creación de una aplicación web de Razor.For images of the preceding steps, see Create a Razor web app. Ejecutar la aplicación.Run the app.

Configurar el estilo del sitioSet up the site style

Con algunos cambios se configura el menú del sitio, el diseño y la página principal.A few changes set up the site menu, layout, and home page. Actualice Pages/Shared/_Layout.cshtml con los cambios siguientes:Update Pages/Shared/_Layout.cshtml with the following changes:

  • Cambie todas las repeticiones de "ContosoUniversity" por "Contoso University".Change each occurrence of "ContosoUniversity" to "Contoso University". Hay tres repeticiones.There are three occurrences.

  • Agregue entradas de menú para Students, Courses, Instructors y Departments, y elimine la entrada de menú Contact.Add menu entries for Students, Courses, Instructors, and Departments, and delete the Contact menu entry.

Los cambios aparecen resaltados.The changes are highlighted. (No se muestra todo el marcado).(All the markup is not displayed.)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] : Contoso University</title>

    <environment include="Development">
        <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" />
        <link rel="stylesheet" href="~/css/site.css" />
    </environment>
    <environment exclude="Development">
        <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css"
              asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
              asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" />
        <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
    </environment>
</head>
<body>
    <nav class="navbar navbar-inverse navbar-fixed-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a asp-page="/Index" class="navbar-brand">Contoso University</a>
            </div>
            <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li><a asp-page="/Index">Home</a></li>
                    <li><a asp-page="/About">About</a></li>
                    <li><a asp-page="/Students/Index">Students</a></li>
                    <li><a asp-page="/Courses/Index">Courses</a></li>
                    <li><a asp-page="/Instructors/Index">Instructors</a></li>
                    <li><a asp-page="/Departments/Index">Departments</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <partial name="_CookieConsentPartial" />

    <div class="container body-content">
        @RenderBody()
        <hr />
        <footer>
            <p>&copy; 2018 : Contoso University</p>
        </footer>
    </div>

    @*Remaining markup not shown for brevity.*@

En Pages/Index.cshtml, reemplace el contenido del archivo con el código siguiente para reemplazar el texto sobre ASP.NET y MVC con texto sobre esta aplicación:In Pages/Index.cshtml, replace the contents of the file with the following code to replace the text about ASP.NET and MVC with text about this app:

@page
@model IndexModel
@{
    ViewData["Title"] = "Home page";
}

<div class="jumbotron">
    <h1>Contoso University</h1>
</div>
<div class="row">
    <div class="col-md-4">
        <h2>Welcome to Contoso University</h2>
        <p>
            Contoso University is a sample application that
            demonstrates how to use Entity Framework Core in an
            ASP.NET Core Razor Pages web app.
        </p>
    </div>
    <div class="col-md-4">
        <h2>Build it from scratch</h2>
        <p>You can build the application by following the steps in a series of tutorials.</p>
        <p>
            <a class="btn btn-default"
               href="https://docs.microsoft.com/aspnet/core/data/ef-rp/intro">
                See the tutorial &raquo;
            </a>
        </p>
    </div>
    <div class="col-md-4">
        <h2>Download it</h2>
        <p>You can download the completed project from GitHub.</p>
        <p>
            <a class="btn btn-default"
               href="https://github.com/dotnet/AspNetCore.Docs/tree/master/aspnetcore/data/ef-rp/intro/samples/">
                See project source code &raquo;
            </a>
        </p>
    </div>
</div>

Crear el modelo de datosCreate the data model

Cree las clases de entidad para la aplicación Contoso University.Create entity classes for the Contoso University app. Comience con las tres entidades siguientes:Start with the following three entities:

Diagrama del modelo de datos Course-Enrollment-Student

Hay una relación uno a varios entre las entidades Student y Enrollment.There's a one-to-many relationship between Student and Enrollment entities. Hay una relación uno a varios entre las entidades Course y Enrollment.There's a one-to-many relationship between Course and Enrollment entities. Un estudiante se puede inscribir en cualquier número de cursos.A student can enroll in any number of courses. Un curso puede tener cualquier número de alumnos inscritos.A course can have any number of students enrolled in it.

En las secciones siguientes, se crea una clase para cada una de estas entidades.In the following sections, a class for each one of these entities is created.

La entidad StudentThe Student entity

Diagrama de la entidad Student

Cree una carpeta Models.Create a Models folder. En la carpeta Models, cree un archivo de clase denominado Student.cs con el código siguiente:In the Models folder, create a class file named Student.cs with the following code:

using System;
using System.Collections.Generic;

namespace ContosoUniversity.Models
{
    public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
}

La propiedad ID se convierte en la columna de clave principal de la tabla de base de datos (DB) que corresponde a esta clase.The ID property becomes the primary key column of the database (DB) table that corresponds to this class. De forma predeterminada, EF Core interpreta como la clave principal una propiedad que se denomine ID o classnameID.By default, EF Core interprets a property that's named ID or classnameID as the primary key. En classnameID, classname es el nombre de la clase.In classnameID, classname is the name of the class. En el ejemplo anterior, la clave principal alternativa que se reconoce de forma automática es StudentID.The alternative automatically recognized primary key is StudentID in the preceding example.

La propiedad Enrollments es una propiedad de navegación.The Enrollments property is a navigation property. Las propiedades de navegación se vinculan a otras entidades relacionadas con esta entidad.Navigation properties link to other entities that are related to this entity. En este caso, la propiedad Enrollments de una Student entity contiene todas las entidades Enrollment que están relacionadas con esa entidad Student.In this case, the Enrollments property of a Student entity holds all of the Enrollment entities that are related to that Student. Por ejemplo, si una fila Student de la base de datos tiene dos filas Enrollment relacionadas, la propiedad de navegación Enrollments contiene esas dos entidades Enrollment.For example, if a Student row in the DB has two related Enrollment rows, the Enrollments navigation property contains those two Enrollment entities. Una fila Enrollment relacionada es la que contiene el valor de clave principal de ese estudiante en la columna StudentID.A related Enrollment row is a row that contains that student's primary key value in the StudentID column. Por ejemplo, suponga que el estudiante con ID=1 tiene dos filas en la tabla Enrollment.For example, suppose the student with ID=1 has two rows in the Enrollment table. La tabla Enrollment tiene dos filas con StudentID = 1.The Enrollment table has two rows with StudentID = 1. StudentID es una clave externa en la tabla Enrollment que especifica el estudiante en la tabla Student.StudentID is a foreign key in the Enrollment table that specifies the student in the Student table.

Si una propiedad de navegación puede contener varias entidades, la propiedad de navegación debe ser un tipo de lista, como ICollection<T>.If a navigation property can hold multiple entities, the navigation property must be a list type, such as ICollection<T>. Se puede especificar ICollection<T>, o bien un tipo como List<T> o HashSet<T>.ICollection<T> can be specified, or a type such as List<T> or HashSet<T>. Cuando se usa ICollection<T>, EF Core crea una colección HashSet<T> de forma predeterminada.When ICollection<T> is used, EF Core creates a HashSet<T> collection by default. Las propiedades de navegación que contienen varias entidades proceden de relaciones de varios a varios y uno a varios.Navigation properties that hold multiple entities come from many-to-many and one-to-many relationships.

La entidad EnrollmentThe Enrollment entity

Diagrama de la entidad Enrollment

En la carpeta Models, cree Enrollment.cs con el código siguiente:In the Models folder, create Enrollment.cs with the following code:

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 Course Course { get; set; }
        public Student Student { get; set; }
    }
}

La propiedad EnrollmentID es la clave principal.The EnrollmentID property is the primary key. En esta entidad se usa el patrón classnameID en lugar de ID como en la entidad Student.This entity uses the classnameID pattern instead of ID like the Student entity. Normalmente, los desarrolladores eligen un patrón y lo usan en todo el modelo de datos.Typically developers choose one pattern and use it throughout the data model. En un tutorial posterior, se muestra el uso de ID sin un nombre de clase para facilitar la implementación de la herencia en el modelo de datos.In a later tutorial, using ID without classname is shown to make it easier to implement inheritance in the data model.

La propiedad Grade es una enum.The Grade property is an enum. El signo de interrogación después de la declaración de tipo Grade indica que la propiedad Grade acepta valores NULL.The question mark after the Grade type declaration indicates that the Grade property is nullable. 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.A grade that's null is different from a zero grade -- null means a grade isn't known or hasn't been assigned yet.

La propiedad StudentID es una clave externa y la propiedad de navegación correspondiente es Student.The StudentID property is a foreign key, and the corresponding navigation property is Student. Una entidad Enrollment está asociada con una entidad Student, por lo que la propiedad contiene una única entidad Student.An Enrollment entity is associated with one Student entity, so the property contains a single Student entity. La entidad Student difiere de la propiedad de navegación Student.Enrollments, que contiene varias entidades Enrollment.The Student entity differs from the Student.Enrollments navigation property, which contains multiple Enrollment entities.

La propiedad CourseID es una clave externa y la propiedad de navegación correspondiente es Course.The CourseID property is a foreign key, and the corresponding navigation property is Course. Una entidad Enrollment está asociada con una entidad Course.An Enrollment entity is associated with one Course entity.

EF Core interpreta una propiedad como una clave externa si se denomina <navigation property name><primary key property name>.EF Core interprets a property as a foreign key if it's named <navigation property name><primary key property name>. Por ejemplo, StudentID para la propiedad de navegación Student, puesto que la clave principal de la entidad Student es ID.For example,StudentID for the Student navigation property, since the Student entity's primary key is ID. Las propiedades de clave externa también se pueden denominar <primary key property name>.Foreign key properties can also be named <primary key property name>. Por ejemplo CourseID, dado que la clave principal de la entidad Course es CourseID.For example, CourseID since the Course entity's primary key is CourseID.

La entidad CourseThe Course entity

Diagrama de la entidad Course

En la carpeta Models, cree Course.cs con el código siguiente:In the Models folder, create Course.cs with the following code:

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 ICollection<Enrollment> Enrollments { get; set; }
    }
}

La propiedad Enrollments es una propiedad de navegación.The Enrollments property is a navigation property. Una entidad Course puede estar relacionada con cualquier número de entidades Enrollment.A Course entity can be related to any number of Enrollment entities.

El atributo DatabaseGenerated permite que la aplicación especifique la clave principal en lugar de hacer que la base de datos la genere.The DatabaseGenerated attribute allows the app to specify the primary key rather than having the DB generate it.

Aplicación de scaffolding al modelo de alumnosScaffold the student model

En esta sección, se aplica scaffolding al modelo de alumnos.In this section, the student model is scaffolded. Es decir, la herramienta de scaffolding genera páginas para las operaciones de creación, lectura, actualización y eliminación (CRUD) del modelo de alumnos.That is, the scaffolding tool produces pages for Create, Read, Update, and Delete (CRUD) operations for the student model.

  • Compile el proyecto.Build the project.
  • Cree la carpeta Pages/Students.Create the Pages/Students folder.
  • En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Pages/Students > Agregar > Nuevo elemento con scaffold.In Solution Explorer, right click on the Pages/Students folder > Add > New Scaffolded Item.
  • En el cuadro de diálogo Agregar scaffold, seleccione Páginas de Razor que usan Entity Framework (CRUD) > AGREGAR.In the Add Scaffold dialog, select Razor Pages using Entity Framework (CRUD) > ADD.

Complete el cuadro de diálogo para agregar instancias de Razor Pages que usan Entity Framework (CRUD) :Complete the Add Razor Pages using Entity Framework (CRUD) dialog:

  • En la lista desplegable Clase de modelo, seleccione Student (ContosoUniversity.Models) .In the Model class drop-down, select Student (ContosoUniversity.Models).
  • En la fila Clase de contexto de datos, haga clic en el signo + (más) y cambie el nombre generado por ContosoUniversity.Models.SchoolContext.In the Data context class row, select the + (plus) sign and change the generated name to ContosoUniversity.Models.SchoolContext.
  • En la lista desplegable Clase de contexto de datos, seleccione ContosoUniversity.Models.SchoolContextIn the Data context class drop-down, select ContosoUniversity.Models.SchoolContext
  • Seleccione Agregar.Select Add.

Cuadro de diálogo CRUD

Si tiene algún problema con el paso anterior, consulte Aplicar scaffolding al modelo de película.See Scaffold the movie model if you have a problem with the preceding step.

El proceso de scaffolding ha creado y cambiado los archivos siguientes:The scaffold process created and changed the following files:

Archivos creadosFiles created

  • Pages/Students Create, Delete, Details, Edit, Index.Pages/Students Create, Delete, Details, Edit, Index.
  • Data/SchoolContext.csData/SchoolContext.cs

Actualizaciones de archivosFile updates

  • Startup.cs: en la sección siguiente se detallan los cambios realizados en este archivo.Startup.cs : Changes to this file are detailed in the next section.
  • appsettings.json : se agrega la cadena de conexión que se usa para conectarse a una base de datos local.appsettings.json : The connection string used to connect to a local database is added.

Examinar el contexto registrado con la inserción de dependenciasExamine the context registered with dependency injection

ASP.NET Core integra la inserción de dependencias.ASP.NET Core is built with dependency injection. Los servicios (como el contexto de base de datos de EF Core) se registran con inserción de dependencias durante el inicio de la aplicación.Services (such as the EF Core DB context) are registered with dependency injection during application startup. Estos servicios se proporcionan a los componentes que los necesitan (como Razor Pages) a través de parámetros de constructor.Components that require these services (such as Razor Pages) are provided these services via constructor parameters. El código de constructor que obtiene una instancia de contexto de base de datos se muestra más adelante en el tutorial.The constructor code that gets a db context instance is shown later in the tutorial.

La herramienta de scaffolding creó de forma automática un contexto de base de datos y lo registró con el contenedor de inserción de dependencias.The scaffolding tool automatically created a DB Context and registered it with the dependency injection container.

Examine el método ConfigureServices de Startup.cs.Examine the ConfigureServices method in Startup.cs. El proveedor de scaffolding ha agregado la línea resaltada:The highlighted line was added by the scaffolder:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for 
        //non -essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

    services.AddDbContext<SchoolContext>(options =>
       options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
}

El nombre de la cadena de conexión se pasa al contexto mediante una llamada a un método en un objeto DbContextOptions.The name of the connection string is passed in to the context by calling a method on a DbContextOptions object. Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la cadena de conexión desde el archivo appsettings.json .For local development, the ASP.NET Core configuration system reads the connection string from the appsettings.json file.

Actualización de mainUpdate main

En Program.cs, modifique el método Main para que haga lo siguiente:In Program.cs, modify the Main method to do the following:

  • Obtener una instancia del contexto de base de datos desde el contenedor de inserción de dependencias.Get a DB context instance from the dependency injection container.
  • Llame a EnsureCreated.Call the EnsureCreated.
  • Elimine el contexto cuando finalice el método EnsureCreated.Dispose the context when the EnsureCreated method completes.

En el código siguiente se muestra el archivo Program.cs actualizado.The following code shows the updated Program.cs file.

using ContosoUniversity.Models;                   // SchoolContext
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;   // CreateScope
using Microsoft.Extensions.Logging;
using System;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateWebHostBuilder(args).Build();

            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;

                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    context.Database.EnsureCreated();
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred creating the DB.");
                }
            }

            host.Run();
        }

        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>();
    }
}

EnsureCreated garantiza la existencia de la base de datos para el contexto.EnsureCreated ensures that the database for the context exists. Si existe, no se realiza ninguna acción.If it exists, no action is taken. Si no existe, se crean la base de datos y todo su esquema.If it does not exist, then the database and all its schema are created. En EnsureCreated no se usan migraciones para crear la base de datos.EnsureCreated does not use migrations to create the database. Una base de datos que se cree con EnsureCreated no se podrá actualizar más adelante mediante las migraciones.A database that is created with EnsureCreated cannot be later updated using migrations.

EnsureCreated se llama durante el inicio de la aplicación, lo que permite el flujo de trabajo siguiente:EnsureCreated is called on app start, which allows the following work flow:

  • Se elimina la base de datos.Delete the DB.
  • Se cambia el esquema de base de datos (por ejemplo, se agrega un campo EmailAddress).Change the DB schema (for example, add an EmailAddress field).
  • Ejecute la aplicación.Run the app.
  • EnsureCreated crea una base de datos con la columna EmailAddress.EnsureCreated creates a DB with theEmailAddress column.

EnsureCreated es útil al principio del desarrollo, cuando el esquema evoluciona rápidamente.EnsureCreated is convenient early in development when the schema is rapidly evolving. Más adelante, en el tutorial se elimina la base de datos y se usan las migraciones.Later in the tutorial the DB is deleted and migrations are used.

Prueba de la aplicaciónTest the app

Ejecute la aplicación y acepte la directiva de cookies.Run the app and accept the cookie policy. Esta aplicación no conserva información de carácter personal.This app doesn't keep personal information. Puede obtener más información sobre la directiva de cookies en Compatibilidad con el Reglamento general de protección de datos (RGPD) de la UE.You can read about the cookie policy at EU General Data Protection Regulation (GDPR) support.

  • Haga clic en el vínculo Students y, después, en Crear nuevo.Select the Students link and then Create New.
  • Pruebe los vínculos Edit, Details y Delete.Test the Edit, Details, and Delete links.

Examinar el contexto de base de datos SchoolContextExamine the SchoolContext DB context

La clase principal que coordina la funcionalidad de EF Core para un modelo de datos determinado es la clase de contexto de base de datos.The main class that coordinates EF Core functionality for a given data model is the DB context class. El contexto de datos se deriva de Microsoft.EntityFrameworkCore.DbContext.The data context is derived from Microsoft.EntityFrameworkCore.DbContext. En el contexto de datos se especifica qué entidades se incluyen en el modelo de datos.The data context specifies which entities are included in the data model. En este proyecto, la clase se denomina SchoolContext.In this project, the class is named SchoolContext.

Actualice SchoolContext.cs con el código siguiente:Update SchoolContext.cs with the following code:

using Microsoft.EntityFrameworkCore;

namespace ContosoUniversity.Models
{
    public class SchoolContext : DbContext
    {
        public SchoolContext(DbContextOptions<SchoolContext> options)
            : base(options)
        {
        }

        public DbSet<Student> Student { get; set; }
        public DbSet<Enrollment> Enrollment { get; set; }
        public DbSet<Course> Course { get; set; }
    }
}

El código resaltado crea una propiedad DbSet<TEntity> para cada conjunto de entidades.The highlighted code creates a DbSet<TEntity> property for each entity set. En la terminología de EF Core:In EF Core terminology:

  • Un conjunto de entidades normalmente se corresponde a una tabla de base de datos.An entity set typically corresponds to a DB table.
  • Una entidad se corresponde con una fila de la tabla.An entity corresponds to a row in the table.

DbSet<Enrollment> y DbSet<Course> se pueden omitir.DbSet<Enrollment> and DbSet<Course> could be omitted. EF Core las incluye implícitamente porque la entidad Student hace referencia a la entidad Enrollment y la entidad Enrollment hace referencia a la entidad Course.EF Core includes them implicitly because the Student entity references the Enrollment entity, and the Enrollment entity references the Course entity. Para este tutorial, conserve DbSet<Enrollment> y DbSet<Course> en el SchoolContext.For this tutorial, keep DbSet<Enrollment> and DbSet<Course> in the SchoolContext.

SQL Server Express LocalDBSQL Server Express LocalDB

La cadena de conexión especifica SQL Server LocalDB.The connection string specifies SQL Server LocalDB. LocalDB es una versión ligera del motor de base de datos de SQL Server Express y está dirigida al desarrollo de aplicaciones, no al uso en producción.LocalDB is a lightweight version of the SQL Server Express Database Engine and is intended for app development, not production use. LocalDB se inicia a petición y se ejecuta en modo de usuario, sin necesidad de una configuración compleja.LocalDB starts on demand and runs in user mode, so there's no complex configuration. De forma predeterminada, LocalDB crea archivos de base de datos .mdf en el directorio C:/Users/<user>.By default, LocalDB creates .mdf DB files in the C:/Users/<user> directory.

Agregar código para inicializar la base de datos con datos de pruebaAdd code to initialize the DB with test data

EF Core crea una base de datos vacía.EF Core creates an empty DB. En esta sección, se escribe un método Initialize para rellenarlo con datos de prueba.In this section, an Initialize method is written to populate it with test data.

En la carpeta Data, cree un archivo de clase denominado DbInitializer.cs y agregue el código siguiente:In the Data folder, create a new class file named DbInitializer.cs and add the following code:

using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Models
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Student.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
            new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
            };
            foreach (Student s in students)
            {
                context.Student.Add(s);
            }
            context.SaveChanges();

            var courses = new 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}
            };
            foreach (Course c in courses)
            {
                context.Course.Add(c);
            }
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
            new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
            new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
            new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
            new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
            new Enrollment{StudentID=3,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
            new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
            new Enrollment{StudentID=6,CourseID=1045},
            new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };
            foreach (Enrollment e in enrollments)
            {
                context.Enrollment.Add(e);
            }
            context.SaveChanges();
        }
    }
}

Nota: El código anterior usa Models para el espacio de nombres (namespace ContosoUniversity.Models) en lugar de Data.Note: The preceding code uses Models for the namespace (namespace ContosoUniversity.Models) rather than Data. Models es coherente con el código generado por el proveedor de scaffolding.Models is consistent with the scaffolder-generated code. Para obtener más información, consulte este problema de scaffolding de GitHub.For more information, see this GitHub scaffolding issue.

El código comprueba si hay estudiantes en la base de datos.The code checks if there are any students in the DB. Si no hay alumnos en la base de datos, se inicializa con datos de prueba.If there are no students in the DB, the DB is initialized with test data. Carga los datos de prueba en matrices en lugar de colecciones List<T> para optimizar el rendimiento.It loads test data into arrays rather than List<T> collections to optimize performance.

El método EnsureCreated crea automáticamente la base de datos para el contexto de base de datos.The EnsureCreated method automatically creates the DB for the DB context. Si la base de datos existe, EnsureCreated vuelve sin modificarla.If the DB exists, EnsureCreated returns without modifying the DB.

En Program.cs, modifique el método Main para que llame a Initialize:In Program.cs, modify the Main method to call Initialize:

public class Program
{
    public static void Main(string[] args)
    {
        var host = CreateWebHostBuilder(args).Build();

        using (var scope = host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;

            try
            {
                var context = services.GetRequiredService<SchoolContext>();
                // using ContosoUniversity.Data; 
                DbInitializer.Initialize(context);
            }
            catch (Exception ex)
            {
                var logger = services.GetRequiredService<ILogger<Program>>();
                logger.LogError(ex, "An error occurred creating the DB.");
            }
        }

        host.Run();
    }

    public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>();
}

Detenga la aplciación si se está ejecutando y ejecute el comando siguiente en la Consola del Administrador de paquetes (PMC):Stop the app if it's running, and run the following command in the Package Manager Console (PMC):

Drop-Database

Ver la base de datosView the DB

El nombre de la base de datos se genera a partir del nombre de contexto proporcionado anteriormente, más un guión y un GUID.The database name is generated from the context name you provided earlier plus a dash and a GUID. Por lo tanto, el nombre de la base de datos será "SchoolContext-{GUID}".Thus, the database name will be "SchoolContext-{GUID}". El GUID será diferente para cada usuario.The GUID will be different for each user. Abra el Explorador de objetos de SQL Server (SSOX) desde el menú Vista en Visual Studio.Open SQL Server Object Explorer (SSOX) from the View menu in Visual Studio. En SSOX, haga clic en (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID} .In SSOX, click (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}.

Expanda el nodo Tablas.Expand the Tables node.

Haga clic con el botón derecho en la tabla Student y haga clic en Ver datos para ver las columnas que se crearon y las filas que se insertaron en la tabla.Right-click the Student table and click View Data to see the columns created and the rows inserted into the table.

Código asincrónicoAsynchronous code

La programación asincrónica es el modo predeterminado de ASP.NET Core y EF Core.Asynchronous programming is the default mode for ASP.NET Core and EF Core.

Un servidor web tiene un número limitado de subprocesos disponibles y, en situaciones de carga alta, es posible que todos los subprocesos disponibles estén en uso.A web server has a limited number of threads available, and in high load situations all of the available threads might be in use. Cuando esto ocurre, el servidor no puede procesar nuevas solicitudes hasta que los subprocesos se liberen.When that happens, the server can't process new requests until the threads are freed up. Con el código sincrónico, se pueden acumular muchos subprocesos mientras no estén realizando ningún trabajo porque están a la espera de que finalice la E/S.With synchronous code, many threads may be tied up while they aren't actually doing any work because they're waiting for I/O to complete. Con el código asincrónico, cuando un proceso está a la espera de que finalice la E/S, se libera su subproceso para el que el servidor lo use para el procesamiento de otras solicitudes.With asynchronous code, when a process is waiting for I/O to complete, its thread is freed up for the server to use for processing other requests. Como resultado, el código asincrónico permite que los recursos de servidor se usen de forma más eficaz, y el servidor está habilitado para administrar más tráfico sin retrasos.As a result, asynchronous code enables server resources to be used more efficiently, and the server is enabled to handle more traffic without delays.

El código asincrónico introduce una pequeña cantidad de sobrecarga en tiempo de ejecución.Asynchronous code does introduce a small amount of overhead at run time. En situaciones de poco tráfico, la disminución del rendimiento es insignificante, mientras que en situaciones de tráfico elevado, la posible mejora del rendimiento es importante.For low traffic situations, the performance hit is negligible, while for high traffic situations, the potential performance improvement is substantial.

En el código siguiente, la palabra clave async, el valor devuelto Task<T>, la palabra clave await y el método ToListAsync hacen que el código se ejecute de forma asincrónica.In the following code, the async keyword, Task<T> return value, await keyword, and ToListAsync method make the code execute asynchronously.

public async Task OnGetAsync()
{
    Student = await _context.Student.ToListAsync();
}
  • La palabra clave async indica al compilador que:The async keyword tells the compiler to:

    • Genere devoluciones de llamada para partes del cuerpo del método.Generate callbacks for parts of the method body.
    • Cree automáticamente el objeto Task que se devuelve.Automatically create the Task object that's returned. Para más información, vea Tipo de valor devuelto Task.For more information, see Task Return Type.
  • El tipo devuelto implícito Task representa el trabajo en curso.The implicit return type Task represents ongoing work.

  • La palabra clave await hace que el compilador divida el método en dos partes.The await keyword causes the compiler to split the method into two parts. La primera parte termina con la operación que se inició de forma asincrónica.The first part ends with the operation that's started asynchronously. La segunda parte se coloca en un método de devolución de llamada que se llama cuando finaliza la operación.The second part is put into a callback method that's called when the operation completes.

  • ToListAsync es la versión asincrónica del método de extensión ToList.ToListAsync is the asynchronous version of the ToList extension method.

Algunos aspectos que tener en cuenta al escribir código asincrónico en el que se usa EF Core son los siguientes:Some things to be aware of when writing asynchronous code that uses EF Core:

  • Solo se ejecutan de forma asincrónica las instrucciones que hacen que las consultas o los comandos se envíen a la base de datos.Only statements that cause queries or commands to be sent to the DB are executed asynchronously. Esto incluye ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync y SaveChangesAsync.That includes, ToListAsync, SingleOrDefaultAsync, FirstOrDefaultAsync, and SaveChangesAsync. No incluye las instrucciones que solo cambian una IQueryable, como var students = context.Students.Where(s => s.LastName == "Davolio").It doesn't include statements that just change an IQueryable, such as var students = context.Students.Where(s => s.LastName == "Davolio").
  • Un contexto de EF Core no es seguro para subprocesos: no intente realizar varias operaciones en paralelo.An EF Core context isn't thread safe: don't try to do multiple operations in parallel.
  • Para aprovechar las ventajas de rendimiento del código asincrónico, compruebe que en los paquetes de biblioteca (por ejemplo para paginación) se usa async si llaman a métodos de EF Core que envían consultas a la base de datos.To take advantage of the performance benefits of async code, verify that library packages (such as for paging) use async if they call EF Core methods that send queries to the DB.

Para obtener más información sobre la programación asincrónica en .NET, vea Programación asincrónica y Programación asincrónica con async y await.For more information about asynchronous programming in .NET, see Async Overview and Asynchronous programming with async and await.

En el siguiente tutorial, se examinan las operaciones CRUD (crear, leer, actualizar y eliminar) básicas.In the next tutorial, basic CRUD (create, read, update, delete) operations are examined.

Recursos adicionalesAdditional resources