Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulte la directiva de compatibilidad de .NET y .NET Core. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Importante
Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.
Para la versión actual, consulte la versión de .NET 9 de este artículo.
Por Tom Dykstra, Jeremy Likness y Jon P Smith
Este es el primero de una serie de tutoriales que muestran cómo usar Entity Framework (EF) Core en una aplicación ASP.NET Core Razor Pages . En el tutorial se crea un sitio web de una universidad ficticia, Contoso University. El sitio incluye funciones como la admisión de alumnos, la creación de cursos y las asignaciones de instructores. En el tutorial se usa el enfoque de Code First. Para obtener información sobre cómo seguir este tutorial mediante el primer enfoque de la base de datos, consulte este problema de Github.
Descargue o vea la aplicación completada.Descargue las instrucciones.
Requisitos previos
- Si no está familiarizado con Pages, consulte la serie de tutoriales RazorRazor Pages antes de comenzar esta.
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web.
Motores de bases de datos
Las instrucciones de Visual Studio usan SQL Server LocalDB, una versión de SQL Server Express que solo se ejecuta en Windows.
Solución de problemas
Si tiene un problema que no puede resolver, compare el código con el proyecto completado. Una buena manera de obtener ayuda es publicar una pregunta en StackOverflow.com, usando la etiqueta ASP.NET Core o la EF Core etiqueta .
La aplicación de ejemplo
La aplicación compilada en estos tutoriales es un sitio web básico de una universidad. Los usuarios pueden ver y actualizar la información de estudiantes, cursos e instructores. Estas son algunas de las pantallas que se crean en el tutorial.
El estilo de la interfaz de usuario de este sitio se basa en las plantillas de proyecto integradas. El enfoque del tutorial es cómo usar EF Core con ASP.NET Core, no cómo personalizar la interfaz de usuario.
Opcional: compilación de la descarga de ejemplo
Este paso es opcional. Se recomienda compilar la aplicación completa cuando tenga problemas que no pueda resolver. Si tiene un problema que no puede resolver, compare el código con el proyecto completado. Descargue las instrucciones.
Seleccione ContosoUniversity.csproj
para abrir el proyecto.
Compile el proyecto.
En la Consola del administrador de paquetes (PMC), ejecute el comando siguiente:
Update-Database
Ejecute el proyecto para inicializar la base de datos.
Creación del proyecto de aplicación web
Inicie Visual Studio 2022 y seleccione Crear un nuevo proyecto.
En el cuadro de diálogo Crear un nuevo proyecto , seleccione ASP.NET Core Web App y, a continuación, seleccione Siguiente.
En el cuadro de diálogo Configurar el nuevo proyecto , escriba
ContosoUniversity
en Nombre del proyecto. Es importante asignar un nombre al proyecto ContosoUniversity, incluida la coincidencia de mayúsculas, por lo que los espacios de nombres coincidirán al copiar y pegar código de ejemplo.Seleccione Siguiente.
En el cuadro de diálogo Información adicional , seleccione .NET 6.0 (soporte técnico a largo plazo) y, a continuación, seleccione Crear.
Configurar el estilo del sitio
Copie y pegue el siguiente código en el archivo Pages/Shared/_Layout.cshtml
:
<!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" asp-append-version="true" />
<link rel="stylesheet" href="~/ContosoUniversity.styles.css" asp-append-version="true" />
</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-bs-toggle="collapse" data-bs-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 justify-content-between">
<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">
© 2021 - 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>
@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. En el código anterior se realizan los cambios siguientes:
- Todas las repeticiones de "ContosoUniversity" por "Contoso University". Hay tres ocurrencias.
- Se eliminan las entradas del menú Home y Privacy.
- Se agregan entradas para About, Students, Courses, Instructors y Departments.
En Pages/Index.cshtml
, reemplace el contenido del archivo por el código siguiente:
@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/main/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.
Ejecute la aplicación para comprobar que aparece la página principal.
El modelo de datos
En las secciones siguientes se crea un modelo de datos:
Un alumno se puede inscribir en cualquier número de cursos y un curso puede tener cualquier número de alumnos inscritos.
La entidad Student
- Cree una carpeta Models en la carpeta del proyecto.
- Cree el archivo
Models/Student.cs
con el siguiente código: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. De forma predeterminada, EF Core interpreta como la clave principal una propiedad que se denomine ID
o classnameID
. Por tanto, el nombre que se reconoce de forma automática para la clave principal de la clase Student
es StudentID
. Para obtener más información, consulte EF Core - Claves.
La propiedad Enrollments
es una propiedad de navegación. Las propiedades de navegación contienen otras entidades relacionadas con esta entidad. En este caso, la propiedad Enrollments
de una entidad Student
contiene todas las entidades Enrollment
que están relacionadas con esa instancia de 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.
En la base de datos, una fila Enrollment se relaciona con una fila Student si su columna StudentID
contiene el valor de identificador del estudiante. Por ejemplo, imagine que una fila Student tiene el identificador 1. Las filas Enrollment relacionadas tendrán StudentID
= 1.
StudentID
es una clave externa en la tabla de Inscripción.
La propiedad Enrollments
se define como ICollection<Enrollment>
porque puede haber varias entidades Enrollment relacionadas. Puede usar otros tipos de colección, como List<Enrollment>
o HashSet<Enrollment>
. Cuando se usa ICollection<Enrollment>
, EF Core crea una colección HashSet<Enrollment>
de forma predeterminada.
La entidad Enrollment
Cree el archivo Models/Enrollment.cs
con el siguiente código:
using System.ComponentModel.DataAnnotations;
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; }
[DisplayFormat(NullDisplayText = "No grade")]
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. Para un modelo de datos de producción, muchos desarrolladores eligen un patrón y lo usan de forma coherente. En este tutorial se usan los dos simplemente para ilustrar el trabajo. El uso de ID
sin classname
facilita la implementación de algunos tipos de cambios del modelo de datos.
La propiedad Grade
es una enum
. El signo de interrogación después de la declaración de tipo Grade
indica que la propiedad Grade
puede ser nula. Una calificación que sea NULL es diferente de una calificación que sea cero; NULL significa que no se conoce una calificación o que todavía no se ha asignado.
La propiedad StudentID
es una clave externa y la propiedad de navegación correspondiente es Student
. Una entidad Enrollment
está asociada con una entidad Student
, por lo que la propiedad contiene una única entidad Student
.
La propiedad CourseID
es una clave externa y la propiedad de navegación correspondiente es Course
. Una entidad Enrollment
está asociada con una entidad Course
.
EF Core interpreta una propiedad como una clave externa si se denomina <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
. Las propiedades de clave externa también se pueden denominar <primary key property name>
. Por ejemplo CourseID
, dado que la clave principal de la entidad Course
es CourseID
.
La entidad Course
Cree el archivo Models/Course.cs
con el siguiente código:
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. Una entidad Course
puede estar relacionada con cualquier número de entidades Enrollment
.
El atributo DatabaseGenerated
permite que la aplicación especifique la clave principal en lugar de hacer que la base de datos la genere.
Compile la aplicación. El compilador genera varias advertencias sobre cómo se controlan los valores null
. Consulte este problema de GitHub, tipos de referencia que aceptan valores NULL y Tutorial: Expresar la intención de diseño con más claridad con tipos de referencia que aceptan valores NULL y que no aceptan valores NULL para obtener más información.
Para eliminar las advertencias de los tipos de referencia que aceptan valores NULL, quite la siguiente línea del archivo ContosoUniversity.csproj
:
<Nullable>enable</Nullable>
Actualmente, el motor de scaffolding no admite tipos de referencia que aceptan valores NULL, por lo que los modelos usados en scaffold tampoco pueden.
Quite la anotación de tipo de referencia que acepta valores NULL ?
de public string? RequestId { get; set; }
en Pages/Error.cshtml.cs
para que el proyecto se compile sin advertencias del compilador.
Scaffolding de las páginas Student
En esta sección, se usa la herramienta de scaffolding de ASP.NET Core para generar lo siguiente:
- Una clase EF Core de
DbContext
. El contexto es la clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos determinado. Se deriva de la clase Microsoft.EntityFrameworkCore.DbContext. -
Razor Pages que controlan las operaciones de creación, lectura, actualización y eliminación (CRUD) de la entidad
Student
.
- Cree una carpeta Pages/Students .
- En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Pages/Students y seleccione Agregar>nuevo elemento con scaffolding.
- En el cuadro de diálogo Agregar nuevo elemento de andamiaje:
- En la pestaña izquierda, seleccione Páginas comunes >> instaladasRazor.
- Seleccione Razor Páginas con Entity Framework (CRUD)>ADD.
- En el cuadro de diálogo Agregar Razor páginas mediante Entity Framework (CRUD):
- En la lista desplegable Clase de modelo , seleccione Student (ContosoUniversity.Models) .
- En la fila de la Clase de contexto de datos, seleccione el signo + (más).
- Cambie el nombre del contexto de datos para que acabe en
SchoolContext
en lugar deContosoUniversityContext
. Nombre del contexto actualizado:ContosoUniversity.Data.SchoolContext
. - Seleccione Agregar para terminar de agregar la clase de contexto de datos.
- Seleccione Agregar para finalizar el cuadro de diálogo Agregar Razor páginas .
- Cambie el nombre del contexto de datos para que acabe en
Los paquetes siguientes se instalan de forma automática:
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.
El proceso de scaffolding:
- Crea Razor páginas en la carpeta Pages/Students :
-
Create.cshtml
yCreate.cshtml.cs
-
Delete.cshtml
yDelete.cshtml.cs
-
Details.cshtml
yDetails.cshtml.cs
-
Edit.cshtml
yEdit.cshtml.cs
-
Index.cshtml
yIndex.cshtml.cs
-
- Crea
Data/SchoolContext.cs
. - Agrega el contexto a la inserción de dependencias en
Program.cs
. - Agrega una cadena de conexión de la base de datos a
appsettings.json
.
Cadena de conexión de base de datos
La herramienta de scaffolding genera una cadena de conexión en el archivo appsettings.json
.
La cadena de conexión especifica SQL Server LocalDB:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext-0e9;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. De forma predeterminada, LocalDB crea .mdf archivos en el C:/Users/<user>
directorio .
Actualización de la clase de contexto de base de datos
La clase principal que coordina la funcionalidad de EF Core para un modelo de datos determinado es la clase context de la base de datos. El contexto se deriva de Microsoft.EntityFrameworkCore.DbContext. En el contexto se especifica qué entidades se incluyen en el modelo de datos. En este proyecto, la clase se denomina SchoolContext
.
Actualice Data/SchoolContext.cs
con el siguiente código:
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. Para que el código de las páginas Razor coincida con el nombre nuevo de DBSet
, realice un cambio global de:
_context.Student.
a: _context.Students.
Hay ocho repeticiones.
Dado que un conjunto de entidades contiene varias entidades, muchos desarrolladores prefieren que los nombres de la propiedad DBSet
sean plurales.
El código resaltado:
- Crea una propiedad DbSet<TEntity> para cada conjunto de entidades. En terminología de EF Core:
- Un conjunto de entidades normalmente se corresponde a una tabla de base de datos.
- Una entidad se corresponde con una fila de la tabla.
- Llama a OnModelCreating.
OnModelCreating
:- se llama cuando
SchoolContext
se ha inicializado, pero antes de que el modelo se haya asegurado y utilizado para inicializar el contexto. - Es necesario porque, más adelante en el tutorial, la entidad
Student
tendrá referencias a las demás entidades.
- se llama cuando
Esperamos corregir este problema en una versión futura.
Program.cs
ASP.NET Core se basa en la inyección de dependencias. Los servicios como SchoolContext
se registran con inserción de dependencias durante el inicio de la aplicación. Estos servicios se proporcionan a los componentes que los necesitan, como páginas Razor, a través de parámetros de constructor. El código de constructor que obtiene una instancia de contexto de base de datos se muestra más adelante en el tutorial.
La herramienta de scaffolding ha registrado de forma automática la clase de contexto con el contenedor de inserción de dependencias.
El proveedor de scaffolding ha agregado las líneas resaltadas siguientes:
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.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. Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la cadena de conexión del appsettings.json
archivo o appsettings.Development.json
.
Incorporación del filtro de excepción de base de datos
Agregue AddDatabaseDeveloperPageExceptionFilter y UseMigrationsEndPoint, tal como se muestra en el código siguiente:
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
Agregue el paquete NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore .
En la Consola del administrador de paquetes, escriba esto para agregar el paquete NuGet:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
El paquete NuGet de Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
proporciona middleware de ASP.NET Core para páginas de error de Entity Framework Core. Este middleware ayuda a detectar y diagnosticar errores con migraciones de Entity Framework Core.
AddDatabaseDeveloperPageExceptionFilter
proporciona información de error útil en el entorno de desarrollo para errores en las migraciones de EF.
Creación de la base de datos
Actualice Program.cs
para crear la base de datos si no existe:
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("SchoolContext")));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
// DbInitializer.Initialize(context);
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
El método EnsureCreated no realiza ninguna acción si existe una base de datos para el contexto. Si no existe ninguna base de datos, se crean la base de datos y el esquema.
EnsureCreated
habilita el flujo de trabajo siguiente para controlar los cambios del modelo de datos:
- Se elimina la base de datos. Se pierden los datos existentes.
- Se cambia el modelo de datos. Por ejemplo, se agrega un campo
EmailAddress
. - Ejecutar la aplicación.
-
EnsureCreated
crea una base de datos con el esquema nuevo.
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. La situación es distinta cuando es necesario conservar los datos introducidos en la base de datos. En ese caso, use las migraciones.
Más adelante en la serie de tutoriales, se eliminará la base de datos creada por EnsureCreated
y se usarán las migraciones. Una base de datos creada por EnsureCreated
no se puede actualizar mediante migraciones.
Prueba de la aplicación
- Ejecutar la aplicación.
- Seleccione el enlace Students y luego Create New.
- Pruebe los vínculos Edit, Details y Delete.
Inicializar la base de datos
El método EnsureCreated
crea una base de datos vacía. En esta sección se agrega código que rellena la base de datos con datos de prueba.
Cree el archivo Data/DbInitializer.cs
con el siguiente código:
using ContosoUniversity.Models;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
// 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. Si no hay ningún alumno, agrega datos de prueba a la base de datos. Crea los datos de prueba en matrices en lugar de colecciones List<T>
para optimizar el rendimiento.
- En
Program.cs
, quite//
en la líneaDbInitializer.Initialize
:
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
DbInitializer.Initialize(context);
}
Detenga la aplicación si se está ejecutando y ejecute el siguiente comando en la consola del Administrador de paquetes (PMC):
Drop-Database -Confirm
Responda con
Y
para eliminar la base de datos.
- Reinicie la aplicación.
- Seleccione la página Students para ver los datos inicializados.
Consulta la base de datos
- Abra el Explorador de objetos de SQL Server (SSOX) en el menú Ver de Visual Studio.
- En SSOX, seleccione (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.
- Expanda el nodo Tablas .
- Haga clic con el botón derecho en la tabla Student y haga clic en Ver datos para ver las columnas creadas y las filas insertadas en la tabla.
- Haga clic con el botón derecho en la tabla Student y haga clic en Ver código para ver cómo se asigna el
Student
modelo al esquema de la tablaStudent
.
Métodos asincrónicos de EF en aplicaciones web de ASP.NET Core
La programación asincrónica es el modo predeterminado de ASP.NET Core y 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. Cuando esto ocurre, el servidor no puede procesar nuevas solicitudes hasta que los subprocesos se liberen. 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. 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. 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.
El código asincrónico introduce una pequeña cantidad de sobrecarga en tiempo de ejecución. 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.
En el código siguiente, la palabra clave async , Task
el valor devuelto, await
la palabra clave y ToListAsync
el método hacen que el código se ejecute de forma asincrónica.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
- La palabra clave
async
indica al compilador que:- Genere devoluciones de llamada para partes del cuerpo del método.
- Cree el objeto Task que se devuelve.
- El tipo devuelto
Task
representa el trabajo en curso. - La palabra clave
await
hace que el compilador divida el método en dos partes. La primera parte termina con la operación que se inició de forma asincrónica. La segunda parte se coloca en un método de devolución de llamada que se llama cuando finaliza la operación. -
ToListAsync
es la versión asincrónica del método de extensiónToList
.
Algunos aspectos que tener en cuenta al escribir código asincrónico en el que se usa EF Core son los siguientes:
- 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. Esto incluye
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
ySaveChangesAsync
. No incluye las instrucciones que solo cambian unaIQueryable
, comovar 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.
- 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.
Para obtener más información sobre la programación asincrónica en .NET, consulte Async Overview y Programación asincrónica con async y await.
Advertencia
La implementación asincrónica de Microsoft.Data.SqlClient tiene algunos problemas conocidos (#593, #601 y otros). Si observa problemas inesperados de rendimiento, pruebe a usar la ejecución del comando de sincronización en su lugar, especialmente cuando trabaje con valores binarios o de texto grandes.
Consideraciones de rendimiento
En general, una página web no debe cargar un número arbitrario de filas. Una consulta debe utilizar la paginación o un enfoque de limitación. Por ejemplo, la consulta anterior podría usar Take
para limitar las filas devueltas:
public async Task OnGetAsync()
{
Student = await _context.Students.Take(10).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.
La paginación se trata más adelante en el tutorial.
Para obtener más información, consulte Consideraciones de rendimiento (EF).
Pasos siguientes
Uso de SQLite para el desarrollo, SQL Server para producción
Este es el primero de una serie de tutoriales que muestran cómo usar Entity Framework (EF) Core en una aplicación ASP.NET Core Razor Pages . En el tutorial se crea un sitio web de una universidad ficticia, Contoso University. El sitio incluye funciones como la admisión de alumnos, la creación de cursos y las asignaciones de instructores. En el tutorial se usa el enfoque de Code First. Para obtener información sobre cómo seguir este tutorial mediante el primer enfoque de la base de datos, consulte este problema de Github.
Descargue o vea la aplicación completada.Descargue las instrucciones.
Requisitos previos
- Si no está familiarizado con Pages, consulte la serie de tutoriales RazorRazor Pages antes de comenzar esta.
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web.
Motores de bases de datos
Las instrucciones de Visual Studio usan SQL Server LocalDB, una versión de SQL Server Express que solo se ejecuta en Windows.
Solución de problemas
Si tiene un problema que no puede resolver, compare el código con el proyecto completado. Una buena manera de obtener ayuda es publicar una pregunta en StackOverflow.com, usando la etiqueta ASP.NET Core o la EF Core etiqueta .
La aplicación de ejemplo
La aplicación compilada en estos tutoriales es un sitio web básico de una universidad. Los usuarios pueden ver y actualizar la información de estudiantes, cursos e instructores. Estas son algunas de las pantallas que se crean en el tutorial.
El estilo de la interfaz de usuario de este sitio se basa en las plantillas de proyecto integradas. El enfoque del tutorial es cómo usar EF Core con ASP.NET Core, no cómo personalizar la interfaz de usuario.
Opcional: compilación de la descarga de ejemplo
Este paso es opcional. Se recomienda compilar la aplicación completa cuando tenga problemas que no pueda resolver. Si tiene un problema que no puede resolver, compare el código con el proyecto completado. Descargue las instrucciones.
Seleccione ContosoUniversity.csproj
para abrir el proyecto.
- Compile el proyecto.
- En la Consola del administrador de paquetes (PMC), ejecute el comando siguiente:
Update-Database
Ejecute el proyecto para inicializar la base de datos.
Creación del proyecto de aplicación web
- Inicie Visual Studio y seleccione Crear un nuevo proyecto.
- En el cuadro de diálogo Crear un nuevo proyecto, seleccione ASP.NET Core Web Application>Siguiente.
- En el cuadro de diálogo Configurar el nuevo proyecto , escriba
ContosoUniversity
en Nombre del proyecto. Es importante usar este nombre exacto, incluido el uso de mayúsculas, para que cadanamespace
coincida cuando se copie el código. - Seleccione Crear.
- En el cuadro de diálogo Crear una nueva aplicación web ASP.NET Core , seleccione:
- .NET Core y ASP.NET Core 5.0 en las listas desplegables.
- ASP.NET Core Web App.
-
Crear
Configurar el estilo del sitio
Copie y pegue el siguiente código en el archivo Pages/Shared/_Layout.cshtml
:
<!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">
© 2021 - 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>
El archivo de diseño establece el encabezado, el pie de página y el menú del sitio. En el código anterior se realizan los cambios siguientes:
- Todas las repeticiones de "ContosoUniversity" por "Contoso University". Hay tres ocurrencias.
- Se eliminan las entradas del menú Home y Privacy.
- Se agregan entradas para About, Students, Courses, Instructors y Departments.
En Pages/Index.cshtml
, reemplace el contenido del archivo por el código siguiente:
@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/main/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.
Ejecute la aplicación para comprobar que aparece la página principal.
El modelo de datos
En las secciones siguientes se crea un modelo de datos:
Un alumno se puede inscribir en cualquier número de cursos y un curso puede tener cualquier número de alumnos inscritos.
La entidad Student
Cree una carpeta Models en la carpeta del proyecto.
Cree el archivo
Models/Student.cs
con el siguiente código: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. De forma predeterminada, EF Core interpreta como la clave principal una propiedad que se denomine ID
o classnameID
. Por tanto, el nombre que se reconoce de forma automática para la clave principal de la clase Student
es StudentID
. Para obtener más información, consulte EF Core - Claves.
La propiedad Enrollments
es una propiedad de navegación. Las propiedades de navegación contienen otras entidades relacionadas con esta entidad. En este caso, la propiedad Enrollments
de una entidad Student
contiene todas las entidades Enrollment
que están relacionadas con esa instancia de 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.
En la base de datos, una fila Enrollment se relaciona con una fila Student si su columna StudentID
contiene el valor de identificador del estudiante. Por ejemplo, imagine que una fila Student tiene el identificador 1. Las filas Enrollment relacionadas tendrán StudentID
= 1.
StudentID
es una clave externa en la tabla de Inscripción.
La propiedad Enrollments
se define como ICollection<Enrollment>
porque puede haber varias entidades Enrollment relacionadas. Puede usar otros tipos de colección, como List<Enrollment>
o HashSet<Enrollment>
. Cuando se usa ICollection<Enrollment>
, EF Core crea una colección HashSet<Enrollment>
de forma predeterminada.
La entidad Enrollment
Cree el archivo Models/Enrollment.cs
con el siguiente código:
using System.ComponentModel.DataAnnotations;
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; }
[DisplayFormat(NullDisplayText = "No grade")]
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. Para un modelo de datos de producción, muchos desarrolladores eligen un patrón y lo usan de forma coherente. En este tutorial se usan los dos simplemente para ilustrar el trabajo. El uso de ID
sin classname
facilita la implementación de algunos tipos de cambios del modelo de datos.
La propiedad Grade
es una enum
. El signo de interrogación después de la declaración de tipo Grade
indica que la propiedad Grade
puede ser nula. Una calificación que sea NULL es diferente de una calificación que sea cero; NULL significa que no se conoce una calificación o que todavía no se ha asignado.
La propiedad StudentID
es una clave externa y la propiedad de navegación correspondiente es Student
. Una entidad Enrollment
está asociada con una entidad Student
, por lo que la propiedad contiene una única entidad Student
.
La propiedad CourseID
es una clave externa y la propiedad de navegación correspondiente es Course
. Una entidad Enrollment
está asociada con una entidad Course
.
EF Core interpreta una propiedad como una clave externa si se denomina <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
. Las propiedades de clave externa también se pueden denominar <primary key property name>
. Por ejemplo CourseID
, dado que la clave principal de la entidad Course
es CourseID
.
La entidad Course
Cree el archivo Models/Course.cs
con el siguiente código:
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. Una entidad Course
puede estar relacionada con cualquier número de entidades Enrollment
.
El atributo DatabaseGenerated
permite que la aplicación especifique la clave principal en lugar de hacer que la base de datos la genere.
Compile el proyecto para comprobar que no hay errores de compilación.
Scaffolding de las páginas Student
En esta sección, se usa la herramienta de scaffolding de ASP.NET Core para generar lo siguiente:
- Una clase EF Core de
DbContext
. El contexto es la clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos determinado. Se deriva de la clase Microsoft.EntityFrameworkCore.DbContext. -
Razor Pages que controlan las operaciones de creación, lectura, actualización y eliminación (CRUD) de la entidad
Student
.
- Cree una carpeta Pages/Students .
- En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Pages/Students y seleccione Agregar>nuevo elemento con scaffolding.
- En el cuadro de diálogo Agregar nuevo elemento de andamiaje:
- En la pestaña izquierda, seleccione Páginas comunes >> instaladasRazor.
- Seleccione Razor Páginas con Entity Framework (CRUD)>ADD.
- En el cuadro de diálogo Agregar Razor páginas mediante Entity Framework (CRUD):
- En la lista desplegable Clase de modelo , seleccione Student (ContosoUniversity.Models) .
- En la fila de la Clase de contexto de datos, seleccione el signo + (más).
- Cambie el nombre del contexto de datos para que acabe en
SchoolContext
en lugar deContosoUniversityContext
. Nombre del contexto actualizado:ContosoUniversity.Data.SchoolContext
. - Seleccione Agregar para terminar de agregar la clase de contexto de datos.
- Seleccione Agregar para finalizar el cuadro de diálogo Agregar Razor páginas .
- Cambie el nombre del contexto de datos para que acabe en
Si se produce un error en el andamiaje 'Install the package Microsoft.VisualStudio.Web.CodeGeneration.Design and try again.'
, vuelva a ejecutar la herramienta de andamiaje o consulte este problema de GitHub.
Los paquetes siguientes se instalan de forma automática:
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.
El proceso de scaffolding:
- Crea Razor páginas en la carpeta Pages/Students :
-
Create.cshtml
yCreate.cshtml.cs
-
Delete.cshtml
yDelete.cshtml.cs
-
Details.cshtml
yDetails.cshtml.cs
-
Edit.cshtml
yEdit.cshtml.cs
-
Index.cshtml
yIndex.cshtml.cs
-
- Crea
Data/SchoolContext.cs
. - Agrega el contexto a la inserción de dependencias en
Startup.cs
. - Agrega una cadena de conexión de la base de datos a
appsettings.json
.
Cadena de conexión de base de datos
La herramienta de scaffolding genera una cadena de conexión en el archivo appsettings.json
.
La cadena de conexión especifica SQL Server LocalDB:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=CU-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. De forma predeterminada, LocalDB crea .mdf archivos en el C:/Users/<user>
directorio .
Actualización de la clase de contexto de base de datos
La clase principal que coordina la funcionalidad de EF Core para un modelo de datos determinado es la clase context de la base de datos. El contexto se deriva de Microsoft.EntityFrameworkCore.DbContext. En el contexto se especifica qué entidades se incluyen en el modelo de datos. En este proyecto, la clase se denomina SchoolContext
.
Actualice Data/SchoolContext.cs
con el siguiente código:
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. Para que el código de las páginas Razor coincida con el nombre nuevo de DBSet
, realice un cambio global de:
_context.Student.
a: _context.Students.
Hay ocho repeticiones.
Dado que un conjunto de entidades contiene varias entidades, muchos desarrolladores prefieren que los nombres de la propiedad DBSet
sean plurales.
El código resaltado:
- Crea una propiedad DbSet<TEntity> para cada conjunto de entidades. En terminología de EF Core:
- Un conjunto de entidades normalmente se corresponde a una tabla de base de datos.
- Una entidad se corresponde con una fila de la tabla.
- Llama a OnModelCreating.
OnModelCreating
:- se llama cuando
SchoolContext
se ha inicializado, pero antes de que el modelo se haya asegurado y utilizado para inicializar el contexto. - Es necesario porque, más adelante en el tutorial, la entidad
Student
tendrá referencias a las demás entidades.
- se llama cuando
Compile el proyecto para comprobar que no haya errores del compilador.
Startup.cs
ASP.NET Core se basa en la inyección de dependencias. Los servicios como SchoolContext
se registran con inserción de dependencias durante el inicio de la aplicación. Estos servicios se proporcionan a los componentes que los necesitan, como páginas Razor, a través de parámetros de constructor. El código de constructor que obtiene una instancia de contexto de base de datos se muestra más adelante en el tutorial.
La herramienta de scaffolding ha registrado de forma automática la clase de contexto con el contenedor de inserción de dependencias.
El proveedor de scaffolding ha agregado las líneas resaltadas siguientes:
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. Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la cadena de conexión del appsettings.json
archivo.
Incorporación del filtro de excepción de base de datos
Agregue AddDatabaseDeveloperPageExceptionFilter y UseMigrationsEndPoint, tal como se muestra en el código siguiente:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
services.AddDatabaseDeveloperPageExceptionFilter();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
Agregue el paquete NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore .
En la Consola del administrador de paquetes, escriba esto para agregar el paquete NuGet:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
El paquete NuGet de Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
proporciona middleware de ASP.NET Core para páginas de error de Entity Framework Core. Este middleware ayuda a detectar y diagnosticar errores con migraciones de Entity Framework Core.
AddDatabaseDeveloperPageExceptionFilter
proporciona información de error útil en el entorno de desarrollo para errores en las migraciones de EF.
Creación de la base de datos
Actualice Program.cs
para crear la base de datos si no existe:
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. Si no existe ninguna base de datos, se crean la base de datos y el esquema.
EnsureCreated
habilita el flujo de trabajo siguiente para controlar los cambios del modelo de datos:
- Se elimina la base de datos. Se pierden los datos existentes.
- Se cambia el modelo de datos. Por ejemplo, se agrega un campo
EmailAddress
. - Ejecutar la aplicación.
-
EnsureCreated
crea una base de datos con el esquema nuevo.
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. La situación es distinta cuando es necesario conservar los datos introducidos en la base de datos. En ese caso, use las migraciones.
Más adelante en la serie de tutoriales, se eliminará la base de datos creada por EnsureCreated
y se usarán las migraciones. Una base de datos creada por EnsureCreated
no se puede actualizar mediante migraciones.
Prueba de la aplicación
- Ejecutar la aplicación.
- Seleccione el enlace Students y luego Create New.
- Pruebe los vínculos Edit, Details y Delete.
Inicializar la base de datos
El método EnsureCreated
crea una base de datos vacía. En esta sección se agrega código que rellena la base de datos con datos de prueba.
Cree el archivo Data/DbInitializer.cs
con el siguiente código:
using ContosoUniversity.Models;
using System;
using System.Linq;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
// 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. Si no hay ningún alumno, agrega datos de prueba a la base de datos. Crea los datos de prueba en matrices en lugar de colecciones List<T>
para optimizar el rendimiento.
En
Program.cs
, quite//
en la líneaDbInitializer.Initialize
:context.Database.EnsureCreated(); DbInitializer.Initialize(context);
Detenga la aplicación si se está ejecutando y ejecute el siguiente comando en la consola del Administrador de paquetes (PMC):
Drop-Database -Confirm
Responda con
Y
para eliminar la base de datos.
- Reinicie la aplicación.
- Seleccione la página Students para ver los datos inicializados.
Consulta la base de datos
- Abra el Explorador de objetos de SQL Server (SSOX) en el menú Ver de Visual Studio.
- En SSOX, seleccione (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.
- Expanda el nodo Tablas .
- Haga clic con el botón derecho en la tabla Student y haga clic en Ver datos para ver las columnas creadas y las filas insertadas en la tabla.
- Haga clic con el botón derecho en la tabla Student y haga clic en Ver código para ver cómo se asigna el
Student
modelo al esquema de la tablaStudent
.
Código asincrónico
La programación asincrónica es el modo predeterminado de ASP.NET Core y 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. Cuando esto ocurre, el servidor no puede procesar nuevas solicitudes hasta que los subprocesos se liberen. 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. 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. 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.
El código asincrónico introduce una pequeña cantidad de sobrecarga en tiempo de ejecución. 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.
En el código siguiente, la palabra clave async , Task
el valor devuelto, await
la palabra clave y ToListAsync
el método hacen que el código se ejecute de forma asincrónica.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
- La palabra clave
async
indica al compilador que:- Genere devoluciones de llamada para partes del cuerpo del método.
- Cree el objeto Task que se devuelve.
- El tipo devuelto
Task
representa el trabajo en curso. - La palabra clave
await
hace que el compilador divida el método en dos partes. La primera parte termina con la operación que se inició de forma asincrónica. La segunda parte se coloca en un método de devolución de llamada que se llama cuando finaliza la operación. -
ToListAsync
es la versión asincrónica del método de extensiónToList
.
Algunos aspectos que tener en cuenta al escribir código asincrónico en el que se usa EF Core son los siguientes:
- 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. Esto incluye
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
ySaveChangesAsync
. No incluye las instrucciones que solo cambian unaIQueryable
, comovar 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.
- 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.
Para obtener más información sobre la programación asincrónica en .NET, consulte Async Overview y Programación asincrónica con async y await.
Consideraciones de rendimiento
En general, una página web no debe cargar un número arbitrario de filas. Una consulta debe utilizar la paginación o un enfoque de limitación. Por ejemplo, la consulta anterior podría usar Take
para limitar las filas devueltas:
public async Task OnGetAsync()
{
Student = await _context.Students.Take(10).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.
MaxModelBindingCollectionSize tiene como valor predeterminado 1024. El código siguiente establece MaxModelBindingCollectionSize
:
public void ConfigureServices(IServiceCollection services)
{
var myMaxModelBindingCollectionSize = Convert.ToInt32(
Configuration["MyMaxModelBindingCollectionSize"] ?? "100");
services.Configure<MvcOptions>(options =>
options.MaxModelBindingCollectionSize = myMaxModelBindingCollectionSize);
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
services.AddDatabaseDeveloperPageExceptionFilter();
}
Consulte Configuración para obtener información sobre las opciones de configuración como MyMaxModelBindingCollectionSize
.
La paginación se trata más adelante en el tutorial.
Para obtener más información, consulte Consideraciones de rendimiento (EF).
Registros SQL de Entity Framework Core
La configuración de registros suele proporcionarla la sección Logging
de los archivos appsettings.{Environment}.json
. Para registrar instrucciones SQL, agregue "Microsoft.EntityFrameworkCore.Database.Command": "Information"
al archivo appsettings.Development.json
:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDB-2;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
,"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
},
"AllowedHosts": "*"
}
Con el archivo JSON anterior, las instrucciones SQL se muestran en la línea de comandos y en ventana de salida de Visual Studio.
Para obtener más información, consulte Registro en .NET Core y ASP.NET Core y este problema de GitHub.
Pasos siguientes
Uso de SQLite para el desarrollo, SQL Server para producción
Este es el primero de una serie de tutoriales que muestran cómo usar Entity Framework (EF) Core en una aplicación ASP.NET Core Razor Pages . En el tutorial se crea un sitio web de una universidad ficticia, Contoso University. El sitio incluye funciones como la admisión de alumnos, la creación de cursos y las asignaciones de instructores. En el tutorial se usa el enfoque de Code First. Para obtener información sobre cómo seguir este tutorial mediante el primer enfoque de la base de datos, consulte este problema de Github.
Descargue o vea la aplicación completada.Descargue las instrucciones.
Requisitos previos
- Si no está familiarizado con Pages, consulte la serie de tutoriales RazorRazor Pages antes de comenzar esta.
Visual Studio 2022 con la carga de trabajo de ASP.NET y desarrollo web.
Motores de bases de datos
Las instrucciones de Visual Studio usan SQL Server LocalDB, una versión de SQL Server Express que solo se ejecuta en Windows.
Las instrucciones de Visual Studio Code usan SQLite, un motor de base de datos multiplataforma.
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.
Solución de problemas
Si tiene un problema que no puede resolver, compare el código con el proyecto completado. Una buena manera de obtener ayuda es publicar una pregunta en StackOverflow.com, usando la etiqueta ASP.NET Core o la EF Core etiqueta .
La aplicación de ejemplo
La aplicación compilada en estos tutoriales es un sitio web básico de una universidad. Los usuarios pueden ver y actualizar la información de estudiantes, cursos e instructores. Estas son algunas de las pantallas que se crean en el tutorial.
El estilo de la interfaz de usuario de este sitio se basa en las plantillas de proyecto integradas. El enfoque del tutorial es cómo usar EF Core, no cómo personalizar la interfaz de usuario.
Siga el vínculo de la parte superior de la página para obtener el código fuente para el proyecto completado. La carpeta cu30 tiene el código de la versión ASP.NET Core 3.0 del tutorial. Los archivos que reflejan el estado del código de los tutoriales 1-7 se pueden encontrar en la carpeta cu30snapshots .
Para ejecutar la aplicación después de descargar el proyecto completado:
Compile el proyecto.
En la Consola del administrador de paquetes (PMC), ejecute el comando siguiente:
Update-Database
Ejecute el proyecto para inicializar la base de datos.
Creación del proyecto de aplicación web
- En el menú Archivo de Visual Studio, seleccione Nuevo>proyecto.
- Seleccione ASP.NET Aplicación web principal.
- Asigne al proyecto el nombre 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.
- Seleccione .NET Core y ASP.NET Core 3.0 en las listas desplegables y, a continuación, seleccione Aplicación web.
Configurar el estilo del sitio
Configure el encabezado, el pie de página y el menú del sitio mediante la actualización de Pages/Shared/_Layout.cshtml
:
Cambie todas las repeticiones de "ContosoUniversity" por "Contoso University". Hay tres ocurrencias.
Elimine las Home entradas de menú y Privacy y agregue entradas para About, Students, Courses, Instructors y Departments.
Los cambios aparecen resaltados.
<!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">
© 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:
@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/main/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.
El modelo de datos
En las secciones siguientes se crea un modelo de datos:
Un alumno se puede inscribir en cualquier número de cursos y un curso puede tener cualquier número de alumnos inscritos.
La entidad Student
Cree una carpeta Models en la carpeta del proyecto.
Cree el archivo
Models/Student.cs
con el siguiente código: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. De forma predeterminada, EF Core interpreta como la clave principal una propiedad que se denomine ID
o classnameID
. Por tanto, el nombre que se reconoce de forma automática para la clave principal de la clase Student
es StudentID
. Para obtener más información, consulte EF Core - Claves.
La propiedad Enrollments
es una propiedad de navegación. Las propiedades de navegación contienen otras entidades relacionadas con esta entidad. En este caso, la propiedad Enrollments
de una entidad Student
contiene todas las entidades Enrollment
que están relacionadas con esa instancia de 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.
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. Por ejemplo, imagine que una fila Student tiene el identificador 1. Las filas Enrollment relacionadas tendrán StudentID = 1. StudentID es una clave externa en la tabla Inscripción.
La propiedad Enrollments
se define como ICollection<Enrollment>
porque puede haber varias entidades Enrollment relacionadas. Puede usar otros tipos de colección, como List<Enrollment>
o HashSet<Enrollment>
. Cuando se usa ICollection<Enrollment>
, EF Core crea una colección HashSet<Enrollment>
de forma predeterminada.
La entidad Enrollment
Cree el archivo Models/Enrollment.cs
con el siguiente código:
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. Para un modelo de datos de producción, elija un patrón y úselo de forma coherente. En este tutorial se usan los dos simplemente para ilustrar el trabajo. El uso de ID
sin classname
facilita la implementación de algunos tipos de cambios del modelo de datos.
La propiedad Grade
es una enum
. El signo de interrogación después de la declaración de tipo Grade
indica que la propiedad Grade
puede ser nula. Una calificación que sea NULL es diferente de una calificación que sea cero; NULL significa que no se conoce una calificación o que todavía no se ha asignado.
La propiedad StudentID
es una clave externa y la propiedad de navegación correspondiente es Student
. Una entidad Enrollment
está asociada con una entidad Student
, por lo que la propiedad contiene una única entidad Student
.
La propiedad CourseID
es una clave externa y la propiedad de navegación correspondiente es Course
. Una entidad Enrollment
está asociada con una entidad Course
.
EF Core interpreta una propiedad como una clave externa si se denomina <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
. Las propiedades de clave externa también se pueden denominar <primary key property name>
. Por ejemplo CourseID
, dado que la clave principal de la entidad Course
es CourseID
.
La entidad Course
Cree el archivo Models/Course.cs
con el siguiente código:
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. Una entidad Course
puede estar relacionada con cualquier número de entidades Enrollment
.
El atributo DatabaseGenerated
permite que la aplicación especifique la clave principal en lugar de hacer que la base de datos la genere.
Compile el proyecto para comprobar que no hay errores de compilación.
Scaffolding de las páginas Student
En esta sección, se usa la herramienta de scaffolding de ASP.NET Core para generar lo siguiente:
- Una clase EF Core de . El contexto es la clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos determinado. Se deriva de la clase
Microsoft.EntityFrameworkCore.DbContext
. -
Razor Pages que controlan las operaciones de creación, lectura, actualización y eliminación (CRUD) de la entidad
Student
.
- Cree una carpeta Students en la carpeta Pages .
- En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Pages/Students y seleccione Agregar>nuevo elemento con scaffolding.
- En el cuadro de diálogo Agregar estructura, seleccione Razor Páginas con Entity Framework (CRUD)>AGREGAR.
- En el cuadro de diálogo Agregar Razor páginas mediante Entity Framework (CRUD):
- En la lista desplegable Clase de modelo , seleccione Student (ContosoUniversity.Models) .
- En la fila de la Clase de contexto de datos, seleccione el signo + (más).
- Cambie el nombre del contexto de datos de ContosoUniversity.Models.ContosoUniversityContext a ContosoUniversity.Data.SchoolContext.
- Seleccione Agregar.
Los paquetes siguientes se instalan de forma automática:
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.
El proceso de scaffolding:
- Crea Razor páginas en la carpeta Pages/Students :
-
Create.cshtml
yCreate.cshtml.cs
-
Delete.cshtml
yDelete.cshtml.cs
-
Details.cshtml
yDetails.cshtml.cs
-
Edit.cshtml
yEdit.cshtml.cs
-
Index.cshtml
yIndex.cshtml.cs
-
- Crea
Data/SchoolContext.cs
. - Agrega el contexto a la inserción de dependencias en
Startup.cs
. - Agrega una cadena de conexión de la base de datos a
appsettings.json
.
Cadena de conexión de base de datos
El appsettings.json
archivo especifica la cadena de conexión 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. De forma predeterminada, LocalDB crea .mdf archivos en el C:/Users/<user>
directorio .
Actualización de la clase de contexto de base de datos
La clase principal que coordina la funcionalidad de EF Core para un modelo de datos determinado es la clase context de la base de datos. El contexto se deriva de Microsoft.EntityFrameworkCore.DbContext. En el contexto se especifica qué entidades se incluyen en el modelo de datos. En este proyecto, la clase se denomina SchoolContext
.
Actualice Data/SchoolContext.cs
con el siguiente código:
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. En terminología de EF Core:
- Un conjunto de entidades normalmente se corresponde a una tabla de base de datos.
- Una entidad se corresponde con una fila de la tabla.
Puesto que un conjunto de entidades contiene varias entidades, las propiedades DBSet deben ser nombres en plural. Como la herramienta de scaffolding ha creado una instancia Student
de DBSet, en este paso se cambia a Students
en plural.
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. Hay ocho repeticiones.
Compile el proyecto para comprobar que no haya errores del compilador.
Startup.cs
ASP.NET Core se basa en la inyección de dependencias. 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. Estos servicios se proporcionan a los componentes que los necesitan (como Razor Pages) a través de parámetros de constructor. El código de constructor que obtiene una instancia de contexto de base de datos se muestra más adelante en el tutorial.
La herramienta de scaffolding ha registrado de forma automática la clase de contexto con el contenedor de inserción de dependencias.
En
ConfigureServices
, el proveedor de scaffolding ha agregado la línea resaltada: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. Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la cadena de conexión del appsettings.json
archivo.
Creación de la base de datos
Actualice Program.cs
para crear la base de datos si no existe:
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. Si no existe ninguna base de datos, se crean la base de datos y el esquema.
EnsureCreated
habilita el flujo de trabajo siguiente para controlar los cambios del modelo de datos:
- Se elimina la base de datos. Se pierden los datos existentes.
- Se cambia el modelo de datos. Por ejemplo, se agrega un campo
EmailAddress
. - Ejecutar la aplicación.
-
EnsureCreated
crea una base de datos con el esquema nuevo.
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. La situación es distinta cuando es necesario conservar los datos introducidos en la base de datos. En ese caso, use las migraciones.
Más adelante en la serie de tutoriales, eliminará la base de datos creada por EnsureCreated
y, en su lugar, usará las migraciones. Una base de datos creada por EnsureCreated
no se puede actualizar mediante migraciones.
Prueba de la aplicación
- Ejecutar la aplicación.
- Seleccione el enlace Students y luego Create New.
- Pruebe los vínculos Edit, Details y Delete.
Inicializar la base de datos
El método EnsureCreated
crea una base de datos vacía. En esta sección se agrega código que rellena la base de datos con datos de prueba.
Cree el archivo Data/DbInitializer.cs
con el siguiente código:
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. Si no hay ningún alumno, agrega datos de prueba a la base de datos. Crea los datos de prueba en matrices en lugar de colecciones List<T>
para optimizar el rendimiento.
En
Program.cs
, reemplace la llamada aEnsureCreated
con una llamada aDbInitializer.Initialize
:// context.Database.EnsureCreated(); DbInitializer.Initialize(context);
Detenga la aplicación si se está ejecutando y ejecute el siguiente comando en la consola del Administrador de paquetes (PMC):
Drop-Database
Reinicie la aplicación.
Seleccione la página Students para ver los datos inicializados.
Consulta la base de datos
- Abra el Explorador de objetos de SQL Server (SSOX) en el menú Ver de Visual Studio.
- En SSOX, seleccione (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.
- Expanda el nodo Tablas .
- Haga clic con el botón derecho en la tabla Student y haga clic en Ver datos para ver las columnas creadas y las filas insertadas en la tabla.
- Haga clic con el botón derecho en la tabla Student y haga clic en Ver código para ver cómo se asigna el
Student
modelo al esquema de la tablaStudent
.
Código asincrónico
La programación asincrónica es el modo predeterminado de ASP.NET Core y 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. Cuando esto ocurre, el servidor no puede procesar nuevas solicitudes hasta que los subprocesos se liberen. 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. 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. 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.
El código asincrónico introduce una pequeña cantidad de sobrecarga en tiempo de ejecución. 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.
En el código siguiente, la palabra clave async , Task<T>
el valor devuelto, await
la palabra clave y ToListAsync
el método hacen que el código se ejecute de forma asincrónica.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
- La palabra clave
async
indica al compilador que:- Genere devoluciones de llamada para partes del cuerpo del método.
- Cree el objeto Task que se devuelve.
- El tipo devuelto
Task<T>
representa el trabajo en curso. - La palabra clave
await
hace que el compilador divida el método en dos partes. La primera parte termina con la operación que se inició de forma asincrónica. La segunda parte se coloca en un método de devolución de llamada que se llama cuando finaliza la operación. -
ToListAsync
es la versión asincrónica del método de extensiónToList
.
Algunos aspectos que tener en cuenta al escribir código asincrónico en el que se usa EF Core son los siguientes:
- 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. Esto incluye
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
ySaveChangesAsync
. No incluye las instrucciones que solo cambian unaIQueryable
, comovar 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.
- 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.
Para obtener más información sobre la programación asincrónica en .NET, consulte Async Overview y Programación asincrónica con async y await.