Evento
Campionato do Mundo de Power BI DataViz
Feb 14, 4 PM - Mar 31, 4 PM
Con 4 posibilidades de entrar, poderías gañar un paquete de conferencias e facelo ao Live Grand Finale en Las Vegas
Máis informaciónEste explorador xa non é compatible.
Actualice a Microsoft Edge para dispoñer das funcionalidades máis recentes, as actualizacións de seguranza e a asistencia técnica.
Por Tom Dykstra y Rick Anderson
En este tutorial se explica el funcionamiento de ASP.NET Core MVC y Entity Framework Core con controladores y vistas. Razor Pages es un modelo de programación alternativo. Para un nuevo desarrollo, se recomienda Razor Pages antes que MVC con controladores y vistas. Vea una versión de Razor Pages de este tutorial. Cada tutorial cubre un material distinto a otros:
Algunos aspectos que este tutorial de MVC cubre y el de Razor Pages no:
Algunos aspectos que el tutorial de Razor Pages cubre y este no:
En la aplicación web de ejemplo de Contoso University se muestra cómo crear una aplicación web ASP.NET Core MVC con Entity Framework (EF) Core y Visual Studio.
La aplicación de ejemplo es un sitio web de una universidad ficticia, Contoso University. Incluye funciones como la admisión de estudiantes, la creación de cursos y asignaciones de instructores. Esta página es la primera de una serie de tutoriales en los que se explica cómo crear la aplicación de ejemplo de Contoso University.
Este tutorial no se ha actualizado para ASP.NET Core 6 o versiones posteriores. Las instrucciones del tutorial no funcionarán correctamente si crea un proyecto destinado a ASP.NET Core 6 o una posterior. Por ejemplo, las plantillas web de ASP.NET Core 6 y versiones posteriores usan el modelo de hospedaje mínimo, que unifica Startup.cs
y Program.cs
en un solo archivo Program.cs
.
Otra diferencia introducida en .NET 6 es la característica NRT (tipos de referencia que admiten valores NULL). Las plantillas de proyecto habilitan esta característica de forma predeterminada. Pueden producirse problemas cuando EF considera que se requiere una propiedad en .NET 6 que admite un valor NULL en .NET 5. Por ejemplo, la página Crear alumno producirá un error de forma silenciosa a menos que se haga que la propiedad Enrollments
admita un valor NULL o la etiqueta del asistente asp-validation-summary
se cambie de ModelOnly
a All
.
Se recomienda instalar y usar el SDK de .NET 5 para este tutorial. Hasta que se actualice este tutorial, consulte Razor Páginas con Entity Framework Core en ASP.NET Core: Tutorial 1 de 8 sobre cómo usar Entity Framework con ASP.NET Core 6 o versiones posteriores.
En las instrucciones de Visual Studio se usa SQL Server LocalDB, una versión de SQL Server Express que solo se ejecuta en Windows.
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. Para obtener una lista de errores comunes y cómo resolverlos, consulta la sección de solución de problemas del último tutorial de la serie. Si ahí no encuentras lo que necesita, puedes publicar una pregunta en StackOverflow.com para ASP.NET Core o EF Core.
Suxestión
Esta es una serie de 10 tutoriales y cada uno se basa en lo que se realiza en los anteriores. Considera la posibilidad de guardar una copia del proyecto después de completar correctamente cada tutorial. Después, si experimentas problemas, puedes empezar desde el tutorial anterior en lugar de volver al principio de la serie completa.
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 de la aplicación:
ContosoUniversity
en Nombre del proyecto. Es importante usar este nombre exacto, incluido el uso de mayúsculas, para que cada namespace
coincida cuando se copie el código.Algunos cambios básicos configuran el menú del sitio, el diseño y la página principal.
Abre Views/Shared/_Layout.cshtml
y realiza los siguientes cambios:
ContosoUniversity
a Contoso University
. Hay tres ocurrencias.Los cambios anteriores se resaltan en el código siguiente:
<!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-controller="Home" asp-action="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 justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="About">About</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Students" asp-action="Index">Students</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Courses" asp-action="Index">Courses</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Departments" asp-action="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">
© 2020 - Contoso University - <a asp-area="" asp-controller="Home" asp-action="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>
En Views/Home/Index.cshtml
, reemplaza el contenido del archivo con el siguiente marcado:
@{
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 MVC web application.
</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.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial »</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/main/aspnetcore/data/ef-mvc/intro/samples/5cu-final">See project source code »</a></p>
</div>
</div>
Presiona CTRL+F5 para ejecutar el proyecto o selecciona Depurar > Iniciar sin depurar en el menú. La página principal se muestra con pestañas para las páginas creadas en este tutorial.
En este tutorial se usa SQL Server y el paquete de proveedor es Microsoft.EntityFrameworkCore.SqlServer.
Este paquete de SQL Server de EF y sus dependencias (Microsoft.EntityFrameworkCore
y Microsoft.EntityFrameworkCore.Relational
) proporcionan compatibilidad en tiempo de ejecución para EF.
Agrega el paquete NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore. En la Consola del administrador de paquetes (PMC), escribe los comandos siguientes para agregar los paquetes NuGet:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.SqlServer
El paquete NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
proporciona middleware de ASP.NET Core para páginas de error de EF Core. Este middleware ayuda a detectar y diagnosticar errores con migraciones de EF Core.
Para obtener información sobre otros proveedores de base de datos disponibles para EF Core, consulta Proveedores de bases de datos.
Se crean las siguientes clases de entidad para esta aplicación:
Las entidades anteriores tienen las siguientes relaciones:
Student
y Enrollment
. Un alumno se puede inscribir en cualquier número de cursos.Course
y Enrollment
. Un curso puede tener cualquier número de alumnos inscritos.En las secciones siguientes, se crea una clase para cada una de estas entidades.
En la carpeta Models, crea la clase Student
con el código siguiente:
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
es la columna de clave principal (PK) de la tabla de base de datos que se corresponde a esta clase. De forma predeterminada, EF interpreta como la clave principal una propiedad que se denomine ID
o classnameID
. Por ejemplo, la clave principal podría tener el nombre StudentID
en lugar de ID
.
La propiedad Enrollments
es una propiedad de navegación. Las propiedades de navegación contienen otras entidades relacionadas con esta entidad. La propiedad Enrollments
de una entidad Student
:
Enrollment
que están relacionadas con esa entidad Student
.Student
específica en la base de datos tiene dos filas Enrollment
relacionadas: Student
de esa entidad Enrollments
contiene esas dos entidades Enrollment
.Las filas Enrollment
contienen el valor de clave principal del alumno en la columna de clave externa (StudentID
) .
Si una propiedad de navegación puede contener varias entidades:
ICollection<T>
, List<T>
o HashSet<T>
.Las relaciones de navegación de varios a varios y de uno a varios pueden contener varias entidades. Cuando se usa ICollection<T>
, EF crea una colección HashSet<T>
de forma predeterminada.
En la carpeta Models, crea la clase Enrollment
con el código siguiente:
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public 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í misma. La entidad Student
usó el patrón ID
. Algunos desarrolladores prefieren usar un patrón en todo el modelo de datos. En este tutorial, la variación muestra que se puede usar cualquiera de los patrones. En un tutorial posterior, verá cómo el uso de ID
sin un nombre de clase facilita la implementación de la herencia en el modelo de datos.
La propiedad Grade
es una enum
. El signo ?
después de la declaración de tipo Grade
indica que la propiedad Grade
acepta valores NULL. Un curso que sea null
es diferente de un curso cero. null
significa que no se conoce un curso o que todavía no se ha asignado.
La propiedad StudentID
es una clave externa (FK) y la propiedad de navegación correspondiente es Student
. Una entidad Enrollment
está asociada con una entidad Student
, por lo que la propiedad solo puede contener una entidad Student
. Esto difiere de la propiedad de navegación Student.Enrollments
, que contiene varias entidades Enrollment
.
La propiedad CourseID
es una clave externa y la propiedad de navegación correspondiente es Course
. Una entidad Enrollment
está asociada con una entidad Course
.
Entity Framework interpreta una propiedad como una propiedad de clave externa si lleva por nombre <
nombre de propiedad de navegación><
nombre de propiedad de clave principal>
. Por ejemplo, StudentID
para la propiedad de navegación Student
, puesto que la clave principal de la entidad Student
es ID
. Las propiedades clave externa también pueden llevar por nombre <
nombre de propiedad de clave principal>
. Por ejemplo, CourseID
porque la clave principal de la entidad Course
es CourseID
.
En la carpeta Models, crea la clase Course
con el código siguiente:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public 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 se explica en un tutorial posterior. Este atributo permite especificar la clave principal para el curso en lugar de hacer que la base de datos la genere.
La clase principal que coordina la funcionalidad de EF para un modelo de datos determinado es la clase de contexto de base de datos DbContext. Esta clase se crea derivándola de la clase Microsoft.EntityFrameworkCore.DbContext
. En la clase derivada DbContext
se especifica qué entidades se incluyen en el modelo de datos. Algunos comportamientos de EF se pueden personalizar. En este proyecto, la clase se denomina SchoolContext
.
En la carpeta del proyecto, cree una carpeta denominada Data
.
En la carpeta Data, cree una clase SchoolContext
con el código siguiente:
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
}
}
El código anterior crea una propiedad DbSet
para el conjunto de entidades. En la terminología de EF:
Se podrían haber omitido las instrucciones DbSet<Enrollment>
y DbSet<Course>
, y el funcionamiento sería el mismo. EF las incluiría implícitamente porque:
Student
hace referencia a la entidad Enrollment
.Enrollment
hace referencia a la entidad Course
.Cuando se crea la base de datos, EF crea las tablas con los mismos nombres que los nombres de propiedad DbSet
. Los nombres de propiedad para las colecciones suelen ser plurales. Por ejemplo, Students
en lugar de Student
. Los desarrolladores están en desacuerdo sobre si los nombres de tabla deben ser plurales o no. Para estos tutoriales, se invalida el comportamiento predeterminado mediante la especificación de nombres de tabla en singular en DbContext
. Para ello, agregue el código resaltado siguiente después de la última propiedad DbSet.
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
ASP.NET Core incluye la inserción de dependencias. Los servicios (como el contexto de base de datos de EF) 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 los controladores MVC) a través de parámetros de constructor. Más adelante en este tutorial verá el código de constructor de controlador que obtiene una instancia de contexto.
Para registrar SchoolContext
como servicio, abra Startup.cs
y agregue las líneas resaltadas al método ConfigureServices
.
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace ContosoUniversity
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllersWithViews();
}
El nombre de la cadena de conexión se pasa al contexto mediante una llamada a un método en un objeto DbContextOptionsBuilder
. Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la cadena de conexión desde el archivo appsettings.json
.
Abra el archivo appsettings.json
y agregue una cadena de conexión como se muestra en el marcado siguiente:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*"
}
Agregue AddDatabaseDeveloperPageExceptionFilter a ConfigureServices
, tal como se muestra en el código siguiente:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddControllersWithViews();
}
AddDatabaseDeveloperPageExceptionFilter
proporciona información de error útil en el entorno de desarrollo.
La cadena de conexión especifica 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 se inicia a petición y se ejecuta en modo de usuario, sin necesidad de una configuración compleja. De forma predeterminada, LocalDB crea archivos de base de datos .mdf en el directorio C:/Users/<user>
.
EF crea una base de datos vacía. En esta sección, se agrega un método al que se llama después de crear la base de datos para rellenarla con datos de prueba.
El método EnsureCreated
se usa para crear automáticamente la base de datos. En un tutorial posterior, verá cómo controlar los cambios en el modelo mediante Migraciones de Code First para cambiar el esquema de base de datos en lugar de quitar y volver a crear la base de datos.
En la carpeta Data, cree una nueva clase denominada DbInitializer
con el código siguiente:
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("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.Students.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.Courses.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.Enrollments.Add(e);
}
context.SaveChanges();
}
}
}
El código anterior comprueba si existe la base de datos:
List<T>
para optimizar el rendimiento.Actualice Program.cs
con el siguiente código:
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>();
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>();
});
}
}
Program.cs
hace lo siguiente en el inicio de la aplicación:
DbInitializer.Initialize
.Initialize
como se muestra en el código siguiente: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>();
DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the database.");
}
}
host.Run();
}
La primera vez que se ejecuta la aplicación, se crea la base de datos y se carga con datos de prueba. Cada vez que los datos del modelo cambian:
En los tutoriales posteriores, la base de datos se modifica cuando cambia el modelo de datos, sin tener que eliminarla y volver a crearla. No se pierden datos cuando cambia el modelo de datos.
Use el motor de scaffolding de Visual Studio para agregar un controlador y vistas de MVC que usará EF para consultar y guardar los datos.
La creación automática de vistas y métodos de acción CRUD se conoce como scaffolding.
Controllers
y selecciona Agregar > Nuevo elemento con scaffold.El motor de scaffolding de Visual Studio crea un archivo StudentsController.cs
y un conjunto de vistas (archivos *.cshtml
) que funcionan con el controlador.
Observa que el controlador toma SchoolContext
como parámetro de constructor.
namespace ContosoUniversity.Controllers
{
public class StudentsController : Controller
{
private readonly SchoolContext _context;
public StudentsController(SchoolContext context)
{
_context = context;
}
La inserción de dependencias de ASP.NET Core se encarga de pasar una instancia de SchoolContext
al controlador. Lo configuraste en la clase Startup
.
El controlador contiene un método de acción Index
, que muestra todos los alumnos en la base de datos. El método obtiene una lista de estudiantes de la entidad Students, que se establece leyendo la propiedad Students
de la instancia del contexto de base de datos:
public async Task<IActionResult> Index()
{
return View(await _context.Students.ToListAsync());
}
Más adelante en el tutorial obtendrá información sobre los elementos de programación asincrónicos de este código.
En la vista Views/Students/Index.cshtml
se muestra esta lista en una tabla:
@model IEnumerable<ContosoUniversity.Models.Student>
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.LastName)
</th>
<th>
@Html.DisplayNameFor(model => model.FirstMidName)
</th>
<th>
@Html.DisplayNameFor(model => model.EnrollmentDate)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="@item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Presiona CTRL+F5 para ejecutar el proyecto o selecciona Depurar > Iniciar sin depurar en el menú.
Haz clic en la pestaña Students para ver los datos de prueba insertados por el método DbInitializer.Initialize
. En función del ancho de la ventana del explorador, verás el vínculo de la pestaña Students
en la parte superior de la página o tendrás que hacer clic en el icono de navegación en la esquina superior derecha para verlo.
Cuando se inicia la aplicación, el método DbInitializer.Initialize
llama a EnsureCreated
. EF observó que no había ninguna base de datos:
Initialize
ha rellenado la base de datos con datos.Usa el Explorador de objetos de SQL Server (SSOX) para ver la base de datos en Visual Studio:
ContosoUniversity1
, la entrada del nombre de la base de datos que se encuentra en la cadena de conexión en el archivo appsettings.json
.Haz clic con el botón derecho en la tabla Student y haz clic en Ver datos para ver los datos de la tabla.
Los archivos de base de datos *.mdf
y *.ldf
se encuentran en la carpeta >.
Dado que se llama a EnsureCreated
en el método de inicializador que se ejecuta en el inicio de la aplicación, puede:
Student
.Por ejemplo, si se agrega una propiedad EmailAddress
a la clase Student
, una nueva columna EmailAddress
en la tabla que se ha vuelto a crear. La vista no mostrará la nueva propiedad EmailAddress
.
La cantidad de código que se escribe para que EF cree una base de datos completa es mínima debido al uso que hace EF de las convenciones:
DbSet
se usan como nombres de tabla. Para las entidades a las que no se hace referencia con una propiedad DbSet
, los nombres de clase de entidad se usan como nombres de tabla.ID
o classnameID
se reconocen como propiedades de clave principal.<
nombre de propiedad de navegación><
nombre de propiedad de clave principal>
. Por ejemplo, StudentID
para la propiedad de navegación Student
, puesto que la clave principal de la entidad Student
es ID
. Las propiedades clave externa también pueden llevar por nombre <
nombre de propiedad de clave principal>
. Por ejemplo EnrollmentID
, dado que la clave principal de la entidad Enrollment
es EnrollmentID
.El comportamiento de las convenciones se puede reemplazar. Por ejemplo, los nombres de tabla se pueden especificar explícitamente, como se mostró anteriormente en este tutorial. Los nombres de columna y cualquier propiedad se pueden establecer como clave principal o clave externa.
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 está habilitado para administrar más tráfico sin retrasos.
El código asincrónico introduce una pequeña cantidad de sobrecarga en tiempo de ejecución, pero para 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, async
, Task<T>
, await
y ToListAsync
hacen que el código se ejecute de forma asincrónica.
public async Task<IActionResult> Index()
{
return View(await _context.Students.ToListAsync());
}
async
indica al compilador que genere devoluciones de llamada para partes del cuerpo del método y que cree automáticamente el objeto Task<IActionResult>
que se devuelve.Task<IActionResult>
representa el trabajo en curso con un resultado de tipo IActionResult
.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ón ToList
.Algunos aspectos que tener en cuenta al escribir código asincrónico en el que se usa EF son los siguientes:
ToListAsync
, SingleOrDefaultAsync
y SaveChangesAsync
. No incluye, por ejemplo, instrucciones que solo cambian una IQueryable
, como var students = context.Students.Where(s => s.LastName == "Davolio")
.await
.Para obtener más información sobre la programación asincrónica en .NET, vea Información general de Async.
Consulte Consideraciones de rendimiento para obtener información sobre cómo limitar el número de las entidades devueltas por una consulta.
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 más información, vea Registrarse en .NET Core y ASP.NET Core y esta incidencia de GitHub.
Pase al tutorial siguiente para obtener información sobre cómo realizar operaciones CRUD (crear, leer, actualizar y eliminar) básicas.
En este tutorial se explica el funcionamiento de ASP.NET Core MVC y Entity Framework Core con controladores y vistas. Razor Pages es un modelo de programación alternativo. Para un nuevo desarrollo, se recomienda Razor Pages antes que MVC con controladores y vistas. Vea una versión de Razor Pages de este tutorial. Cada tutorial cubre un material distinto a otros:
Algunos aspectos que este tutorial de MVC cubre y el de Razor Pages no:
Algunos aspectos que el tutorial de Razor Pages cubre y este no:
En la aplicación web de ejemplo Contoso University se muestra cómo crear aplicaciones web de ASP.NET Core 2.2 MVC con Entity Framework (EF) Core 2.2 y Visual Studio 2019.
Este tutorial no se ha actualizado para ASP.NET Core 3.1. Se ha actualizado para ASP.NET Core 5.0.
La aplicación de ejemplo es un sitio web de una universidad ficticia, Contoso University. Incluye funciones como la admisión de estudiantes, la creación de cursos y asignaciones de instructores. Este es el primero de una serie de tutoriales en los que se explica cómo crear la aplicación de ejemplo Contoso University desde el principio.
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. Para obtener una lista de errores comunes y cómo resolverlos, consulta la sección de solución de problemas del último tutorial de la serie. Si ahí no encuentras lo que necesita, puedes publicar una pregunta en StackOverflow.com para ASP.NET Core o EF Core.
Suxestión
Esta es una serie de 10 tutoriales y cada uno se basa en lo que se realiza en los anteriores. Considera la posibilidad de guardar una copia del proyecto después de completar correctamente cada tutorial. Después, si experimentas problemas, puedes empezar desde el tutorial anterior en lugar de volver al principio de la serie completa.
La aplicación que se va a compilar en estos tutoriales es un sitio web sencillo de una universidad.
Los usuarios pueden ver y actualizar la información de estudiantes, cursos e instructores. A continuación se muestran algunas de las pantallas que se van a crear.
Abre Visual Studio.
En el menú Archivo, selecciona Nuevo > Proyecto.
En el panel de la izquierda, selecciona Instalado > Visual C# > Web.
Selecciona la plantilla de proyecto Aplicación web ASP.NET Core.
Escribe ContosoUniversity como el nombre y haz clic en Aceptar.
Espera que aparezca el cuadro de diálogo Nueva aplicación web ASP.NET Core.
Selecciona .NET Core, ASP.NET Core 2.2 y la plantilla aplicación web (controlador de vista de modelos).
Asegúrate de que Autenticación esté establecida en Sin autenticación.
Selecciona Aceptar.
Algunos cambios sencillos configurarán el menú del sitio, el diseño y la página principal.
Abre Views/Shared/_Layout.cshtml
y realiza los siguientes cambios:
Cambia todas las repeticiones de "ContosoUniversity" por "Contoso University". Hay tres ocurrencias.
Agrega entradas de menú para About, Students, Courses, Instructors y Departments, y elimina la entrada de menú Privacy.
Los cambios aparecen resaltados.
<!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" />
</environment>
<environment exclude="Development">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.css"
asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute"
crossorigin="anonymous"
integrity="sha256-eSi1q2PG6J7g7ib17yAaWMcrr5GrtohYChqibrV7PBE="/>
</environment>
<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-controller="Home" asp-action="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-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="About">About</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Students" asp-action="Index">Students</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Courses" asp-action="Index">Courses</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Departments" asp-action="Index">Departments</a>
</li>
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<partial name="_CookieConsentPartial" />
<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-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<environment include="Development">
<script src="~/lib/jquery/dist/jquery.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"></script>
</environment>
<environment exclude="Development">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.js"
asp-fallback-src="~/lib/jquery/dist/jquery.js"
asp-fallback-test="window.jQuery"
crossorigin="anonymous"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/js/bootstrap.bundle.js"
asp-fallback-src="~/lib/bootstrap/dist/js/bootstrap.bundle.js"
asp-fallback-test="window.jQuery && window.jQuery.fn && window.jQuery.fn.modal"
crossorigin="anonymous"
integrity="sha256-E/V4cWE4qvAeO5MOhjtGtqDzPndRO1LBk8lJ/PR7CA4=">
</script>
</environment>
<script src="~/js/site.js" asp-append-version="true"></script>
@RenderSection("Scripts", required: false)
</body>
</html>
En Views/Home/Index.cshtml
, reemplaza el contenido del archivo con el código siguiente para reemplazar el texto sobre ASP.NET y MVC con texto sobre esta aplicación:
@{
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 MVC web application.
</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.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial »</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/main/aspnetcore/data/ef-mvc/intro/samples/cu-final">See project source code »</a></p>
</div>
</div>
Presiona CTRL+F5 para ejecutar el proyecto o selecciona Depurar > Iniciar sin depurar en el menú. Verá la página principal con pestañas para las páginas que creará en estos tutoriales.
Para agregar compatibilidad con EF Core a un proyecto, instale el proveedor de base de datos que quiera tener como destino. En este tutorial se usa SQL Server y el paquete de proveedor es Microsoft.EntityFrameworkCore.SqlServer. Este paquete se incluye en el metapaquete Microsoft.AspNetCore.App, por lo que no es necesario hacer referencia al paquete.
Este paquete de SQL Server de EF y sus dependencias (Microsoft.EntityFrameworkCore
y Microsoft.EntityFrameworkCore.Relational
) proporcionan compatibilidad en tiempo de ejecución para EF. Más adelante, en el tutorial Migraciones, agregará un paquete de herramientas.
Para obtener información sobre otros proveedores de base de datos disponibles para Entity Framework Core, vea Proveedores de bases de datos.
A continuación podrá crear las clases de entidad para la aplicación Contoso University. Empezará por las tres siguientes entidades.
Hay una relación uno a varios entre las entidades Student
y Enrollment
, y también entre las entidades Course
y Enrollment
. En otras palabras, un estudiante se puede inscribir en cualquier número de cursos y un curso puede tener cualquier número de alumnos inscritos.
En las secciones siguientes creará una clase para cada una de estas entidades.
En la carpeta Models, cree un archivo de clase denominado Student.cs
y reemplace el código de plantilla con el código siguiente.
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 convertirá en la columna de clave principal de la tabla de base de datos que corresponde a esta clase. De forma predeterminada, Entity Framework interpreta como la clave principal una propiedad que se denomine ID
o classnameID
.
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 Student entity
contendrá todas las entidades Enrollment
que estén relacionadas con esa entidad Student
. En otras palabras, si una fila Student
determinada en la base de datos tiene dos filas Enrollment
relacionadas (filas que contienen el valor de clave principal de ese estudiante en la columna de clave externa StudentID), la propiedad de navegación Student
de esa entidad Enrollments
contendrá esas dos entidades Enrollment
.
Si una propiedad de navegación puede contener varias entidades (como en las relaciones de varios a varios o uno a varios), su tipo debe ser una lista a la que se puedan agregar las entradas, eliminarlas y actualizarlas, como ICollection<T>
. Puede especificar ICollection<T>
o un tipo como List<T>
o HashSet<T>
. Si especifica ICollection<T>
, EF crea una colección HashSet<T>
de forma predeterminada.
En la carpeta Models, cree Enrollment.cs
y reemplace el código existente con el código siguiente:
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
}
La propiedad EnrollmentID
será la clave principal; esta entidad usa el patrón classnameID
en lugar de ID
por sí solo, como se vio en la entidad Student
. Normalmente debería elegir un patrón y usarlo en todo el modelo de datos. En este caso, la variación muestra que se puede usar cualquiera de los patrones. En un tutorial posterior, verá cómo el uso de ID sin un nombre de clase facilita la implementación de la herencia en el 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
acepta valores NULL. Una calificación que sea NULL es diferente de una calificación que sea cero; NULL significa que no se conoce una calificación o que todavía no se ha asignado.
La propiedad StudentID
es una clave externa y la propiedad de navegación correspondiente es Student
. Una entidad Enrollment
está asociada con una entidad Student
, por lo que la propiedad solo puede contener un única entidad Student
(a diferencia de la propiedad de navegación Student.Enrollments
que se vio anteriormente, que puede contener varias entidades Enrollment
).
La propiedad CourseID
es una clave externa y la propiedad de navegación correspondiente es Course
. Una entidad Enrollment
está asociada con una entidad Course
.
Entity Framework interpreta una propiedad como propiedad de clave externa si se denomina <navigation property name><primary key property name>
(por ejemplo StudentID
para la propiedad de navegación Student
, dado que la clave principal de la entidad Student
es ID
). Las propiedades de clave externa también se pueden denominar simplemente <primary key property name>
(por ejemplo CourseID
, dado que la clave principal de la entidad Course
es CourseID
).
En la carpeta Models, cree Course.cs
y reemplace el código existente con el código siguiente:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
La propiedad Enrollments
es una propiedad de navegación. Una entidad Course
puede estar relacionada con cualquier número de entidades Enrollment
.
En un DatabaseGenerated
de esta serie se incluirá más información sobre el atributo . Básicamente, este atributo permite escribir la clave principal para el curso en lugar de hacer que la base de datos lo genere.
La clase principal que coordina la funcionalidad de Entity Framework para un modelo de datos determinado es la clase de contexto de base de datos. Esta clase se crea al derivar de la clase Microsoft.EntityFrameworkCore.DbContext
. En el código se especifica qué entidades se incluyen en el modelo de datos. También se puede personalizar determinado comportamiento de Entity Framework. En este proyecto, la clase se denomina SchoolContext
.
En la carpeta del proyecto, cree una carpeta denominada Data.
En la carpeta Data, cree un archivo de clase denominado SchoolContext.cs
y reemplace el código de plantilla con el código siguiente:
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
}
}
Este código crea una propiedad DbSet
para cada conjunto de entidades. En la terminología de Entity Framework, un conjunto de entidades suele corresponderse con una tabla de base de datos, mientras que una entidad lo hace con una fila de la tabla.
Se podrían haber omitido las instrucciones DbSet<Enrollment>
y DbSet<Course>
, y el funcionamiento sería el mismo. Entity Framework las incluiría implícitamente porque la entidad Student
hace referencia a la entidad Enrollment
y la entidad Enrollment
hace referencia a la entidad Course
.
Cuando se crea la base de datos, EF crea las tablas con los mismos nombres que los nombres de propiedad DbSet
. Los nombres de propiedad para las colecciones normalmente están en plural (Students en lugar de Student), pero los desarrolladores no están de acuerdo sobre si los nombres de tabla deben estar en plural o no. Para estos tutoriales, se invalidará el comportamiento predeterminado mediante la especificación de nombres de tabla en singular en DbContext. Para ello, agregue el código resaltado siguiente después de la última propiedad DbSet.
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<Enrollment>().ToTable("Enrollment");
modelBuilder.Entity<Student>().ToTable("Student");
}
}
}
Compile el proyecto para comprobar si hay errores del compilador.
ASP.NET Core implementa la inserción de dependencias de forma predeterminada. Los servicios (como el contexto de base de datos de EF) 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 los controladores MVC) a través de parámetros de constructor. Más adelante en este tutorial verá el código de constructor de controlador que obtiene una instancia de contexto.
Para registrar SchoolContext
como servicio, abra Startup.cs
y agregue las líneas resaltadas al método ConfigureServices
.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
}
El nombre de la cadena de conexión se pasa al contexto mediante una llamada a un método en un objeto DbContextOptionsBuilder
. Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la cadena de conexión desde el archivo appsettings.json
.
Agregue instrucciones using
para los espacios de nombres ContosoUniversity.Data
y Microsoft.EntityFrameworkCore
, y después compile el proyecto.
using ContosoUniversity.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Http;
Abra el archivo appsettings.json
y agregue una cadena de conexión como se muestra en el ejemplo siguiente.
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}
La cadena de conexión especifica una base de datos de SQL Server LocalDB. LocalDB es una versión ligera del motor de base de datos de SQL Server Express que está dirigida al desarrollo de aplicaciones, no al uso en producción. LocalDB se inicia a petición y se ejecuta en modo de usuario, sin necesidad de una configuración compleja. De forma predeterminada, LocalDB crea archivos de base de datos .mdf en el directorio C:/Users/<user>
.
Entity Framework creará una base de datos vacía por usted. En esta sección, escribirá un método que se llama después de crear la base de datos para rellenarla con datos de prueba.
Aquí usará el método EnsureCreated
para crear automáticamente la base de datos. En un tutorial posterior, verá cómo controlar los cambios en el modelo mediante Migraciones de Code First para cambiar el esquema de base de datos en lugar de quitar y volver a crear la base de datos.
En la carpeta Data, cree un archivo de clase denominado DbInitializer.cs
y reemplace el código de plantilla con el código siguiente, que hace que se cree una base de datos cuando es necesario y carga datos de prueba en la nueva base de datos.
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("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.Students.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.Courses.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.Enrollments.Add(e);
}
context.SaveChanges();
}
}
}
El código comprueba si hay estudiantes en la base de datos, y si no es así, asume que la base de datos es nueva y debe inicializarse con datos de prueba. Carga los datos de prueba en matrices en lugar de colecciones List<T>
para optimizar el rendimiento.
En Program.cs
, modifique el método Main
para que haga lo siguiente al iniciar la aplicación:
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>();
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>();
});
}
}
La primera vez que ejecute la aplicación, se creará la base de datos y se inicializará con datos de prueba. Siempre que cambie el modelo de datos:
En los tutoriales posteriores, verá cómo modificar la base de datos cuando cambie el modelo de datos, sin tener que eliminarla y volver a crearla.
En esta sección, se usa el motor de scaffolding de Visual Studio para agregar un controlador y vistas de MVC que usarán EF para consultar y guardar los datos.
La creación automática de vistas y métodos de acción CRUD se conoce como scaffolding. El scaffolding difiere de la generación de código en que el código con scaffolding es un punto de partida que se puede modificar para satisfacer sus propias necesidades, mientras que el código generado normalmente no se modifica. Cuando tenga que personalizar código generado, use clases parciales o regenere el código cuando se produzcan cambios.
El motor de scaffolding de Visual Studio crea un archivo StudentsController.cs
y un conjunto de vistas (archivos .cshtml
) que funcionan con el controlador.
Observa que el controlador toma SchoolContext
como parámetro de constructor.
namespace ContosoUniversity.Controllers
{
public class StudentsController : Controller
{
private readonly SchoolContext _context;
public StudentsController(SchoolContext context)
{
_context = context;
}
La inserción de dependencias de ASP.NET Core se encarga de pasar una instancia de SchoolContext
al controlador. Se configuró en el archivo Startup.cs
.
El controlador contiene un método de acción Index
, que muestra todos los alumnos en la base de datos. El método obtiene una lista de estudiantes de la entidad Students, que se establece leyendo la propiedad Students
de la instancia del contexto de base de datos:
public async Task<IActionResult> Index()
{
return View(await _context.Students.ToListAsync());
}
Más adelante en el tutorial obtendrá información sobre los elementos de programación asincrónicos de este código.
En la vista Views/Students/Index.cshtml
se muestra esta lista en una tabla:
@model IEnumerable<ContosoUniversity.Models.Student>
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.LastName)
</th>
<th>
@Html.DisplayNameFor(model => model.FirstMidName)
</th>
<th>
@Html.DisplayNameFor(model => model.EnrollmentDate)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model) {
<tr>
<td>
@Html.DisplayFor(modelItem => item.LastName)
</td>
<td>
@Html.DisplayFor(modelItem => item.FirstMidName)
</td>
<td>
@Html.DisplayFor(modelItem => item.EnrollmentDate)
</td>
<td>
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="@item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Presiona CTRL+F5 para ejecutar el proyecto o selecciona Depurar > Iniciar sin depurar en el menú.
Haz clic en la pestaña Students para ver los datos de prueba insertados por el método DbInitializer.Initialize
. En función del ancho de la ventana del explorador, verás el vínculo de la pestaña Students
en la parte superior de la página o tendrás que hacer clic en el icono de navegación en la esquina superior derecha para verlo.
Al iniciar la aplicación, el método DbInitializer.Initialize
llama a EnsureCreated
. EF comprobó que no había ninguna base de datos y creó una, y después el resto del código del método Initialize
la rellenó con datos. Puede usar el Explorador de objetos de SQL Server (SSOX) para ver la base de datos en Visual Studio.
Cierre el explorador.
Si la ventana de SSOX no está abierta, selecciónela en el menú Vista de Visual Studio.
En SSOX, haga clic en (localdb)\MSSQLLocalDB > Databases y después en la entrada del nombre de base de datos que se encuentra en la cadena de conexión del archivo appsettings.json
.
Expande el nodo Tablas para ver las tablas de la base de datos.
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.
Los archivos de base de datos .mdf y .ldf se encuentran en la carpeta C:\Usuarios<nombre_de_usuario>.
Como se está llamando a EnsureCreated
en el método de inicializador que se ejecuta al iniciar la aplicación, ahora podría realizar un cambio en la clase Student
, eliminar la base de datos, volver a ejecutar la aplicación y la base de datos se volvería a crear de forma automática para que coincida con el cambio. Por ejemplo, si agrega una propiedad EmailAddress
a la clase Student
, verá una columna EmailAddress
nueva en la tabla que se ha vuelto a crear.
La cantidad de código que tendría que escribir para que Entity Framework pudiera crear una base de datos completa para usted es mínima debido al uso de convenciones o las suposiciones que hace Entity Framework.
DbSet
se usan como nombres de tabla. Para las entidades a las que no se hace referencia con una propiedad DbSet
, los nombres de clase de entidad se usan como nombres de tabla.StudentID
para la propiedad de navegación Student
, dado que la clave principal de la entidad Student
es ID
). Las propiedades de clave externa también se pueden denominar simplemente <nombre de la propiedad de clave principal> (por ejemplo, EnrollmentID
, dado que la clave principal de la entidad Enrollment
es EnrollmentID
).El comportamiento de las convenciones se puede reemplazar. Por ejemplo, puede especificar explícitamente los nombres de tabla, como se vio anteriormente en este tutorial. Y puede establecer los nombres de columna y cualquier propiedad como clave principal o clave externa, como verá en un tutorial posterior de esta serie.
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 está habilitado para administrar más tráfico sin retrasos.
El código asincrónico introduce una pequeña cantidad de sobrecarga en tiempo de ejecución, pero para 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
, 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.
public async Task<IActionResult> Index()
{
return View(await _context.Students.ToListAsync());
}
async
indica al compilador que genere devoluciones de llamada para partes del cuerpo del método y que cree automáticamente el objeto Task<IActionResult>
que se devuelve.Task<IActionResult>
representa el trabajo en curso con un resultado de tipo IActionResult
.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ón ToList
.Algunos aspectos que tener en cuenta al escribir código asincrónico en el que se usa Entity Framework son los siguientes:
ToListAsync
, SingleOrDefaultAsync
y SaveChangesAsync
. No incluye, por ejemplo, instrucciones que solo cambian una IQueryable
, como var students = context.Students.Where(s => s.LastName == "Davolio")
.await
.Para obtener más información sobre la programación asincrónica en .NET, vea Información general de Async.
Pase al tutorial siguiente para obtener información sobre cómo realizar operaciones CRUD (crear, leer, actualizar y eliminar) básicas.
Comentarios de ASP.NET Core
ASP.NET Core é un proxecto de código aberto. Selecciona unha ligazón para ofrecer comentarios:
Evento
Campionato do Mundo de Power BI DataViz
Feb 14, 4 PM - Mar 31, 4 PM
Con 4 posibilidades de entrar, poderías gañar un paquete de conferencias e facelo ao Live Grand Finale en Las Vegas
Máis informaciónFormación
Módulo
Uso de una base de datos con una API mínima, Entity Framework Core y ASP.NET Core - Training
Aprenda a agregar una base de datos a una aplicación de API mínima.
Documentación
Tutorial: Implementación de la funcionalidad CRUD: ASP.NET MVC con EF Core
En este tutorial, podrá revisar y personalizar el código CRUD (crear, leer, actualizar y eliminar) que el scaffolding de MVC crea automáticamente para usted en controladores y vistas.
ASP.NET Core MVC con EF Core: serie de tutoriales
Lista de temas en ASP.NET Core MVC con EF Core
Razor Pages con Entity Framework Core en ASP.NET Core: Tutorial 1 de 8
Se muestra cómo crear una aplicación de Razor Pages mediante Entity Framework Core