Partilhar via


Razor Páginas com Entity Framework Core no ASP.NET Core - Tutorial 1 de 8

Note

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.

Warning

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 9 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 Entity Framework (EF) Core em um aplicativo ASP.NET Core Razor Pages . Os tutoriais criam um site para uma Universidade Contoso fictícia. O site inclui funcionalidades como admissão de alunos, criação de cursos e tarefas de instrutores. O tutorial utiliza a abordagem code first, que prioriza o código. Para obter informações sobre como seguir este tutorial usando a primeira abordagem do banco de dados, consulte este problema do Github.

Transfira ou veja a aplicação concluída.Instruções para download.

Prerequisites

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.

Troubleshooting

Se você encontrar um problema que não pode resolver, compare seu código com o projeto concluído. Uma boa maneira de obter ajuda é postando uma pergunta para StackOverflow.com, usando a tag ASP.NET Core ou a EF Core tag .

O aplicativo de exemplo

O aplicativo construído nesses tutoriais é um site básico da universidade. Os usuários podem visualizar e atualizar informações de alunos, cursos e instrutores. Aqui estão algumas das telas criadas no tutorial.

Página Índice de Estudantes

Alunos Editar página

O estilo da interface do usuário deste site é baseado nos modelos de projeto internos. O foco do tutorial está em como usar EF Core com o ASP.NET Core, não em como personalizar a interface do usuário.

Opcional: criar o download de exemplo

Esta etapa é opcional. A criação do aplicativo concluído é recomendada quando você tem problemas que não consegue resolver. Se você encontrar um problema que não pode resolver, compare seu código com o projeto concluído. Instruções para download.

Selecione ContosoUniversity.csproj para abrir o projeto.

  • Construa o projeto.

  • No Console do Gerenciador de Pacotes (PMC), execute o seguinte comando:

    Update-Database
    

Execute o projeto para semear o banco de dados.

Criar o projeto de aplicativo Web

  1. Inicie o Visual Studio 2022 e selecione Criar um novo projeto.

    Criar um novo projeto a partir da janela inicial

  2. Na caixa de diálogo Criar um novo projeto , selecione ASP.NET Core Web Appe, em seguida, selecione Seguinte.

    Criar uma ASP.NET Core Web App

  3. Na caixa de diálogo Configurar o novo projeto, digite ContosoUniversity para nome do projeto. É importante nomear o projeto ContosoUniversity, incluindo a capitalização correspondente, para que os namespaces correspondam quando se copiar e colar código de exemplo.

  4. Selecione Avançar.

  5. Na caixa de diálogo Informações adicionais, selecione .NET 6.0 (Suporte de longo prazo) e, em seguida, selecione Criar.

    Informações adicionais

Configurar o estilo do site

Copie e cole o seguinte código no Pages/Shared/_Layout.cshtml arquivo:

<!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">
            &copy; 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:

  • Substitua cada ocorrência de "ContosoUniversity" por "Contoso University". Há três ocorrências.
  • As entradas de menu Home e Privacy são eliminadas.
  • As inscrições são adicionadas para Sobre, Alunos, Cursos, Instrutores e Departamentos.

No 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 por texto sobre este aplicativo.

Execute o aplicativo para verificar se a página inicial aparece.

O modelo de dados

As seções a seguir criam um modelo de dados:

Curso -Enrollment-Student diagrama de modelo de dados

Um aluno pode se inscrever em qualquer número de cursos, e um curso pode ter qualquer número de alunos matriculados nele.

A entidade estudantil

Diagrama de entidade estudantil

  • 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 ID propriedade se torna a coluna de chave primária da tabela de banco de dados que corresponde a essa classe. Por padrão, EF Core interpreta uma propriedade nomeada ID ou classnameID como a chave primária. Portanto, o nome alternativo reconhecido automaticamente para a chave primária da Student classe é StudentID. Para mais informações, consulte EF Core - Chaves.

A propriedade Enrollments é uma propriedade de navegação . As propriedades de navegação mantêm outras entidades que estão associadas a esta entidade. Neste caso, a Enrollments propriedade de uma Student entidade detém todas as Enrollment entidades que estão relacionadas com esse Aluno. Por exemplo, se uma linha Estudante no banco de dados tiver duas linhas de Inscrição relacionadas, a Enrollments propriedade de navegação conterá essas duas entidades de Inscrição.

No banco de dados, uma linha Inscrição estará relacionada a uma linha Aluno se sua StudentID coluna contiver o valor de ID do aluno. Por exemplo, suponha que uma linha Student tenha ID=1. As linhas de inscrição relacionadas terão StudentID = 1. StudentID é uma chave estrangeira na tabela Inscrição.

A Enrollments propriedade é 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, EF Core cria uma HashSet<Enrollment> coleção por padrão.

A entidade de Inscrição

Diagrama de entidade de inscrição

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 EnrollmentID propriedade é a chave primária, esta entidade usa o classnameID padrão em vez de ID por si só. Para um modelo de dados de produção, muitos desenvolvedores escolhem um padrão e o usam de forma consistente. Este tutorial usa ambos apenas para ilustrar que ambos funcionam. O uso 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 Grade tipo indica que a Grade propriedade é anulável. Uma nota nula é diferente de uma nota zero — nula significa que uma nota não é conhecida ou ainda não foi atribuída.

A propriedade StudentID é uma chave estrangeira e a propriedade de navegação correspondente é Student. Uma Enrollment entidade está associada a uma Student entidade, portanto, a propriedade contém uma única Student entidade.

A propriedade CourseID é uma chave estrangeira e a propriedade de navegação correspondente é Course. Uma entidade Enrollment está associada a uma entidade Course.

EF Core interpreta uma propriedade como uma chave estrangeira se ela for nomeada <navigation property name><primary key property name>. Por exemplo,StudentID é a chave estrangeira para Student propriedade de navegação, visto que a chave primária da entidade Student é ID. As propriedades da chave estrangeira também podem ser nomeadas <primary key property name>. Por exemplo, CourseID desde que a Course chave primária da entidade seja CourseID.

A entidade do Curso

Diagrama de entidade do curso

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 com qualquer número de entidades Enrollment.

O DatabaseGenerated atributo permite que o aplicativo especifique a chave primária em vez de fazer com que o banco de dados a gere.

Crie o aplicativo. O compilador gera vários avisos sobre como null valores são tratados. Consulte este problema do GitHub, Tipos de referência anuláveis e Tutorial: Expresse sua intenção de design mais claramente com tipos de referência anuláveis e não anuláveis para obter mais informações.

Para eliminar os avisos de tipos de referência anuláveis, remova a seguinte linha do arquivo ContosoUniversity.csproj:

<Nullable>enable</Nullable>

O motor de andaimes atualmente não suporta tipos de referência anuláveis, portanto, os modelos usados em andaimes também não podem.

Remova a anotação de tipo de referência anulável de ? em public string? RequestId { get; set; } para que o projeto seja compilado sem avisos do compilador.

Páginas de apoio ao estudante

Nesta seção, a ferramenta de andaime ASP.NET Core é usada para gerar:

  • Uma EF CoreDbContext aula. O contexto é a classe principal que coordena a funcionalidade do Entity Framework para um determinado modelo de dados. Deriva da Microsoft.EntityFrameworkCore.DbContext classe.
  • Razor páginas que executam operações de Criar, Ler, Atualizar e Excluir (CRUD) para entidade Student.
  • Crie uma pasta Páginas/Alunos .
  • No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Páginas/Alunos e selecione Adicionar>Novo Item de Andaime.
  • Na caixa de diálogo Adicionar Novo Item de Andaime :
    • Na guia à esquerda, selecione Páginas comuns >> instaladas Razor
    • Selecione Razor Páginas usando o Entity Framework (CRUD)>ADD.
  • Na caixa de diálogo Adicionar Razor páginas usando o Entity Framework (CRUD):
    • Na lista suspensa Classe modelo, selecione Estudante (ContosoUniversity.Models).
    • Na linha da classe de contexto de dados, selecione o sinal de + (mais).
      • Altere o nome do contexto de dados para terminar em SchoolContext vez de ContosoUniversityContext. 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 a caixa de diálogo Adicionar Razor páginas .

Os seguintes pacotes são instalados automaticamente:

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

Se a etapa anterior falhar, construa o projeto e tente novamente a etapa do andaime.

O processo de andaime:

  • Cria Razor páginas na pasta Páginas/Alunos :
    • Create.cshtml e Create.cshtml.cs
    • Delete.cshtml e Delete.cshtml.cs
    • Details.cshtml e Details.cshtml.cs
    • Edit.cshtml e Edit.cshtml.cs
    • Index.cshtml e Index.cshtml.cs
  • Cria Data/SchoolContext.cs.
  • Adiciona o contexto à injeção de dependência no Program.cs.
  • Adiciona uma cadeia de conexão de banco de dados ao appsettings.json.

Cadeia de conexão de banco de dados

A ferramenta de andaime gera uma cadeia de conexão no appsettings.json arquivo.

A cadeia de conexão especifica o SQL Server LocalDB:

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

O LocalDB é uma versão leve do Mecanismo de Banco de Dados do SQL Server Express e destina-se ao desenvolvimento de aplicativos, não ao uso em produção. Por padrão, o LocalDB cria .mdf arquivos no C:/Users/<user> diretório.

Atualizar a classe de contexto do banco de dados

A classe principal que coordena a EF Core funcionalidade para um determinado modelo de dados é a classe de contexto do 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 singular DbSet<Student> Student para o plural DbSet<Student> Students. Para fazer com que o código das Razor Páginas corresponda ao novo DBSet nome, faça uma alteração global a partir de: _context.Student. Para: _context.Students.

São 8 ocorrências.

Como um conjunto de entidades contém várias entidades, muitos desenvolvedores preferem que os nomes de propriedade DBSet sejam plurais.

O código destacado:

  • Cria uma DbSet<TEntity> propriedade para cada conjunto de entidades. Na EF Core terminologia:
    • Um conjunto de entidades normalmente corresponde a uma tabela de banco de dados.
    • Uma entidade corresponde a uma linha na tabela.
  • Chamadas OnModelCreating. OnModelCreating:
    • É chamado quando SchoolContext foi inicializado, mas antes que o modelo tenha sido protegido e usado para inicializar o contexto.
    • É obrigatório porque posteriormente no tutorial a Student entidade terá referências às outras entidades.

Esperamos corrigir esse problema em uma versão futura.

Program.cs

ASP.NET Core é construído com injeção de dependência. Serviços como o SchoolContext são registados com injeção de dependência durante a inicialização da aplicação. Os componentes que requerem esses serviços, como as Razor páginas, têm esses serviços disponibilizados através de parâmetros do construtor. O código do construtor que obtém uma instância de contexto de banco de dados é mostrado posteriormente no tutorial.

A ferramenta de andaime registrou automaticamente a classe de contexto com o contêiner de injeção de dependência.

O andaime adicionou as seguintes linhas destacadas:

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 chamando um método em um objeto DbContextOptions. Para o desenvolvimento local, o sistema de configuração ASP.NET Core lê a cadeia de conexão do arquivo appsettings.json ou do arquivo appsettings.Development.json.

Adicionar o filtro de exceção do banco de dados

Adicionar AddDatabaseDeveloperPageExceptionFilter e UseMigrationsEndPoint como mostrado no código a seguir:

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, digite o seguinte para adicionar o pacote NuGet:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

O Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore pacote NuGet fornece middleware ASP.NET Core para páginas de erro do Entity Framework Core. Esse middleware ajuda a detetar e diagnosticar erros com migrações do Entity Framework Core.

O AddDatabaseDeveloperPageExceptionFilter fornece informações úteis sobre erros no ambiente de desenvolvimento para erros de migrações do Entity Framework.

Criar a base 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 EnsureCreated método não executa nenhuma ação se existir um banco de dados para o contexto. Se nenhum banco de dados existir, ele criará o banco de dados e o esquema. EnsureCreated Permite o seguinte fluxo de trabalho para lidar com alterações de modelo de dados:

  • Exclua o banco de dados. Todos os dados existentes são perdidos.
  • Altere o modelo de dados. Por exemplo, adicione um EmailAddress campo.
  • 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 introduzidos na base de dados têm de ser preservados. Quando for esse o caso, use migrações.

Mais tarde na série de tutoriais, o banco de dados criado por EnsureCreated é excluído e utilizam-se migrações. Um banco de dados criado por EnsureCreated não pode ser atualizado usando migrações.

Testar a aplicação

  • Execute o aplicativo.
  • Selecione o link Alunos e, em seguida, Criar novo.
  • Teste os links Editar, Detalhes e Excluir.

Semear o banco de dados

O EnsureCreated método cria um banco de dados vazio. Esta seção adiciona código que preenche o banco de dados com 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á algum aluno no banco de dados. Se não houver alunos, adiciona dados de teste ao banco de dados. Ele cria os dados de teste em matrizes em vez de List<T> coleções para otimizar o desempenho.

  • No Program.cs, remova // da DbInitializer.Initialize linha:
using (var scope = app.Services.CreateScope())
{
    var services = scope.ServiceProvider;

    var context = services.GetRequiredService<SchoolContext>();
    context.Database.EnsureCreated();
    DbInitializer.Initialize(context);
}
  • Pare o aplicativo se ele estiver em execução e execute o seguinte comando no Console do Gerenciador de Pacotes (PMC):

    Drop-Database -Confirm
    
    
  • Responda com Y para excluir o banco de dados.

  • Reinicie o aplicativo.
  • Selecione a página Alunos para ver os dados pré-definidos.

Ver a base de dados

  • Abra o Pesquisador de Objetos do SQL Server (SSOX) no menu Exibir no Visual Studio.
  • Em SSOX, selecione (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. O nome do banco de dados é gerado a partir do nome de 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 rato na tabela Student e clique em Ver Código para ver como o modelo se mapeia Student para o esquema da Student tabela.

Métodos EF assíncronos em aplicativos Web ASP.NET Core

A programação assíncrona é o modo padrão para ASP.NET Core e 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 sejam liberados. Com o código síncrono, muitos threads podem ficar presos enquanto não estão fazendo trabalho porque estão aguardando a conclusão da E/S. Com o código assíncrono, quando um processo está aguardando a conclusão da E/S, seu thread é liberado para o servidor usar para processar outras solicitações. Como resultado, o código assíncrono permite que os recursos do servidor sejam usados de forma mais eficiente e o servidor pode lidar com 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 tráfego elevado, a melhoria potencial do desempenho é substancial.

No código a seguir, a palavra-chave assíncrona , Task o valor de retorno, await a palavra-chave e ToListAsync o método fazem com que o código seja executado de forma assíncrona.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • A async palavra-chave diz ao compilador para:
    • Gere callbacks para partes do corpo do método.
    • Crie o objeto Task que é retornado.
  • O Task tipo de retorno representa o 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 iniciada de forma assíncrona. A segunda parte é usada numa função de retorno que é chamada quando a operação é concluída.
  • ToListAsync é a versão assíncrona do método de extensão ToList.

Algumas coisas a ter em conta ao escrever código assíncrono que utiliza 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, e SaveChangesAsync. Ele não inclui declarações que apenas alteram um IQueryable, como var students = context.Students.Where(s => s.LastName == "Davolio").
  • Um EF Core contexto não é seguro para execução simultânea: não execute 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 async se chamarem EF Core métodos que enviam consultas para o banco de dados.

Para obter mais informações sobre programação assíncrona no .NET, consulte Visão geral assíncrona e Programação assíncrona com async e await.

Warning

A implementação assíncrona de Microsoft.Data.SqlClient tem alguns problemas conhecidos (#593, #601 e outros). Se você estiver vendo problemas de desempenho inesperados, tente usar a execução de comando de sincronização, especialmente ao lidar com texto grande ou valores binários.

Considerações sobre desempenho

Em geral, uma página da Web não deve carregar um número arbitrário de linhas. Uma consulta deve usar paginação ou uma abordagem limitante. Por exemplo, a consulta anterior pode ser usada Take para limitar as linhas retornadas:

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

Enumerar uma tabela extensa numa visualização pode retornar uma resposta HTTP 200 parcialmente construída se uma exceção de base de dados ocorrer a meio do processo de enumeração.

A paginação é abordada mais adiante no tutorial.

Para obter mais informações, consulte Considerações de desempenho (EF).

Próximos passos

Usar SQLite para desenvolvimento, SQL Server para produção

Este é o primeiro de uma série de tutoriais que mostram como usar o Entity Framework (EF) Core em um aplicativo ASP.NET Core Razor Pages . Os tutoriais criam um site para uma Universidade Contoso fictícia. O site inclui funcionalidades como admissão de alunos, criação de cursos e tarefas de instrutores. O tutorial utiliza a abordagem code first, que prioriza o código. Para obter informações sobre como seguir este tutorial usando a primeira abordagem do banco de dados, consulte este problema do Github.

Transfira ou veja a aplicação concluída.Instruções para download.

Prerequisites

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.

Troubleshooting

Se você encontrar um problema que não pode resolver, compare seu código com o projeto concluído. Uma boa maneira de obter ajuda é postando uma pergunta para StackOverflow.com, usando a tag ASP.NET Core ou a EF Core tag .

O aplicativo de exemplo

O aplicativo construído nesses tutoriais é um site básico da universidade. Os usuários podem visualizar e atualizar informações de alunos, cursos e instrutores. Aqui estão algumas das telas criadas no tutorial.

Página Índice de Estudantes

Alunos Editar página

O estilo da interface do usuário deste site é baseado nos modelos de projeto internos. O foco do tutorial está em como usar EF Core com o ASP.NET Core, não em como personalizar a interface do usuário.

Opcional: criar o download de exemplo

Esta etapa é opcional. A criação do aplicativo concluído é recomendada quando você tem problemas que não consegue resolver. Se você encontrar um problema que não pode resolver, compare seu código com o projeto concluído. Instruções para download.

Selecione ContosoUniversity.csproj para abrir o projeto.

  • Construa o projeto.
  • No Console do Gerenciador de Pacotes (PMC), execute o seguinte comando:
Update-Database

Execute o projeto para semear o banco de dados.

Criar o projeto de aplicativo Web

  1. Inicie o Visual Studio e selecione Criar um novo projeto.
  2. Na caixa de diálogo Criar um novo projeto, selecione Aplicação Web ASP.NET Core>Avançar.
  3. Na caixa de diálogo Configurar o novo projeto, digite ContosoUniversity para nome do projeto. É importante usar esse nome exato, incluindo maiúsculas, para que cada namespace corresponda quando o código é copiado.
  4. Selecione Criar.
  5. Na caixa de diálogo Criar um novo aplicativo Web ASP.NET Core , selecione:
    1. .NET Core e ASP.NET Core 5.0 nas listas.
    2. ASP.NET Aplicação Web Core.
    3. caixa de diálogo CriarNovo Projeto ASP.NET Core

Configurar o estilo do site

Copie e cole o seguinte código no Pages/Shared/_Layout.cshtml arquivo:

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

    <footer class="border-top footer text-muted">
        <div class="container">
            &copy; 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:

  • Substitua cada ocorrência de "ContosoUniversity" por "Contoso University". Há três ocorrências.
  • As entradas de menu Home e Privacy são eliminadas.
  • As inscrições são adicionadas para Sobre, Alunos, Cursos, Instrutores e Departamentos.

No 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 por texto sobre este aplicativo.

Execute o aplicativo para verificar se a página inicial aparece.

O modelo de dados

As seções a seguir criam um modelo de dados:

Curso -Enrollment-Student diagrama de modelo de dados

Um aluno pode se inscrever em qualquer número de cursos, e um curso pode ter qualquer número de alunos matriculados nele.

A entidade estudantil

Diagrama de entidade estudantil

  • 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 ID propriedade se torna a coluna de chave primária da tabela de banco de dados que corresponde a essa classe. Por padrão, EF Core interpreta uma propriedade nomeada ID ou classnameID como a chave primária. Portanto, o nome alternativo reconhecido automaticamente para a chave primária da Student classe é StudentID. Para mais informações, consulte EF Core - Chaves.

A propriedade Enrollments é uma propriedade de navegação . As propriedades de navegação mantêm outras entidades que estão associadas a esta entidade. Neste caso, a Enrollments propriedade de uma Student entidade detém todas as Enrollment entidades que estão relacionadas com esse Aluno. Por exemplo, se uma linha Estudante no banco de dados tiver duas linhas de Inscrição relacionadas, a Enrollments propriedade de navegação conterá essas duas entidades de Inscrição.

No banco de dados, uma linha Inscrição estará relacionada a uma linha Aluno se sua StudentID coluna contiver o valor de ID do aluno. Por exemplo, suponha que uma linha Student tenha ID=1. As linhas de inscrição relacionadas terão StudentID = 1. StudentID é uma chave estrangeira na tabela Inscrição.

A Enrollments propriedade é 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, EF Core cria uma HashSet<Enrollment> coleção por padrão.

A entidade de Inscrição

Diagrama de entidade de inscrição

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 EnrollmentID propriedade é a chave primária, esta entidade usa o classnameID padrão em vez de ID por si só. Para um modelo de dados de produção, muitos desenvolvedores escolhem um padrão e o usam de forma consistente. Este tutorial usa ambos apenas para ilustrar que ambos funcionam. O uso 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 Grade tipo indica que a Grade propriedade é anulável. Uma nota nula é diferente de uma nota zero — nula significa que uma nota não é conhecida ou ainda não foi atribuída.

A propriedade StudentID é uma chave estrangeira e a propriedade de navegação correspondente é Student. Uma Enrollment entidade está associada a uma Student entidade, portanto, a propriedade contém uma única Student entidade.

A propriedade CourseID é uma chave estrangeira e a propriedade de navegação correspondente é Course. Uma entidade Enrollment está associada a uma entidade Course.

EF Core interpreta uma propriedade como uma chave estrangeira se ela for nomeada <navigation property name><primary key property name>. Por exemplo,StudentID é a chave estrangeira para Student propriedade de navegação, visto que a chave primária da entidade Student é ID. As propriedades da chave estrangeira também podem ser nomeadas <primary key property name>. Por exemplo, CourseID desde que a Course chave primária da entidade seja CourseID.

A entidade do Curso

Diagrama de entidade do curso

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 com qualquer número de entidades Enrollment.

O DatabaseGenerated atributo permite que o aplicativo especifique a chave primária em vez de fazer com que o banco de dados a gere.

Crie o projeto para validar que não há erros do compilador.

Páginas de apoio ao estudante

Nesta seção, a ferramenta de andaime ASP.NET Core é usada para gerar:

  • Uma EF CoreDbContext aula. O contexto é a classe principal que coordena a funcionalidade do Entity Framework para um determinado modelo de dados. Deriva da Microsoft.EntityFrameworkCore.DbContext classe.
  • Razor páginas que executam operações de Criar, Ler, Atualizar e Excluir (CRUD) para entidade Student.
  • Crie uma pasta Páginas/Alunos .
  • No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Páginas/Alunos e selecione Adicionar>Novo Item de Andaime.
  • Na caixa de diálogo Adicionar Novo Item de Andaime :
    • Na guia à esquerda, selecione Páginas comuns >> instaladas Razor
    • Selecione Razor Páginas usando o Entity Framework (CRUD)>ADD.
  • Na caixa de diálogo Adicionar Razor páginas usando o Entity Framework (CRUD):
    • Na lista suspensa Classe modelo, selecione Estudante (ContosoUniversity.Models).
    • Na linha da classe de contexto de dados, selecione o sinal de + (mais).
      • Altere o nome do contexto de dados para terminar em SchoolContext vez de ContosoUniversityContext. 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 a caixa de diálogo Adicionar Razor páginas .

Se o andaime falhar com o erro 'Install the package Microsoft.VisualStudio.Web.CodeGeneration.Design and try again.', execute a ferramenta de andaime novamente ou veja esse 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, construa o projeto e tente novamente a etapa do andaime.

O processo de andaime:

  • Cria Razor páginas na pasta Páginas/Alunos :
    • Create.cshtml e Create.cshtml.cs
    • Delete.cshtml e Delete.cshtml.cs
    • Details.cshtml e Details.cshtml.cs
    • Edit.cshtml e Edit.cshtml.cs
    • Index.cshtml e Index.cshtml.cs
  • Cria Data/SchoolContext.cs.
  • Adiciona o contexto à injeção de dependência no Startup.cs.
  • Adiciona uma cadeia de conexão de banco de dados ao appsettings.json.

Cadeia de conexão de banco de dados

A ferramenta de andaime gera uma cadeia de conexão no appsettings.json arquivo.

A cadeia de conexão especifica o SQL Server LocalDB:

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

O LocalDB é uma versão leve do Mecanismo de Banco de Dados do SQL Server Express e destina-se ao desenvolvimento de aplicativos, não ao uso em produção. Por padrão, o LocalDB cria .mdf arquivos no C:/Users/<user> diretório.

Atualizar a classe de contexto do banco de dados

A classe principal que coordena a EF Core funcionalidade para um determinado modelo de dados é a classe de contexto do 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 singular DbSet<Student> Student para o plural DbSet<Student> Students. Para fazer com que o código das Razor Páginas corresponda ao novo DBSet nome, faça uma alteração global a partir de: _context.Student. Para: _context.Students.

São 8 ocorrências.

Como um conjunto de entidades contém várias entidades, muitos desenvolvedores preferem que os nomes de propriedade DBSet sejam plurais.

O código destacado:

  • Cria uma DbSet<TEntity> propriedade para cada conjunto de entidades. Na EF Core terminologia:
    • Um conjunto de entidades normalmente corresponde a uma tabela de banco de dados.
    • Uma entidade corresponde a uma linha na tabela.
  • Chamadas OnModelCreating. OnModelCreating:
    • É chamado quando SchoolContext foi inicializado, mas antes que o modelo tenha sido protegido e usado para inicializar o contexto.
    • É obrigatório porque posteriormente no tutorial a Student entidade terá referências às outras entidades.

Crie o projeto para verificar se não há erros do compilador.

Startup.cs

ASP.NET Core é construído com injeção de dependência. Serviços como o SchoolContext são registados com injeção de dependência durante a inicialização da aplicação. Os componentes que requerem esses serviços, como as Razor páginas, têm esses serviços disponibilizados através de parâmetros do construtor. O código do construtor que obtém uma instância de contexto de banco de dados é mostrado posteriormente no tutorial.

A ferramenta de andaime registrou automaticamente a classe de contexto com o contêiner de injeção de dependência.

O andaime adicionou as seguintes linhas destacadas:

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 chamando um método em um objeto DbContextOptions. Para o desenvolvimento local, o sistema de configuração ASP.NET Core lê a cadeia de conexão do arquivo appsettings.json.

Adicionar o filtro de exceção do banco de dados

Adicionar AddDatabaseDeveloperPageExceptionFilter e UseMigrationsEndPoint como mostrado no código a seguir:

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, digite o seguinte para adicionar o pacote NuGet:

Install-Package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore

O Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore pacote NuGet fornece middleware ASP.NET Core para páginas de erro do Entity Framework Core. Esse middleware ajuda a detetar e diagnosticar erros com migrações do Entity Framework Core.

O AddDatabaseDeveloperPageExceptionFilter fornece informações úteis sobre erros no ambiente de desenvolvimento para erros de migrações do Entity Framework.

Criar a base 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 EnsureCreated método não executa nenhuma ação se existir um banco de dados para o contexto. Se nenhum banco de dados existir, ele criará o banco de dados e o esquema. EnsureCreated Permite o seguinte fluxo de trabalho para lidar com alterações de modelo de dados:

  • Exclua o banco de dados. Todos os dados existentes são perdidos.
  • Altere o modelo de dados. Por exemplo, adicione um EmailAddress campo.
  • 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 introduzidos na base de dados têm de ser preservados. Quando for esse o caso, use migrações.

Mais tarde na série de tutoriais, o banco de dados criado por EnsureCreated é excluído e utilizam-se migrações. Um banco de dados criado por EnsureCreated não pode ser atualizado usando migrações.

Testar a aplicação

  • Execute o aplicativo.
  • Selecione o link Alunos e, em seguida, Criar novo.
  • Teste os links Editar, Detalhes e Excluir.

Semear o banco de dados

O EnsureCreated método cria um banco de dados vazio. Esta seção adiciona código que preenche o banco de dados com 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á algum aluno no banco de dados. Se não houver alunos, adiciona dados de teste ao banco de dados. Ele cria os dados de teste em matrizes em vez de List<T> coleções para otimizar o desempenho.

  • No Program.cs, remova // da DbInitializer.Initialize linha:

      context.Database.EnsureCreated();
      DbInitializer.Initialize(context);
    
  • Pare o aplicativo se ele estiver em execução e execute o seguinte comando no Console do Gerenciador de Pacotes (PMC):

    Drop-Database -Confirm
    
    
  • Responda com Y para excluir o banco de dados.

  • Reinicie o aplicativo.
  • Selecione a página Alunos para ver os dados pré-definidos.

Ver a base de dados

  • Abra o Pesquisador de Objetos do SQL Server (SSOX) no menu Exibir no Visual Studio.
  • Em SSOX, selecione (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. O nome do banco de dados é gerado a partir do nome de 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 rato na tabela Student e clique em Ver Código para ver como o modelo se mapeia Student para o esquema da Student tabela.

Código assíncrono

A programação assíncrona é o modo padrão para ASP.NET Core e 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 sejam liberados. Com o código síncrono, muitos threads podem ficar presos enquanto não estão fazendo trabalho porque estão aguardando a conclusão da E/S. Com o código assíncrono, quando um processo está aguardando a conclusão da E/S, seu thread é liberado para o servidor usar para processar outras solicitações. Como resultado, o código assíncrono permite que os recursos do servidor sejam usados de forma mais eficiente e o servidor pode lidar com 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 tráfego elevado, a melhoria potencial do desempenho é substancial.

No código a seguir, a palavra-chave assíncrona , Task o valor de retorno, await a palavra-chave e ToListAsync o método fazem com que o código seja executado de forma assíncrona.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • A async palavra-chave diz ao compilador para:
    • Gere callbacks para partes do corpo do método.
    • Crie o objeto Task que é retornado.
  • O Task tipo de retorno representa o 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 iniciada de forma assíncrona. A segunda parte é usada numa função de retorno que é chamada quando a operação é concluída.
  • ToListAsync é a versão assíncrona do método de extensão ToList.

Algumas coisas a ter em conta ao escrever código assíncrono que utiliza 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, e SaveChangesAsync. Ele não inclui declarações que apenas alteram um IQueryable, como var students = context.Students.Where(s => s.LastName == "Davolio").
  • Um EF Core contexto não é seguro para execução simultânea: não execute 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 async se chamarem EF Core métodos que enviam consultas para o banco de dados.

Para obter mais informações sobre programação assíncrona no .NET, consulte Visão geral assíncrona e Programação assíncrona com async e await.

Considerações sobre desempenho

Em geral, uma página da Web não deve carregar um número arbitrário de linhas. Uma consulta deve usar paginação ou uma abordagem limitante. Por exemplo, a consulta anterior pode ser usada Take para limitar as linhas retornadas:

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

Enumerar uma tabela extensa numa visualização pode retornar uma resposta HTTP 200 parcialmente construída se uma exceção de base de dados ocorrer a meio do processo de enumeração.

MaxModelBindingCollectionSize o padrão é 1024. O seguinte código 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();
}

Consulte Configuração para obter informações sobre definições de configuração como MyMaxModelBindingCollectionSize.

A paginação é abordada mais adiante no tutorial.

Para obter mais informações, consulte Considerações de desempenho (EF).

Registo SQL do Entity Framework Core

A configuração de registo geralmente é fornecida pela seção Logging de arquivos appsettings.{Environment}.json. Para registrar instruções SQL, 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 Fazendo login no .NET e no ASP.NET Core e este problema do GitHub.

Próximos passos

Usar SQLite para desenvolvimento, SQL Server para produção

Este é o primeiro de uma série de tutoriais que mostram como usar o Entity Framework (EF) Core em um aplicativo ASP.NET Core Razor Pages . Os tutoriais criam um site para uma Universidade Contoso fictícia. O site inclui funcionalidades como admissão de alunos, criação de cursos e tarefas de instrutores. O tutorial utiliza a abordagem code first, que prioriza o código. Para obter informações sobre como seguir este tutorial usando a primeira abordagem do banco de dados, consulte este problema do Github.

Transfira ou veja a aplicação concluída.Instruções para download.

Prerequisites

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 SQLite, um mecanismo de banco de dados de plataforma cruzada.

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 DB Browser for SQLite.

Troubleshooting

Se você encontrar um problema que não pode resolver, compare seu código com o projeto concluído. Uma boa maneira de obter ajuda é postando uma pergunta para StackOverflow.com, usando a tag ASP.NET Core ou a EF Core tag .

O aplicativo de exemplo

O aplicativo construído nesses tutoriais é um site básico da universidade. Os usuários podem visualizar e atualizar informações de alunos, cursos e instrutores. Aqui estão algumas das telas criadas no tutorial.

Página Índice de Estudantes

Alunos Editar página

O estilo da interface do usuário deste site é baseado nos modelos de projeto internos. O foco do tutorial está em como usar EF Coree 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 tutoriais 1-7 podem ser encontrados na pasta cu30snapshots .

Para executar o aplicativo depois de baixar o projeto concluído:

  • Construa o projeto.

  • No Console do Gerenciador de Pacotes (PMC), execute o seguinte comando:

    Update-Database
    
  • Execute o projeto para semear o banco de dados.

Criar o projeto de aplicativo Web

  • No menu Visual Studio Ficheiro, selecione Novo>Projeto.
  • Selecione Aplicação Web ASP.NET Core.
  • Nomeie o projeto como ContosoUniversity. É importante usar esse nome exato, incluindo maiúsculas, para que os namespaces correspondam quando o código é copiado e colado.
  • Selecione .NET Core e ASP.NET Core 3.0 nas listas suspensas e, em seguida, selecione Aplicativo Web.

Configurar o estilo do site

Configure o cabeçalho, rodapé e 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 e Home do Privacy menu e adicione entradas para Sobre, Alunos, Cursos, Instrutores e Departamentos.

As mudanças são destacadas.

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

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

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

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

No Pages/Index.cshtml, substitua o conteúdo do arquivo pelo código a seguir para substituir o texto sobre ASP.NET Core por 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 página inicial aparece.

O modelo de dados

As seções a seguir criam um modelo de dados:

Curso -Enrollment-Student diagrama de modelo de dados

Um aluno pode se inscrever em qualquer número de cursos, e um curso pode ter qualquer número de alunos matriculados nele.

A entidade estudantil

Diagrama de entidade estudantil

  • 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 ID propriedade se torna a coluna de chave primária da tabela de banco de dados que corresponde a essa classe. Por padrão, EF Core interpreta uma propriedade nomeada ID ou classnameID como a chave primária. Portanto, o nome alternativo reconhecido automaticamente para a chave primária da Student classe é StudentID. Para mais informações, consulte EF Core - Chaves.

A propriedade Enrollments é uma propriedade de navegação . As propriedades de navegação mantêm outras entidades que estão associadas a esta entidade. Neste caso, a Enrollments propriedade de uma Student entidade detém todas as Enrollment entidades que estão relacionadas com esse Aluno. Por exemplo, se uma linha Estudante no banco de dados tiver duas linhas de Inscrição relacionadas, a Enrollments propriedade de navegação conterá essas duas entidades de Inscrição.

No banco de dados, uma linha Inscrição está relacionada a uma linha Estudante se sua coluna ID do Aluno contiver o valor da ID do aluno. Por exemplo, suponha que uma linha Student tenha ID=1. As linhas de matrícula relacionadas terão StudentID = 1. StudentID é uma chave estrangeira na tabela de matrícula.

A Enrollments propriedade é 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, EF Core cria uma HashSet<Enrollment> coleção por padrão.

A entidade de Inscrição

Diagrama de entidade de inscrição

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 EnrollmentID propriedade é a chave primária, esta entidade usa o classnameID padrão em vez de ID por si só. 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 ambos funcionam. O uso 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 Grade tipo indica que a Grade propriedade é anulável. Uma nota nula é diferente de uma nota zero — nula significa que uma nota não é conhecida ou ainda não foi atribuída.

A propriedade StudentID é uma chave estrangeira e a propriedade de navegação correspondente é Student. Uma Enrollment entidade está associada a uma Student entidade, portanto, a propriedade contém uma única Student entidade.

A propriedade CourseID é uma chave estrangeira e a propriedade de navegação correspondente é Course. Uma entidade Enrollment está associada a uma entidade Course.

EF Core interpreta uma propriedade como uma chave estrangeira se ela for nomeada <navigation property name><primary key property name>. Por exemplo,StudentID é a chave estrangeira para Student propriedade de navegação, visto que a chave primária da entidade Student é ID. As propriedades da chave estrangeira também podem ser nomeadas <primary key property name>. Por exemplo, CourseID desde que a Course chave primária da entidade seja CourseID.

A entidade do Curso

Diagrama de entidade do curso

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 com qualquer número de entidades Enrollment.

O DatabaseGenerated atributo permite que o aplicativo especifique a chave primária em vez de fazer com que o banco de dados a gere.

Crie o projeto para validar que não há erros do compilador.

Páginas de apoio ao estudante

Nesta seção, use a ferramenta de andaime ASP.NET Core para gerar:

  • Uma classe de EF Corecontexto . O contexto é a classe principal que coordena a funcionalidade do Entity Framework para um determinado modelo de dados. Deriva da Microsoft.EntityFrameworkCore.DbContext classe.
  • Razor páginas que executam operações de Criar, Ler, Atualizar e Excluir (CRUD) para 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 de Andaime.
  • Na caixa de diálogo Adicionar andaime , selecione Razor Páginas usando o Entity Framework (CRUD)>ADD.
  • Na caixa de diálogo Adicionar Razor páginas usando o Entity Framework (CRUD):
    • Na lista suspensa Classe modelo, selecione Estudante (ContosoUniversity.Models).
    • Na linha da classe de contexto de dados, selecione o sinal de + (mais).
    • 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, construa o projeto e tente novamente a etapa do andaime.

O processo de andaime:

  • Cria Razor páginas na pasta Páginas/Alunos :
    • Create.cshtml e Create.cshtml.cs
    • Delete.cshtml e Delete.cshtml.cs
    • Details.cshtml e Details.cshtml.cs
    • Edit.cshtml e Edit.cshtml.cs
    • Index.cshtml e Index.cshtml.cs
  • Cria Data/SchoolContext.cs.
  • Adiciona o contexto à injeção de dependência no Startup.cs.
  • Adiciona uma cadeia de conexão de banco de dados ao appsettings.json.

Cadeia de conexão de banco de dados

O appsettings.json arquivo 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"
  }
}

O LocalDB é uma versão leve do Mecanismo de Banco de Dados do SQL Server Express e destina-se ao desenvolvimento de aplicativos, não ao uso em produção. Por padrão, o LocalDB cria .mdf arquivos no C:/Users/<user> diretório.

Atualizar a classe de contexto do banco de dados

A classe principal que coordena a EF Core funcionalidade para um determinado modelo de dados é a classe de contexto do 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 realçado cria uma DbSet<TEntity> propriedade para cada conjunto de entidades. Na EF Core terminologia:

  • Um conjunto de entidades normalmente corresponde a uma tabela de banco de dados.
  • Uma entidade corresponde a uma linha na tabela.

Como um conjunto de entidades contém várias entidades, as propriedades DBSet devem ser nomes plurais. Como a ferramenta de andaime criou umStudent DBSet, esta etapa o altera para plural Students.

Para fazer com que o código das Razor Páginas se adeque ao novo nome de DBSet, faça uma alteração global em todo o projeto de _context.Student para _context.Students. São 8 ocorrências.

Crie o projeto para verificar se não há erros do compilador.

Startup.cs

ASP.NET Core é construído com injeção de dependência. Os serviços (como o contexto do EF Core banco de dados) são registados com injeção de dependência durante a inicialização da aplicação. Os componentes que necessitam desses serviços (como Razor Pages) recebem esses serviços através de parâmetros do construtor. O código do construtor que obtém uma instância de contexto de banco de dados é mostrado posteriormente no tutorial.

A ferramenta de andaime registrou automaticamente a classe de contexto com o contêiner de injeção de dependência.

  • No ConfigureServices, as linhas destacadas foram adicionadas pelo andaime:

    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 chamando um método em um objeto DbContextOptions. Para o desenvolvimento local, o sistema de configuração ASP.NET Core lê a cadeia de conexão do arquivo appsettings.json.

Criar a base 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 EnsureCreated método não executa nenhuma ação se existir um banco de dados para o contexto. Se nenhum banco de dados existir, ele criará o banco de dados e o esquema. EnsureCreated Permite o seguinte fluxo de trabalho para lidar com alterações de modelo de dados:

  • Exclua o banco de dados. Todos os dados existentes são perdidos.
  • Altere o modelo de dados. Por exemplo, adicione um EmailAddress campo.
  • 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 dados. A situação é diferente quando os dados introduzidos na base de dados têm de ser preservados. Quando for esse o caso, use migrações.

Mais adiante no tutorial, elimina o banco de dados que foi criado por EnsureCreated e faz uso de migrações em vez disso. Um banco de dados criado por EnsureCreated não pode ser atualizado usando migrações.

Testar a aplicação

  • Execute o aplicativo.
  • Selecione o link Alunos e, em seguida, Criar novo.
  • Teste os links Editar, Detalhes e Excluir.

Semear o banco de dados

O EnsureCreated método cria um banco de dados vazio. Esta seção adiciona código que preenche o banco de dados com 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á algum aluno no banco de dados. Se não houver alunos, adiciona dados de teste ao banco de dados. Ele cria os dados de teste em matrizes em vez de List<T> coleções para otimizar o desempenho.

  • No Program.cs, substitua a chamada de EnsureCreated por uma chamada de DbInitializer.Initialize:

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

Pare o aplicativo se ele estiver em execução e execute o seguinte comando no Console do Gerenciador de Pacotes (PMC):

Drop-Database
  • Reinicie o aplicativo.

  • Selecione a página Alunos para ver os dados pré-definidos.

Ver a base de dados

  • Abra o Pesquisador de Objetos do SQL Server (SSOX) no menu Exibir no Visual Studio.
  • Em SSOX, selecione (localdb)\MSSQLLocalDB > Databases > SchoolContext-{GUID}. O nome do banco de dados é gerado a partir do nome de 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 rato na tabela Student e clique em Ver Código para ver como o modelo se mapeia Student para o esquema da Student tabela.

Código assíncrono

A programação assíncrona é o modo padrão para ASP.NET Core e 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 sejam liberados. Com o código síncrono, muitas threads podem ser bloqueadas sem realizar nenhum trabalho porque estão aguardando a conclusão das operações de E/S. Com o código assíncrono, quando um processo está aguardando a conclusão da E/S, seu thread é liberado para o servidor usar para processar outras solicitações. Como resultado, o código assíncrono permite que os recursos do servidor sejam usados de forma mais eficiente e o servidor pode lidar com 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 tráfego elevado, a melhoria potencial do desempenho é substancial.

No código a seguir, a palavra-chave assíncrona , Task<T> o valor de retorno, await a palavra-chave e ToListAsync o método fazem com que o código seja executado de forma assíncrona.

public async Task OnGetAsync()
{
    Students = await _context.Students.ToListAsync();
}
  • A async palavra-chave diz ao compilador para:
    • Gere callbacks para partes do corpo do método.
    • Crie o objeto Task que é retornado.
  • O Task<T> tipo de retorno representa o 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 iniciada de forma assíncrona. A segunda parte é usada numa função de retorno que é chamada quando a operação é concluída.
  • ToListAsync é a versão assíncrona do método de extensão ToList.

Algumas coisas a ter em conta ao escrever código assíncrono que utiliza 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, e SaveChangesAsync. Ele não inclui declarações que apenas alteram um IQueryable, como var students = context.Students.Where(s => s.LastName == "Davolio").
  • Um EF Core contexto não é seguro para execução simultânea: não execute 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 async se chamarem EF Core métodos que enviam consultas para o banco de dados.

Para obter mais informações sobre programação assíncrona no .NET, consulte Visão geral assíncrona e Programação assíncrona com async e await.

Próximos passos