Razor Pages com o Entity Framework Core no ASP.NET Core – Tutorial 1 de 8
Observação
Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Aviso
Esta versão do ASP.NET Core não tem mais suporte. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Por Tom Dykstra, Jeremy Likness e Jon P. Smith
Este é o primeiro de uma série de tutoriais que mostram como usar o EF (Entity Framework) Core em um aplicativo Razor Pages do ASP.NET Core. O tutorial cria um site de uma Contoso University fictícia. O site inclui funcionalidades como admissão de alunos, criação de cursos e atribuições de instrutor. O tutorial usa a abordagem de priorização de código. Para obter informações sobre como seguir este tutorial usando a abordagem de priorização do banco de dados, confira este problema do Github.
Baixe ou exiba o aplicativo concluído. Instruções de download.
Pré-requisitos
- Se você for novo no RazorPages, confira a série de tutoriais de Introdução ao Razor Pages antes de iniciar este.
- Visual Studio 2022 com a carga de trabalho do ASP.NET e desenvolvimento Web.
- SDK do .NET 6.0
Mecanismos de banco de dados
As instruções do Visual Studio usam SQL Server LocalDB, uma versão do SQL Server Express que é executada somente no Windows.
Solução de problemas
Se você encontrar um problema que não possa resolver, compare seu código com o projeto concluído. Uma boa maneira de obter ajuda é postando uma pergunta no StackOverflow.com usando a tag ASP.NET Core ou a tag do EF Core.
O aplicativo de exemplo
O aplicativo criado nesses tutoriais é um site básico de universidade. Os usuários podem exibir e atualizar informações de alunos, cursos e instrutores. Veja a seguir algumas das telas criadas no tutorial.
O estilo de interface do usuário deste site baseia-se nos modelos de projeto internos. O foco do tutorial está em como usar o EF Core com o ASP.NET Core, não em como personalizar a interface do usuário.
Opcional: compilar o download de exemplo
Esta etapa é opcional. A criação do aplicativo completo é recomendada quando você tem problemas que não consegue resolver. Se você encontrar um problema que não possa resolver, compare seu código com o projeto concluído. Instruções de download.
Selecione ContosoUniversity.csproj
para abrir o projeto.
Compile o projeto.
No PMC (Console do Gerenciador de Pacotes), execute o seguinte comando:
Update-Database
Execute o projeto para propagar o banco de dados.
Criar o projeto de aplicativo Web
Inicie o Visual Studio 2022 e selecione Criar um novo projeto.
Na caixa de diálogo Criar um projeto novo, selecione Aplicativo Web ASP.NET Core e selecione Avançar.
Na caixa de diálogo Configurar seu novo projeto, insira
ContosoUniversity
no Nome do projeto. É importante nomear o projeto ContosoUniversity, incluindo a correspondência da capitalização, para que os namespaces correspondam quando você copiar e colar o código de exemplo.Selecione Avançar.
No diálogo Informações adicionais, selecione .NET 6.0 (Suporte de longo prazo) e escolha Criar.
Configurar o estilo do site
Copie e cole o seguinte código no arquivo 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>
O arquivo de layout define o cabeçalho, o rodapé e o menu do site. O código anterior faz as seguintes alterações:
- Cada ocorrência de "ContosoUniversity" para "Contoso University". Há três ocorrências.
- As entradas de menu Home e Privacy são excluídas.
- Entradas são adicionadas para Sobre, Alunos, Cursos, Instrutores e Departamentos.
Em Pages/Index.cshtml
, substitua o conteúdo do arquivo pelo seguinte código:
@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>
O código anterior substitui o texto sobre ASP.NET Core pelo texto sobre esse aplicativo.
Execute o aplicativo para verificar se a home page aparece.
O modelo de dados
As seções a seguir criam um modelo de dados:
Um aluno pode ser registrado em qualquer quantidade de cursos e um curso pode ter qualquer quantidade de alunos registrados.
A entidade Student
- Crie uma pasta Modelos na pasta do projeto.
- Crie
Models/Student.cs
com o seguinte 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; } } }
A propriedade ID
se torna a coluna de chave primária da tabela de banco de dados que corresponde a essa classe. Por padrão, o EF Core interpreta uma propriedade chamada ID
ou classnameID
como a chave primária. Portanto, o nome alternativo reconhecido automaticamente para a chave primária da classe Student
é StudentID
. Para mais informações, confira EF Core – Chaves.
A propriedade Enrollments
é uma propriedade de navegação. As propriedades de navegação armazenam outras entidades que estão relacionadas a essa entidade. Nesse caso, a propriedade Enrollments
de uma entidade Student
armazena todas as entidades Enrollment
relacionadas àquele Aluno. Por exemplo, se uma linha Aluno no banco de dados tiver duas linhas Registro relacionadas, a propriedade de navegação Enrollments
conterá duas entidades de Registro.
No banco de dados, uma linha de Registro estará relacionada a uma linha de Aluno se sua coluna StudentID
contiver o valor da ID do aluno. Por exemplo, suponha que uma linha de aluno tenha ID=1. As linhas de registro relacionadas terão StudentID
= 1. StudentID
é uma chave estrangeira na tabela Registro.
A propriedade Enrollments
é definida como ICollection<Enrollment>
porque pode haver várias entidades de registro relacionadas. Outros tipos de coleção podem ser usados, como List<Enrollment>
ou HashSet<Enrollment>
. Quando ICollection<Enrollment>
é usado, o EF Core cria uma coleção HashSet<Enrollment>
por padrão.
A entidade Enrollment
Crie Models/Enrollment.cs
com o seguinte 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; }
}
}
A propriedade EnrollmentID
é a chave primária; essa entidade usa o padrão classnameID
, em vez de ID
por si mesmo. Para um modelo de dados de produção, muitos desenvolvedores escolhem um padrão e usam-no de forma consistente. Este tutorial usa ambos apenas para ilustrar que os dois funcionam. Usar ID
sem classname
facilita a implementação de alguns tipos de alterações no modelo de dados.
A propriedade Grade
é um enum
. O ponto de interrogação após a declaração de tipo Grade
indica que a propriedade Grade
permite valor anulável. Uma nota nula é diferente de uma nota zero – nulo significa que uma nota não é conhecida ou que ainda não foi atribuída.
A propriedade StudentID
é uma chave estrangeira e a propriedade de navegação correspondente é Student
. Uma entidade Enrollment
está associada a uma entidade Student
e, portanto, a propriedade contém uma única entidade Student
.
A propriedade CourseID
é uma chave estrangeira e a propriedade de navegação correspondente é Course
. Uma entidade Enrollment
está associada a uma entidade Course
.
O EF Core interpreta uma propriedade como uma chave estrangeira se ela é nomeada <navigation property name><primary key property name>
. Por exemplo, StudentID
é a chave estrangeira para a propriedade de navegação Student
, pois a chave primária da entidade Student
é ID
. Propriedades de chave estrangeira também podem ser nomeadas <primary key property name>
. Por exemplo, CourseID
, pois a chave primária da entidade Course
é CourseID
.
A entidade Course
Crie Models/Course.cs
com o seguinte 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; }
}
}
A propriedade Enrollments
é uma propriedade de navegação. Uma entidade Course
pode estar relacionada a qualquer quantidade de entidades Enrollment
.
O atributo DatabaseGenerated
permite que o aplicativo especifique a chave primária em vez de fazer com que ela seja gerada pelo banco de dados.
Crie o aplicativo. O compilador gera vários avisos sobre como os valores de null
são tratados. Veja este problema do GitHub, Tipos de referência anuláveis e Tutorial: Expresse sua intenção de design com mais clareza com tipos de referência anuláveis e não anuláveis para obter mais informações.
Para eliminar os avisos dos tipos de referência anuláveis, remova a seguinte linha do arquivo ContosoUniversity.csproj
:
<Nullable>enable</Nullable>
Atualmente, o mecanismo de scaffolding não dá suporte a tipos de referência anuláveis, ou seja, os modelos usados no scaffold também não dão.
Remova a anotação de tipo de referência anulável ?
de public string? RequestId { get; set; }
em Pages/Error.cshtml.cs
para que o projeto seja compilado sem avisos do compilador.
Aplicar scaffold a páginas de Aluno
Nesta seção, a ferramenta de scaffolding do ASP.NET Core é usada para gerar:
- Uma classe EF Core
DbContext
. O contexto é a classe principal que coordena a funcionalidade do Entity Framework para determinado modelo de dados. Ele deriva da classe Microsoft.EntityFrameworkCore.DbContext. - Razor Pages que lidam com as operações CRUD (criar, ler, atualizar e excluir) para a entidade
Student
.
- Crie uma pasta Pages/Students.
- No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Páginas/Alunos e selecione Adicionar>Novo Item com Scaffold.
- No diálogo Adicionar Novo Item de Scaffold:
- Na guia à esquerda, selecione Razor Pages> Comuns >Instaladas
- Selecione Razor Pages usando Entity Framework (CRUD)>ADD.
- Na caixa de diálogo Adicionar Razor Pages usando o Entity Framework (CRUD):
- Na lista suspensa classe Modelo, selecione Aluno (ContosoUniversity.Models).
- Na linha Classe de contexto de dados, selecione o sinal + (adição).
- Altere o nome do contexto de dados para terminar em
SchoolContext
em vez deContosoUniversityContext
. O nome de contexto atualizado:ContosoUniversity.Data.SchoolContext
- Selecione Adicionar para concluir a adição da classe de contexto de dados.
- Selecione Adicionar para concluir o diálogo Adicionar Razor Pages.
- Altere o nome do contexto de dados para terminar em
Os seguintes pacotes são instalados automaticamente:
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.VisualStudio.Web.CodeGeneration.Design
Se a etapa anterior falhar, crie o projeto e repita a etapa de scaffold.
O processo de scaffolding:
- Cria Razor Pages na pasta Pages/Students:
Create.cshtml
eCreate.cshtml.cs
Delete.cshtml
eDelete.cshtml.cs
Details.cshtml
eDetails.cshtml.cs
Edit.cshtml
eEdit.cshtml.cs
Index.cshtml
eIndex.cshtml.cs
- Cria
Data/SchoolContext.cs
. - Adiciona o contexto à injeção de dependência em
Program.cs
. - Adicionar uma cadeia de conexão de banco de dados a
appsettings.json
.
Cadeia de conexão de banco de dados
A ferramenta scaffolding gera uma cadeia de conexão no arquivo appsettings.json
.
A cadeia de conexão especifica um LocalDB do SQL Server:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"SchoolContext": "Server=(localdb)\\mssqllocaldb;Database=SchoolContext-0e9;Trusted_Connection=True;MultipleActiveResultSets=true"
}
}
LocalDB é uma versão leve do Mecanismo de Banco de Dados do SQL Server Express destinado ao desenvolvimento de aplicativos, e não ao uso em produção. Por padrão, o LocalDB cria arquivos .mdf no diretório C:/Users/<user>
.
Atualizar a classe do contexto de banco de dados
A classe principal que coordena a funcionalidade do EF Core de um modelo de dados é a classe de contexto de banco de dados. O contexto é derivado de Microsoft.EntityFrameworkCore.DbContext. O contexto especifica quais entidades são incluídas no modelo de dados. Neste projeto, a classe é chamada SchoolContext
.
Atualize Data/SchoolContext.cs
com o seguinte 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");
}
}
}
O código anterior muda do DbSet<Student> Student
singular para o DbSet<Student> Students
plural. Para fazer com que o código do Razor Pages corresponda ao novo nome de DBSet
, faça uma alteração global de: _context.Student.
para: _context.Students.
Há oito ocorrências.
Como um conjunto de entidades contém várias entidades, muitos desenvolvedores preferem que os nomes de propriedades DBSet
fiquem no plural.
O código realçado:
- Cria uma propriedade DbSet<TEntity> para cada conjunto de entidades. Na terminologia do EF Core:
- Um conjunto de entidades normalmente corresponde a uma tabela de banco de dados.
- Uma entidade corresponde a uma linha da tabela.
- Chama OnModelCreating.
OnModelCreating
:- É chamado quando
SchoolContext
tiver sido inicializado, mas antes de o modelo ter sido protegido e usado para inicializar o contexto. - É necessário porque, mais adiante no tutorial, a entidade
Student
terá referências às outras entidades.
- É chamado quando
Esperamos corrigir esse problema em uma versão futura.
Module.vb
O ASP.NET Core é construído com a injeção de dependência. Serviços como SchoolContext
são registrados com injeção de dependência durante a inicialização do aplicativo. Os componentes que exigem esses serviços, por exemplo, o Razor Pages, recebem esses serviços por meio de parâmetros do construtor. O código de construtor que obtém uma instância de contexto do banco de dados é mostrado mais adiante no tutorial.
A ferramenta de scaffolding registrou automaticamente a classe de contexto com o contêiner de injeção de dependência.
As linhas destacadas abaixo foram adicionadas pelo scaffolder:
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")));
O nome da cadeia de conexão é passado para o contexto com a chamada de um método em um objeto DbContextOptions. Para o desenvolvimento local, o sistema de configuração do ASP.NET Core lê a cadeia de conexão do arquivo appsettings.json
ou o arquivo appsettings.Development.json
.
Adicionar o filtro de exceção de banco de dados
Adicione AddDatabaseDeveloperPageExceptionFilter e UseMigrationsEndPoint conforme mostrado no código abaixo:
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();
}
Adicione o pacote NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.
No Console do Gerenciador de Pacotes, insira o seguinte para adicionar o pacote NuGet:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
O pacote Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
NuGet fornece middleware do ASP.NET Core para páginas de erro do Entity Framework Core. Esse middleware ajuda a detectar e diagnosticar erros em migrações do Entity Framework Core.
O AddDatabaseDeveloperPageExceptionFilter
fornece informações de erro úteis no ambiente de desenvolvimento em erros de migrações de EF.
Criar o banco de dados
Atualize Program.cs
para criar o banco de dados se ele não existir:
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();
O método EnsureCreated não executará nenhuma ação se existir um banco de dados para o contexto. Se não existir nenhum banco de dados, ele criará o banco de dados e o esquema. EnsureCreated
habilita o seguinte fluxo de trabalho para manipular alterações no modelo de dados:
- Exclua o banco de dados. Qualquer dado existente é perdido.
- Altere o modelo de dados. Por exemplo, adicione um campo
EmailAddress
. - Execute o aplicativo.
EnsureCreated
cria um banco de dados com o novo esquema.
Esse fluxo de trabalho funciona no início do desenvolvimento, quando o esquema está evoluindo rapidamente, desde que os dados não precisem ser preservados. A situação é diferente quando os dados que foram inseridos no banco de dados precisam ser preservados. Quando esse for o caso, use migrações.
Posteriormente na série de tutoriais, o banco de dados criado por EnsureCreated
é excluído e as migrações são usadas. Um banco de dados criado pelo EnsureCreated
não pode ser atualizado usando migrações.
Testar o aplicativo
- Execute o aplicativo.
- Selecione o link Alunos e Criar Novo.
- Teste os links Editar, Detalhes e Excluir.
Propagar o banco de dados
O método EnsureCreated
cria um banco de dados vazio. Esta seção adiciona um código que preenche o banco de dados com os dados de teste.
Crie Data/DbInitializer.cs
com o seguinte 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();
}
}
}
O código verifica se há alunos no banco de dados. Se não houver nenhum aluno, ele adicionará dados de teste ao banco de dados. Ele carrega os dados de teste em matrizes, em vez de em coleções de List<T>
, para otimizar o desempenho.
- Em
Program.cs
, remova//
da linhaDbInitializer.Initialize
:
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();
DbInitializer.Initialize(context);
}
Interrompa o aplicativo se ele estiver em execução e execute o seguinte comando no PMC (Console do Gerenciador de Pacotes):
Drop-Database -Confirm
Responda com
Y
para excluir o banco de dados.
- Reinicie o aplicativo.
- Selecione a página Alunos para ver os dados propagados.
Exibição do banco de dados
- Abra o SSOX (Pesquisador de Objetos do SQL Server) no menu Exibir do Visual Studio.
- No SSOX, selecione (localdb)\MSSQLLocalDB > Bancos de Dados > SchoolContext-{GUID}. O nome do banco de dados é gerado usando o nome do contexto fornecido anteriormente, além de um traço e um GUID.
- Expanda o nó Tabelas.
- Clique com o botão direito do mouse na tabela Aluno e clique em Exibir Dados para ver as colunas criadas e as linhas inseridas na tabela.
- Clique com o botão direito do mouse na tabela Aluno e clique em Exibir Código para ver como o modelo
Student
é mapeado para o esquema de tabelaStudent
.
Métodos EF assíncronos em aplicativos Web do ASP.NET Core
A programação assíncrona é o modo padrão do ASP.NET Core e do EF Core.
Um servidor Web tem um número limitado de threads disponíveis e, em situações de alta carga, todos os threads disponíveis podem estar em uso. Quando isso acontece, o servidor não pode processar novas solicitações até que os threads são liberados. Com um código síncrono, muitos threads podem ser vinculados enquanto não estão fazendo trabalho porque estão aguardando a conclusão da E/S. Com um código assíncrono, quando um processo está aguardando a conclusão da E/S, seu thread é liberado para o servidor para ser usado para processar outras solicitações. Como resultado, o código assíncrono permite que os recursos do servidor sejam usados com mais eficiência, e o servidor pode manipular mais tráfego sem atrasos.
O código assíncrono introduz uma pequena quantidade de sobrecarga em tempo de execução. Para situações de baixo tráfego, o impacto no desempenho é insignificante, enquanto para situações de alto tráfego, a melhoria de desempenho potencial é significativa.
No código a seguir, a palavra-chave async, o valor retornado Task
, a palavra-chave await
e o método ToListAsync
fazem o código ser executado de forma assíncrona.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
- A palavra-chave
async
instrui o compilador a:- Gerar retornos de chamada para partes do corpo do método.
- Criar o objeto Task que é retornado.
- O tipo retornado
Task
representa um trabalho em andamento. - A palavra-chave
await
faz com que o compilador divida o método em duas partes. A primeira parte termina com a operação que é iniciada de forma assíncrona. A segunda parte é colocada em um método de retorno de chamada que é chamado quando a operação é concluída. ToListAsync
é a versão assíncrona do método de extensãoToList
.
Algumas coisas a serem consideradas ao escrever um código assíncrono que usa o EF Core:
- Somente instruções que fazem com que consultas ou comandos sejam enviados ao banco de dados são executadas de forma assíncrona. Isso inclui
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
eSaveChangesAsync
. Isso não inclui instruções que apenas alteram umIQueryable
, comovar students = context.Students.Where(s => s.LastName == "Davolio")
. - Um contexto do EF Core não é thread-safe: não tente realizar várias operações em paralelo.
- Para aproveitar os benefícios de desempenho do código assíncrono, verifique se os pacotes de biblioteca (como para paginação) usam o código assíncrono se eles chamarem métodos do EF Core que enviam consultas ao banco de dados.
Para obter mais informações sobre a programação assíncrona, consulte Visão geral de Async e Programação assíncrona com async e await.
Aviso
A implementação assíncrona do Microsoft.Data.SqlClient tem alguns problemas conhecidos (#593, #601 e outros). Se você estiver enfrentando problemas de desempenho inesperados, tente usar a execução de comando de sincronização, especialmente ao lidar com valores binários ou de texto grande.
Considerações sobre o desempenho
Em geral, uma página da Web não deve carregar um número aleatório de linhas. Uma consulta deve usar paginação ou uma abordagem de limitação. Por exemplo, a consulta anterior poderia usar Take
para limitar as linhas retornadas:
public async Task OnGetAsync()
{
Student = await _context.Students.Take(10).ToListAsync();
}
A enumeração de uma tabela grande em um modo de exibição poderá retornar uma resposta HTTP 200 parcialmente construída se uma exceção de banco de dados ocorrer no meio da enumeração.
A paginação será abordada posteriormente no tutorial.
Para obter mais informações, confira Considerações sobre desempenho (EF).
Próximas etapas
Este é o primeiro de uma série de tutoriais que mostram como usar o EF (Entity Framework) Core em um aplicativo Razor Pages do ASP.NET Core. O tutorial cria um site de uma Contoso University fictícia. O site inclui funcionalidades como admissão de alunos, criação de cursos e atribuições de instrutor. O tutorial usa a abordagem de priorização de código. Para obter informações sobre como seguir este tutorial usando a abordagem de priorização do banco de dados, confira este problema do Github.
Baixe ou exiba o aplicativo concluído. Instruções de download.
Pré-requisitos
- Se você for novo no RazorPages, confira a série de tutoriais de Introdução ao Razor Pages antes de iniciar este.
- Visual Studio 2019 16.8 ou posterior com a carga de trabalho do ASP.NET e desenvolvimento Web
- SDK do .NET 5.0
Mecanismos de banco de dados
As instruções do Visual Studio usam SQL Server LocalDB, uma versão do SQL Server Express que é executada somente no Windows.
Solução de problemas
Se você encontrar um problema que não possa resolver, compare seu código com o projeto concluído. Uma boa maneira de obter ajuda é postando uma pergunta no StackOverflow.com usando a tag ASP.NET Core ou a tag do EF Core.
O aplicativo de exemplo
O aplicativo criado nesses tutoriais é um site básico de universidade. Os usuários podem exibir e atualizar informações de alunos, cursos e instrutores. Veja a seguir algumas das telas criadas no tutorial.
O estilo de interface do usuário deste site baseia-se nos modelos de projeto internos. O foco do tutorial está em como usar o EF Core com o ASP.NET Core, não em como personalizar a interface do usuário.
Opcional: compilar o download de exemplo
Esta etapa é opcional. A criação do aplicativo completo é recomendada quando você tem problemas que não consegue resolver. Se você encontrar um problema que não possa resolver, compare seu código com o projeto concluído. Instruções de download.
Selecione ContosoUniversity.csproj
para abrir o projeto.
- Compile o projeto.
- No PMC (Console do Gerenciador de Pacotes), execute o seguinte comando:
Update-Database
Execute o projeto para propagar o banco de dados.
Criar o projeto de aplicativo Web
- Inicie o Visual Studio e selecione Criar um projeto.
- Na caixa de diálogo Criar um novo projeto, selecione Aplicativo Web ASP.NET Core>Avançar.
- No diálogo Configurar seu novo projeto, insira
ContosoUniversity
como Nome do projeto. É importante usar esse nome exato, incluindo a capitalização, para que cada correspondêncianamespace
quando o código é copiado. - Selecione Criar.
- Na caixa de diálogo Criar um novo Aplicativo Web ASP.NET Core, selecione:
- .NET Core e ASP.NET Core 5.0 nas listas suspensas.
- Aplicativo Web ASP.NET Core.
- Criar
Configurar o estilo do site
Copie e cole o seguinte código no arquivo 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>
O arquivo de layout define o cabeçalho, o rodapé e o menu do site. O código anterior faz as seguintes alterações:
- Cada ocorrência de "ContosoUniversity" para "Contoso University". Há três ocorrências.
- As entradas de menu Home e Privacy são excluídas.
- Entradas são adicionadas para Sobre, Alunos, Cursos, Instrutores e Departamentos.
Em Pages/Index.cshtml
, substitua o conteúdo do arquivo pelo seguinte código:
@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>
O código anterior substitui o texto sobre ASP.NET Core pelo texto sobre esse aplicativo.
Execute o aplicativo para verificar se a home page aparece.
O modelo de dados
As seções a seguir criam um modelo de dados:
Um aluno pode ser registrado em qualquer quantidade de cursos e um curso pode ter qualquer quantidade de alunos registrados.
A entidade Student
Crie uma pasta Modelos na pasta do projeto.
Crie
Models/Student.cs
com o seguinte 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; } } }
A propriedade ID
se torna a coluna de chave primária da tabela de banco de dados que corresponde a essa classe. Por padrão, o EF Core interpreta uma propriedade chamada ID
ou classnameID
como a chave primária. Portanto, o nome alternativo reconhecido automaticamente para a chave primária da classe Student
é StudentID
. Para mais informações, confira EF Core – Chaves.
A propriedade Enrollments
é uma propriedade de navegação. As propriedades de navegação armazenam outras entidades que estão relacionadas a essa entidade. Nesse caso, a propriedade Enrollments
de uma entidade Student
armazena todas as entidades Enrollment
relacionadas àquele Aluno. Por exemplo, se uma linha Aluno no banco de dados tiver duas linhas Registro relacionadas, a propriedade de navegação Enrollments
conterá duas entidades de Registro.
No banco de dados, uma linha de Registro estará relacionada a uma linha de Aluno se sua coluna StudentID
contiver o valor da ID do aluno. Por exemplo, suponha que uma linha de aluno tenha ID=1. As linhas de registro relacionadas terão StudentID
= 1. StudentID
é uma chave estrangeira na tabela Registro.
A propriedade Enrollments
é definida como ICollection<Enrollment>
porque pode haver várias entidades de registro relacionadas. Outros tipos de coleção podem ser usados, como List<Enrollment>
ou HashSet<Enrollment>
. Quando ICollection<Enrollment>
é usado, o EF Core cria uma coleção HashSet<Enrollment>
por padrão.
A entidade Enrollment
Crie Models/Enrollment.cs
com o seguinte 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; }
}
}
A propriedade EnrollmentID
é a chave primária; essa entidade usa o padrão classnameID
, em vez de ID
por si mesmo. Para um modelo de dados de produção, muitos desenvolvedores escolhem um padrão e usam-no de forma consistente. Este tutorial usa ambos apenas para ilustrar que os dois funcionam. Usar ID
sem classname
facilita a implementação de alguns tipos de alterações no modelo de dados.
A propriedade Grade
é um enum
. O ponto de interrogação após a declaração de tipo Grade
indica que a propriedade Grade
permite valor anulável. Uma nota nula é diferente de uma nota zero – nulo significa que uma nota não é conhecida ou que ainda não foi atribuída.
A propriedade StudentID
é uma chave estrangeira e a propriedade de navegação correspondente é Student
. Uma entidade Enrollment
está associada a uma entidade Student
e, portanto, a propriedade contém uma única entidade Student
.
A propriedade CourseID
é uma chave estrangeira e a propriedade de navegação correspondente é Course
. Uma entidade Enrollment
está associada a uma entidade Course
.
O EF Core interpreta uma propriedade como uma chave estrangeira se ela é nomeada <navigation property name><primary key property name>
. Por exemplo, StudentID
é a chave estrangeira para a propriedade de navegação Student
, pois a chave primária da entidade Student
é ID
. Propriedades de chave estrangeira também podem ser nomeadas <primary key property name>
. Por exemplo, CourseID
, pois a chave primária da entidade Course
é CourseID
.
A entidade Course
Crie Models/Course.cs
com o seguinte 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; }
}
}
A propriedade Enrollments
é uma propriedade de navegação. Uma entidade Course
pode estar relacionada a qualquer quantidade de entidades Enrollment
.
O atributo DatabaseGenerated
permite que o aplicativo especifique a chave primária em vez de fazer com que ela seja gerada pelo banco de dados.
Compile o projeto para validar que não há erros de compilador.
Aplicar scaffold a páginas de Aluno
Nesta seção, a ferramenta de scaffolding do ASP.NET Core é usada para gerar:
- Uma classe EF Core
DbContext
. O contexto é a classe principal que coordena a funcionalidade do Entity Framework para determinado modelo de dados. Ele deriva da classe Microsoft.EntityFrameworkCore.DbContext. - Razor Pages que lidam com as operações CRUD (criar, ler, atualizar e excluir) para a entidade
Student
.
- Crie uma pasta Pages/Students.
- No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Páginas/Alunos e selecione Adicionar>Novo Item com Scaffold.
- No diálogo Adicionar Novo Item de Scaffold:
- Na guia à esquerda, selecione Razor Pages> Comuns >Instaladas
- Selecione Razor Pages usando Entity Framework (CRUD)>ADD.
- Na caixa de diálogo Adicionar Razor Pages usando o Entity Framework (CRUD):
- Na lista suspensa classe Modelo, selecione Aluno (ContosoUniversity.Models).
- Na linha Classe de contexto de dados, selecione o sinal + (adição).
- Altere o nome do contexto de dados para terminar em
SchoolContext
em vez deContosoUniversityContext
. O nome de contexto atualizado:ContosoUniversity.Data.SchoolContext
- Selecione Adicionar para concluir a adição da classe de contexto de dados.
- Selecione Adicionar para concluir o diálogo Adicionar Razor Pages.
- Altere o nome do contexto de dados para terminar em
Se o scaffolding falhar com o erro 'Install the package Microsoft.VisualStudio.Web.CodeGeneration.Design and try again.'
, execute a ferramenta de scaffold novamente ou veja este problema do GitHub.
Os seguintes pacotes são instalados automaticamente:
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools
Microsoft.VisualStudio.Web.CodeGeneration.Design
Se a etapa anterior falhar, crie o projeto e repita a etapa de scaffold.
O processo de scaffolding:
- Cria Razor Pages na pasta Pages/Students:
Create.cshtml
eCreate.cshtml.cs
Delete.cshtml
eDelete.cshtml.cs
Details.cshtml
eDetails.cshtml.cs
Edit.cshtml
eEdit.cshtml.cs
Index.cshtml
eIndex.cshtml.cs
- Cria
Data/SchoolContext.cs
. - Adiciona o contexto à injeção de dependência em
Startup.cs
. - Adicionar uma cadeia de conexão de banco de dados a
appsettings.json
.
Cadeia de conexão de banco de dados
A ferramenta scaffolding gera uma cadeia de conexão no arquivo appsettings.json
.
A cadeia de conexão especifica um LocalDB do SQL Server:
{
"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 é uma versão leve do Mecanismo de Banco de Dados do SQL Server Express destinado ao desenvolvimento de aplicativos, e não ao uso em produção. Por padrão, o LocalDB cria arquivos .mdf no diretório C:/Users/<user>
.
Atualizar a classe do contexto de banco de dados
A classe principal que coordena a funcionalidade do EF Core de um modelo de dados é a classe de contexto de banco de dados. O contexto é derivado de Microsoft.EntityFrameworkCore.DbContext. O contexto especifica quais entidades são incluídas no modelo de dados. Neste projeto, a classe é chamada SchoolContext
.
Atualize Data/SchoolContext.cs
com o seguinte 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");
}
}
}
O código anterior muda do DbSet<Student> Student
singular para o DbSet<Student> Students
plural. Para fazer com que o código do Razor Pages corresponda ao novo nome de DBSet
, faça uma alteração global de: _context.Student.
para: _context.Students.
Há oito ocorrências.
Como um conjunto de entidades contém várias entidades, muitos desenvolvedores preferem que os nomes de propriedades DBSet
fiquem no plural.
O código realçado:
- Cria uma propriedade DbSet<TEntity> para cada conjunto de entidades. Na terminologia do EF Core:
- Um conjunto de entidades normalmente corresponde a uma tabela de banco de dados.
- Uma entidade corresponde a uma linha da tabela.
- Chama OnModelCreating.
OnModelCreating
:- É chamado quando
SchoolContext
tiver sido inicializado, mas antes de o modelo ter sido protegido e usado para inicializar o contexto. - É necessário porque, mais adiante no tutorial, a entidade
Student
terá referências às outras entidades.
- É chamado quando
Compile o projeto para verificar se não há erros de compilador.
Startup.cs
O ASP.NET Core é construído com a injeção de dependência. Serviços como SchoolContext
são registrados com injeção de dependência durante a inicialização do aplicativo. Os componentes que exigem esses serviços, por exemplo, o Razor Pages, recebem esses serviços por meio de parâmetros do construtor. O código de construtor que obtém uma instância de contexto do banco de dados é mostrado mais adiante no tutorial.
A ferramenta de scaffolding registrou automaticamente a classe de contexto com o contêiner de injeção de dependência.
As linhas destacadas abaixo foram adicionadas pelo scaffolder:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("SchoolContext")));
}
O nome da cadeia de conexão é passado para o contexto com a chamada de um método em um objeto DbContextOptions. Para o desenvolvimento local, o sistema de configuração do ASP.NET Core lê a cadeia de conexão do arquivo appsettings.json
.
Adicionar o filtro de exceção de banco de dados
Adicione AddDatabaseDeveloperPageExceptionFilter e UseMigrationsEndPoint conforme mostrado no código abaixo:
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();
});
}
Adicione o pacote NuGet Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.
No Console do Gerenciador de Pacotes, insira o seguinte para adicionar o pacote NuGet:
Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
O pacote Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore
NuGet fornece middleware do ASP.NET Core para páginas de erro do Entity Framework Core. Esse middleware ajuda a detectar e diagnosticar erros em migrações do Entity Framework Core.
O AddDatabaseDeveloperPageExceptionFilter
fornece informações de erro úteis no ambiente de desenvolvimento em erros de migrações de EF.
Criar o banco de dados
Atualize Program.cs
para criar o banco de dados se ele não existir:
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>();
});
}
}
O método EnsureCreated não executará nenhuma ação se existir um banco de dados para o contexto. Se não existir nenhum banco de dados, ele criará o banco de dados e o esquema. EnsureCreated
habilita o seguinte fluxo de trabalho para manipular alterações no modelo de dados:
- Exclua o banco de dados. Qualquer dado existente é perdido.
- Altere o modelo de dados. Por exemplo, adicione um campo
EmailAddress
. - Execute o aplicativo.
EnsureCreated
cria um banco de dados com o novo esquema.
Esse fluxo de trabalho funciona no início do desenvolvimento, quando o esquema está evoluindo rapidamente, desde que os dados não precisem ser preservados. A situação é diferente quando os dados que foram inseridos no banco de dados precisam ser preservados. Quando esse for o caso, use migrações.
Posteriormente na série de tutoriais, o banco de dados criado por EnsureCreated
é excluído e as migrações são usadas. Um banco de dados criado pelo EnsureCreated
não pode ser atualizado usando migrações.
Testar o aplicativo
- Execute o aplicativo.
- Selecione o link Alunos e Criar Novo.
- Teste os links Editar, Detalhes e Excluir.
Propagar o banco de dados
O método EnsureCreated
cria um banco de dados vazio. Esta seção adiciona um código que preenche o banco de dados com os dados de teste.
Crie Data/DbInitializer.cs
com o seguinte 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();
}
}
}
O código verifica se há alunos no banco de dados. Se não houver nenhum aluno, ele adicionará dados de teste ao banco de dados. Ele carrega os dados de teste em matrizes, em vez de em coleções de List<T>
, para otimizar o desempenho.
Em
Program.cs
, remova//
da linhaDbInitializer.Initialize
:context.Database.EnsureCreated(); DbInitializer.Initialize(context);
Interrompa o aplicativo se ele estiver em execução e execute o seguinte comando no PMC (Console do Gerenciador de Pacotes):
Drop-Database -Confirm
Responda com
Y
para excluir o banco de dados.
- Reinicie o aplicativo.
- Selecione a página Alunos para ver os dados propagados.
Exibição do banco de dados
- Abra o SSOX (Pesquisador de Objetos do SQL Server) no menu Exibir do Visual Studio.
- No SSOX, selecione (localdb)\MSSQLLocalDB > Bancos de Dados > SchoolContext-{GUID}. O nome do banco de dados é gerado usando o nome do contexto fornecido anteriormente, além de um traço e um GUID.
- Expanda o nó Tabelas.
- Clique com o botão direito do mouse na tabela Aluno e clique em Exibir Dados para ver as colunas criadas e as linhas inseridas na tabela.
- Clique com o botão direito do mouse na tabela Aluno e clique em Exibir Código para ver como o modelo
Student
é mapeado para o esquema de tabelaStudent
.
Código assíncrono
A programação assíncrona é o modo padrão do ASP.NET Core e do EF Core.
Um servidor Web tem um número limitado de threads disponíveis e, em situações de alta carga, todos os threads disponíveis podem estar em uso. Quando isso acontece, o servidor não pode processar novas solicitações até que os threads são liberados. Com um código síncrono, muitos threads podem ser vinculados enquanto não estão fazendo trabalho porque estão aguardando a conclusão da E/S. Com um código assíncrono, quando um processo está aguardando a conclusão da E/S, seu thread é liberado para o servidor para ser usado para processar outras solicitações. Como resultado, o código assíncrono permite que os recursos do servidor sejam usados com mais eficiência, e o servidor pode manipular mais tráfego sem atrasos.
O código assíncrono introduz uma pequena quantidade de sobrecarga em tempo de execução. Para situações de baixo tráfego, o impacto no desempenho é insignificante, enquanto para situações de alto tráfego, a melhoria de desempenho potencial é significativa.
No código a seguir, a palavra-chave async, o valor retornado Task
, a palavra-chave await
e o método ToListAsync
fazem o código ser executado de forma assíncrona.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
- A palavra-chave
async
instrui o compilador a:- Gerar retornos de chamada para partes do corpo do método.
- Criar o objeto Task que é retornado.
- O tipo retornado
Task
representa um trabalho em andamento. - A palavra-chave
await
faz com que o compilador divida o método em duas partes. A primeira parte termina com a operação que é iniciada de forma assíncrona. A segunda parte é colocada em um método de retorno de chamada que é chamado quando a operação é concluída. ToListAsync
é a versão assíncrona do método de extensãoToList
.
Algumas coisas a serem consideradas ao escrever um código assíncrono que usa o EF Core:
- Somente instruções que fazem com que consultas ou comandos sejam enviados ao banco de dados são executadas de forma assíncrona. Isso inclui
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
eSaveChangesAsync
. Isso não inclui instruções que apenas alteram umIQueryable
, comovar students = context.Students.Where(s => s.LastName == "Davolio")
. - Um contexto do EF Core não é thread-safe: não tente realizar várias operações em paralelo.
- Para aproveitar os benefícios de desempenho do código assíncrono, verifique se os pacotes de biblioteca (como para paginação) usam o código assíncrono se eles chamarem métodos do EF Core que enviam consultas ao banco de dados.
Para obter mais informações sobre a programação assíncrona, consulte Visão geral de Async e Programação assíncrona com async e await.
Considerações sobre o desempenho
Em geral, uma página da Web não deve carregar um número aleatório de linhas. Uma consulta deve usar paginação ou uma abordagem de limitação. Por exemplo, a consulta anterior poderia usar Take
para limitar as linhas retornadas:
public async Task OnGetAsync()
{
Student = await _context.Students.Take(10).ToListAsync();
}
A enumeração de uma tabela grande em um modo de exibição poderá retornar uma resposta HTTP 200 parcialmente construída se uma exceção de banco de dados ocorrer no meio da enumeração.
MaxModelBindingCollectionSize assume o padrão de 1024. O código abaixo define 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();
}
Confira Configuração para obter informações sobre configurações como MyMaxModelBindingCollectionSize
.
A paginação será abordada posteriormente no tutorial.
Para obter mais informações, confira Considerações sobre desempenho (EF).
Registro em log do SQL do Entity Framework Core
A configuração de log geralmente é fornecida pela seção Logging
dos arquivos appsettings.{Environment}.json
. Para registrar instruções SQL em log, adicione "Microsoft.EntityFrameworkCore.Database.Command": "Information"
ao arquivo 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": "*"
}
Com o JSON anterior, as instruções SQL são exibidas na linha de comando e na janela de saída do Visual Studio.
Para obter mais informações, consulte Registrando em log no .NET Core e no ASP.NET Core e este tópico do GitHub.
Próximas etapas
Este é o primeiro de uma série de tutoriais que mostram como usar o EF (Entity Framework) Core em um aplicativo Razor Pages do ASP.NET Core. O tutorial cria um site de uma Contoso University fictícia. O site inclui funcionalidades como admissão de alunos, criação de cursos e atribuições de instrutor. O tutorial usa a abordagem de priorização de código. Para obter informações sobre como seguir este tutorial usando a abordagem de priorização do banco de dados, confira este problema do Github.
Baixe ou exiba o aplicativo concluído. Instruções de download.
Pré-requisitos
- Se você for novo no RazorPages, confira a série de tutoriais de Introdução ao Razor Pages antes de iniciar este.
- Visual Studio 2019 com carga de trabalho ASP.NET e desenvolvimento Web
- SDK do .NET Core 3.0
Mecanismos de banco de dados
As instruções do Visual Studio usam SQL Server LocalDB, uma versão do SQL Server Express que é executada somente no Windows.
As instruções do Visual Studio Code usam o SQLite, um mecanismo de banco de dados multiplataforma.
Se você optar por usar o SQLite, baixe e instale uma ferramenta de terceiros para gerenciar e exibir um banco de dados SQLite, como o Navegador de BD para SQLite.
Solução de problemas
Se você encontrar um problema que não possa resolver, compare seu código com o projeto concluído. Uma boa maneira de obter ajuda é postando uma pergunta no StackOverflow.com usando a tag ASP.NET Core ou a tag do EF Core.
O aplicativo de exemplo
O aplicativo criado nesses tutoriais é um site básico de universidade. Os usuários podem exibir e atualizar informações de alunos, cursos e instrutores. Veja a seguir algumas das telas criadas no tutorial.
O estilo de interface do usuário deste site baseia-se nos modelos de projeto internos. O foco do tutorial está em como usar o EF Core, não em como personalizar a interface do usuário.
Siga o link na parte superior da página para obter o código-fonte do projeto concluído. A pasta cu30 tem o código para a versão ASP.NET Core 3.0 do tutorial. Os arquivos que refletem o estado do código para os tutoriais 1-7 podem ser encontrados na pasta cu30snapshots.
Para executar o aplicativo depois de baixar o projeto concluído:
Compile o projeto.
No PMC (Console do Gerenciador de Pacotes), execute o seguinte comando:
Update-Database
Execute o projeto para propagar o banco de dados.
Criar o projeto de aplicativo Web
- No menu Arquivo do Visual Studio, selecione Novo>Projeto.
- Selecione Aplicativo Web ASP.NET Core.
- Nomeie o projeto ContosoUniversity. É importante usar esse nome exato, incluindo maiúsculas e minúsculas, para que os namespaces correspondam quando o código for copiado e colado.
- Selecione .NET Core e ASP.NET Core 3.0 na lista suspensa e, em seguida, selecione Aplicativo Web.
Configurar o estilo do site
Configure o cabeçalho, o rodapé e o menu do site atualizando Pages/Shared/_Layout.cshtml
:
Altere cada ocorrência de "ContosoUniversity" para "Contoso University". Há três ocorrências.
Exclua as entradas de menu Home e Privacy e adicione as entradas para Sobre, Alunos, Cursos, Instrutores e Departamentos.
As alterações são realçadas.
<!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>
Em Pages/Index.cshtml
, substitua o conteúdo do arquivo pelo seguinte código para substituir o texto sobre o ASP.NET Core pelo texto sobre este aplicativo:
@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>
Execute o aplicativo para verificar se a home page aparece.
O modelo de dados
As seções a seguir criam um modelo de dados:
Um aluno pode ser registrado em qualquer quantidade de cursos e um curso pode ter qualquer quantidade de alunos registrados.
A entidade Student
Crie uma pasta Modelos na pasta do projeto.
Crie
Models/Student.cs
com o seguinte 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; } } }
A propriedade ID
se torna a coluna de chave primária da tabela de banco de dados que corresponde a essa classe. Por padrão, o EF Core interpreta uma propriedade chamada ID
ou classnameID
como a chave primária. Portanto, o nome alternativo reconhecido automaticamente para a chave primária da classe Student
é StudentID
. Para mais informações, confira EF Core – Chaves.
A propriedade Enrollments
é uma propriedade de navegação. As propriedades de navegação armazenam outras entidades que estão relacionadas a essa entidade. Nesse caso, a propriedade Enrollments
de uma entidade Student
armazena todas as entidades Enrollment
relacionadas àquele Aluno. Por exemplo, se uma linha Aluno no banco de dados tiver duas linhas Registro relacionadas, a propriedade de navegação Enrollments
conterá duas entidades de Registro.
No banco de dados, uma linha de Registro estará relacionada a uma linha de Aluno se sua coluna StudentID contiver o valor da ID do aluno. Por exemplo, suponha que uma linha de aluno tenha ID=1. As linhas de registro relacionadas terão StudentID = 1. StudentID é uma chave estrangeira na tabela de Registro.
A propriedade Enrollments
é definida como ICollection<Enrollment>
porque pode haver várias entidades de registro relacionadas. Você pode usar outros tipos de coleção, como List<Enrollment>
ou HashSet<Enrollment>
. Quando ICollection<Enrollment>
é usado, o EF Core cria uma coleção HashSet<Enrollment>
por padrão.
A entidade Enrollment
Crie Models/Enrollment.cs
com o seguinte 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; }
}
}
A propriedade EnrollmentID
é a chave primária; essa entidade usa o padrão classnameID
, em vez de ID
por si mesmo. Para um modelo de dados de produção, escolha um padrão e use-o de forma consistente. Este tutorial usa ambos apenas para ilustrar que os dois funcionam. Usar ID
sem classname
facilita a implementação de alguns tipos de alterações no modelo de dados.
A propriedade Grade
é um enum
. O ponto de interrogação após a declaração de tipo Grade
indica que a propriedade Grade
permite valor anulável. Uma nota nula é diferente de uma nota zero – nulo significa que uma nota não é conhecida ou que ainda não foi atribuída.
A propriedade StudentID
é uma chave estrangeira e a propriedade de navegação correspondente é Student
. Uma entidade Enrollment
está associada a uma entidade Student
e, portanto, a propriedade contém uma única entidade Student
.
A propriedade CourseID
é uma chave estrangeira e a propriedade de navegação correspondente é Course
. Uma entidade Enrollment
está associada a uma entidade Course
.
O EF Core interpreta uma propriedade como uma chave estrangeira se ela é nomeada <navigation property name><primary key property name>
. Por exemplo, StudentID
é a chave estrangeira para a propriedade de navegação Student
, pois a chave primária da entidade Student
é ID
. Propriedades de chave estrangeira também podem ser nomeadas <primary key property name>
. Por exemplo, CourseID
, pois a chave primária da entidade Course
é CourseID
.
A entidade Course
Crie Models/Course.cs
com o seguinte 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; }
}
}
A propriedade Enrollments
é uma propriedade de navegação. Uma entidade Course
pode estar relacionada a qualquer quantidade de entidades Enrollment
.
O atributo DatabaseGenerated
permite que o aplicativo especifique a chave primária em vez de fazer com que ela seja gerada pelo banco de dados.
Compile o projeto para validar que não há erros de compilador.
Aplicar scaffold a páginas de Aluno
Nesta seção, você usa a ferramenta de scaffolding do ASP.NET Core para gerar:
- Uma classe context de EF Core. O contexto é a classe principal que coordena a funcionalidade do Entity Framework para determinado modelo de dados. Ele deriva da classe
Microsoft.EntityFrameworkCore.DbContext
. - Razor Pages que lidam com as operações CRUD (criar, ler, atualizar e excluir) para a entidade
Student
.
- Crie uma pasta Alunos na pasta Páginas.
- No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Páginas/Alunos e selecione Adicionar>Novo Item com Scaffold.
- No diálogo Adicionar Scaffold, selecione Razor Pages usando o Entity Framework (CRUD)>Adicionar.
- Na caixa de diálogo Adicionar Razor Pages usando o Entity Framework (CRUD):
- Na lista suspensa classe Modelo, selecione Aluno (ContosoUniversity.Models).
- Na linha Classe de contexto de dados, selecione o sinal + (adição).
- Altere o nome do contexto de dados de ContosoUniversity.Models.ContosoUniversityContext para ContosoUniversity.Data.SchoolContext.
- Selecione Adicionar.
Os seguintes pacotes são instalados automaticamente:
Microsoft.VisualStudio.Web.CodeGeneration.Design
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.Extensions.Logging.Debug
Microsoft.EntityFrameworkCore.Tools
Se você tiver um problema com a etapa anterior, compile o projeto e repita a etapa de scaffold.
O processo de scaffolding:
- Cria Razor Pages na pasta Pages/Students:
Create.cshtml
eCreate.cshtml.cs
Delete.cshtml
eDelete.cshtml.cs
Details.cshtml
eDetails.cshtml.cs
Edit.cshtml
eEdit.cshtml.cs
Index.cshtml
eIndex.cshtml.cs
- Cria
Data/SchoolContext.cs
. - Adiciona o contexto à injeção de dependência em
Startup.cs
. - Adicionar uma cadeia de conexão de banco de dados a
appsettings.json
.
Cadeia de conexão de banco de dados
O arquivo appsettings.json
especifica a cadeia de conexão 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 é uma versão leve do Mecanismo de Banco de Dados do SQL Server Express destinado ao desenvolvimento de aplicativos, e não ao uso em produção. Por padrão, o LocalDB cria arquivos .mdf no diretório C:/Users/<user>
.
Atualizar a classe do contexto de banco de dados
A classe principal que coordena a funcionalidade do EF Core de um modelo de dados é a classe de contexto de banco de dados. O contexto é derivado de Microsoft.EntityFrameworkCore.DbContext. O contexto especifica quais entidades são incluídas no modelo de dados. Neste projeto, a classe é chamada SchoolContext
.
Atualize Data/SchoolContext.cs
com o seguinte 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");
}
}
}
O código destacado cria uma propriedade DbSet<TEntity> para cada conjunto de entidades. Na terminologia do EF Core:
- Um conjunto de entidades normalmente corresponde a uma tabela de banco de dados.
- Uma entidade corresponde a uma linha da tabela.
Como um conjunto de entidades contém várias entidades, as propriedades DBSet devem ser nomes no plural. Como a ferramenta scaffolding criou umStudent
DBSet, essa etapa o altera para o Students
no plural.
Para fazer com que o código do Razor Pages corresponda ao novo nome do DBSet, faça uma alteração global em todo o projeto de _context.Student
para _context.Students
. Há oito ocorrências.
Compile o projeto para verificar se não há erros de compilador.
Startup.cs
O ASP.NET Core é construído com a injeção de dependência. Serviços (como o contexto de banco de dados do EF Core) são registrados com injeção de dependência durante a inicialização do aplicativo. Os componentes que exigem esses serviços (por exemplo, o Razor Pages) recebem esses serviços por meio de parâmetros do construtor. O código de construtor que obtém uma instância de contexto do banco de dados é mostrado mais adiante no tutorial.
A ferramenta de scaffolding registrou automaticamente a classe de contexto com o contêiner de injeção de dependência.
Em
ConfigureServices
, as linhas destacadas foram adicionadas pelo scaffolder:public void ConfigureServices(IServiceCollection services) { services.AddRazorPages(); services.AddDbContext<SchoolContext>(options => options.UseSqlServer(Configuration.GetConnectionString("SchoolContext"))); }
O nome da cadeia de conexão é passado para o contexto com a chamada de um método em um objeto DbContextOptions. Para o desenvolvimento local, o sistema de configuração do ASP.NET Core lê a cadeia de conexão do arquivo appsettings.json
.
Criar o banco de dados
Atualize Program.cs
para criar o banco de dados se ele não existir:
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>();
});
}
}
O método EnsureCreated não executará nenhuma ação se existir um banco de dados para o contexto. Se não existir nenhum banco de dados, ele criará o banco de dados e o esquema. EnsureCreated
habilita o seguinte fluxo de trabalho para manipular alterações no modelo de dados:
- Exclua o banco de dados. Qualquer dado existente é perdido.
- Altere o modelo de dados. Por exemplo, adicione um campo
EmailAddress
. - Execute o aplicativo.
EnsureCreated
cria um banco de dados com o novo esquema.
Esse fluxo de trabalho funciona bem no início do desenvolvimento, quando o esquema está evoluindo rapidamente, desde que você não precise preservar os dados. A situação é diferente quando os dados que foram inseridos no banco de dados precisam ser preservados. Quando esse for o caso, use migrações.
Posteriormente na série de tutoriais, você excluirá o banco de dados que foi criado pelo EnsureCreated
e usará migrações em vez disso. Um banco de dados criado pelo EnsureCreated
não pode ser atualizado usando migrações.
Testar o aplicativo
- Execute o aplicativo.
- Selecione o link Alunos e Criar Novo.
- Teste os links Editar, Detalhes e Excluir.
Propagar o banco de dados
O método EnsureCreated
cria um banco de dados vazio. Esta seção adiciona um código que preenche o banco de dados com os dados de teste.
Crie Data/DbInitializer.cs
com o seguinte 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();
}
}
}
O código verifica se há alunos no banco de dados. Se não houver nenhum aluno, ele adicionará dados de teste ao banco de dados. Ele carrega os dados de teste em matrizes, em vez de em coleções de List<T>
, para otimizar o desempenho.
Em
Program.cs
, substitua a chamadaEnsureCreated
por uma chamadaDbInitializer.Initialize
:// context.Database.EnsureCreated(); DbInitializer.Initialize(context);
Interrompa o aplicativo se ele estiver em execução e execute o seguinte comando no PMC (Console do Gerenciador de Pacotes):
Drop-Database
Reinicie o aplicativo.
Selecione a página Alunos para ver os dados propagados.
Exibição do banco de dados
- Abra o SSOX (Pesquisador de Objetos do SQL Server) no menu Exibir do Visual Studio.
- No SSOX, selecione (localdb)\MSSQLLocalDB > Bancos de Dados > SchoolContext-{GUID}. O nome do banco de dados é gerado usando o nome do contexto fornecido anteriormente, além de um traço e um GUID.
- Expanda o nó Tabelas.
- Clique com o botão direito do mouse na tabela Aluno e clique em Exibir Dados para ver as colunas criadas e as linhas inseridas na tabela.
- Clique com o botão direito do mouse na tabela Aluno e clique em Exibir Código para ver como o modelo
Student
é mapeado para o esquema de tabelaStudent
.
Código assíncrono
A programação assíncrona é o modo padrão do ASP.NET Core e do EF Core.
Um servidor Web tem um número limitado de threads disponíveis e, em situações de alta carga, todos os threads disponíveis podem estar em uso. Quando isso acontece, o servidor não pode processar novas solicitações até que os threads são liberados. Com um código síncrono, muitos threads podem ser vinculados enquanto realmente não são fazendo nenhum trabalho porque estão aguardando a conclusão da E/S. Com um código assíncrono, quando um processo está aguardando a conclusão da E/S, seu thread é liberado para o servidor para ser usado para processar outras solicitações. Como resultado, o código assíncrono permite que os recursos do servidor sejam usados com mais eficiência, e o servidor pode manipular mais tráfego sem atrasos.
O código assíncrono introduz uma pequena quantidade de sobrecarga em tempo de execução. Para situações de baixo tráfego, o impacto no desempenho é insignificante, enquanto para situações de alto tráfego, a melhoria de desempenho potencial é significativa.
No código a seguir, a palavra-chave async, o valor retornado Task<T>
, a palavra-chave await
e o método ToListAsync
fazem o código ser executado de forma assíncrona.
public async Task OnGetAsync()
{
Students = await _context.Students.ToListAsync();
}
- A palavra-chave
async
instrui o compilador a:- Gerar retornos de chamada para partes do corpo do método.
- Criar o objeto Task que é retornado.
- O tipo retornado
Task<T>
representa um trabalho em andamento. - A palavra-chave
await
faz com que o compilador divida o método em duas partes. A primeira parte termina com a operação que é iniciada de forma assíncrona. A segunda parte é colocada em um método de retorno de chamada que é chamado quando a operação é concluída. ToListAsync
é a versão assíncrona do método de extensãoToList
.
Algumas coisas a serem consideradas ao escrever um código assíncrono que usa o EF Core:
- Somente instruções que fazem com que consultas ou comandos sejam enviados ao banco de dados são executadas de forma assíncrona. Isso inclui
ToListAsync
,SingleOrDefaultAsync
,FirstOrDefaultAsync
eSaveChangesAsync
. Isso não inclui instruções que apenas alteram umIQueryable
, comovar students = context.Students.Where(s => s.LastName == "Davolio")
. - Um contexto do EF Core não é thread-safe: não tente realizar várias operações em paralelo.
- Para aproveitar os benefícios de desempenho do código assíncrono, verifique se os pacotes de biblioteca (como para paginação) usam o código assíncrono se eles chamarem métodos do EF Core que enviam consultas ao banco de dados.
Para obter mais informações sobre a programação assíncrona, consulte Visão geral de Async e Programação assíncrona com async e await.