Parte 4, adicione um modelo a um aplicativo ASP.NET Core MVC

Observação

Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

Importante

Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.

Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.

By Rick Anderson e Jon P Smith.

Neste tutorial, classes são adicionadas para o gerenciamento de filmes em um banco de dados. Essas classes serão a parte do “Modelo” parte do aplicativo MVC.

Essas classes do modelo são usadas com o Entity Framework Core (EF Core) para trabalhar com um banco de dados. O EF Core é uma estrutura ORM (mapeamento relacional de objetos) que simplifica o código de acesso a dados que você precisa escrever.

As classes de modelo criadas são conhecidas como classes POCO , de Plain Old CLR Objects (Bons e velhos objetos do CLR). As classes POCO não têm nenhuma dependência em EF Core. Elas definem as propriedades dos dados a serem armazenados no banco de dados.

Neste tutorial, você escreve as classes de modelo primeiro e o EF Core cria o banco de dados.

Adicionar uma classe de modelo de dados

Clique com o botão direito do mouse na pasta Modelos>Adicionar>Classe. Dê o nome Movie.cs para o arquivo.

Atualize o arquivo Models/Movie.cs com o seguinte código:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

A classe Movie contém um campo Id, que é exigido pelo banco de dados para a chave primária.

O atributo DataType em ReleaseDate especifica o tipo de dados (Date). Com esse atributo:

  • O usuário não precisa inserir informações de horário no campo de data.
  • Somente a data é exibida, não as informações de tempo.

DataAnnotations são abordados em um tutorial posterior.

O ponto de interrogação após string indica que a propriedade permite valor nulo. Para obter mais informações, confira Tipos de referência anuláveis.

Adicionar pacotes do NuGet

O Visual Studio instala automaticamente os pacotes necessários.

Compile o projeto como uma verificação de erros do compilador.

Aplicar scaffold a páginas de filme

Use a ferramenta scaffolding para produzir páginas CRUD (Create, Read, Update e Delete) para o modelo de filme.

Clique com o botão direito do mouse na pasta Controladores do Gerenciador de Soluções e selecione Adicionar > Novo Item Gerado por Scaffolding.

exibição da etapa acima

No diálogo Adicionar novo item de Scaffold:

  • No painel esquerdo, selecione Instalado>Comum>MVC.
  • Selecione Controlador MVC com exibições, usando o Entity Framework.
  • Selecione Adicionar.

Caixa de diálogo Adicionar Scaffold

Complete a caixa de diálogo Adicionar Controlador MVC com exibições, usando o Entity Framework:

  • Na lista suspensa Classe de modelo, selecione Filme (MvcMovie.Models).
  • Na linha Classe de contexto de dados, selecione o sinal + (adição).
    • Na caixa de diálogo Adicionar Contexto de Dados , o nome da classe MvcMovie.Data.MvcMovieContext é gerado.
    • Selecione Adicionar.
  • Na lista suspensa Provedor de banco de dados, selecione SQL Server.
  • Exibições e Nome do controlador: mantenha o padrão.
  • Selecione Adicionar.

Adicionar contexto de dados mantém os padrões

Se você receber uma mensagem de erro, selecione Adicionar uma segunda vez para tentar novamente.

O scaffolding adiciona os seguintes pacotes:

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

O scaffolding cria o seguinte:

  • Um controlador de filmes: Controllers/MoviesController.cs
  • Razor exibir arquivos para páginas Criar, Excluir, Detalhes, Editar e Índice : Views/Movies/*.cshtml
  • Uma classe de contexto de banco de dados: Data/MvcMovieContext.cs

O scaffolding atualiza o seguinte:

  • Insere as referências de pacote necessárias no arquivo de projeto MvcMovie.csproj.
  • Registra o contexto do banco de dados no arquivo Program.cs.
  • Adicionar uma cadeia de caracteres de conexão do banco de dados ao arquivo appsettings.json.

A criação automática desses arquivos e atualizações de arquivos é conhecida como scaffolding.

As páginas com scaffolding ainda não podem ser usadas porque o banco de dados não existe. Executar o aplicativo e selecionar o link Aplicativo de Filme resulta em uma mensagem de erro Não é possível abrir o banco de dados ou nenhuma tabela desse tipo: filme.

Crie o aplicativo para verificar se não há erros.

Migração inicial

Use o recurso EF CoreMigrações para criar o banco de dados. O recurso Migrações é um conjunto de ferramentas que cria e atualiza um banco de dados para corresponder ao modelo de dados.

No menu Ferramentas, selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes.

No PMC (Console do Gerenciador de Pacotes), Insira os seguintes comandos:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: gera um arquivo de migração Migrations/{timestamp}_InitialCreate.cs. O argumento InitialCreate é o nome da migração. Qualquer nome pode ser usado, mas, por convenção, um nome que descreve a migração é selecionado. Como essa é a primeira migração, a classe gerada contém o código para criar o esquema de banco de dados. O esquema de banco de dados é baseado no modelo especificado na classe MvcMovieContext.

  • Update-Database: atualiza o banco de dados para a migração mais recente, que o comando anterior criou. Esse comando executa o método Up no arquivo Migrations/{time-stamp}_InitialCreate.cs, que cria o banco de dados.

O comando Update-Database gera o seguinte aviso:

Nenhum tipo de repositório foi especificado para a propriedade decimal 'Price' no tipo de entidade 'Movie'. Isso fará com que valores sejam truncados silenciosamente se não couberem na precisão e na escala padrão. Especifique explicitamente o tipo de coluna do SQL Server que pode acomodar todos os valores em 'OnModelCreating' usando 'HasColumnType', especifique precisão e escala usando 'HasPrecision' ou configure um conversor de valor usando 'HasConversion'.

Ignore o aviso anterior, ele é corrigido em um tutorial posterior.

Para obter mais informações sobre as ferramentas PMC para EF Core, veja a referência de ferramentas de EF Core – PMC no Visual Studio.

Testar o aplicativo

Execute o aplicativo e clique no link Aplicativo de Filme.

Se você receber uma exceção semelhante à seguinte, talvez tenha perdido o comando Update-Database na etapa de migrações:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Observação

Talvez você não consiga inserir casas decimais ou vírgulas no campo Price. Para dar suporte à validação do jQuery para localidades com idiomas diferentes do inglês que usam uma vírgula (",") para um ponto decimal e formatos de data diferentes do inglês dos EUA, o aplicativo precisa ser globalizado. Para obter instruções sobre a globalização, consulte esse problema no GitHub.

Examinar a classe de contexto e o registro do banco de dados gerados

Com o EF Core, o acesso aos dados é executado usando um modelo. Um modelo é feito de classes de entidade e um objeto de contexto que representa uma sessão com o banco de dados. O objeto de contexto permite consultar e salvar dados. O contexto de banco de dados é derivado de Microsoft. EntityFrameworkCore.DbContext e especifica as entidades a serem incluídas no modelo de dados.

O scaffolding cria a classe de contexto do banco de dados Data/MvcMovieContext.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

O código anterior cria uma propriedade DbSet<Movie> que representa os filmes no banco de dados.

Injeção de dependência

O ASP.NET Core foi criado com a DI (injeção de dependência). Serviços, como o contexto do banco de dados, são registrados com DI no Program.cs. Esses serviços são fornecidos aos componentes que necessitam deles através de parâmetros do construtor.

No arquivo Controllers/MoviesController.cs, o construtor usa a Injeção de Dependência para injetar o contexto do banco de dados MvcMovieContext no controlador. O contexto de banco de dados é usado em cada um dos métodos CRUD no controlador.

O scaffolding gerou o seguinte código realçado em Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

O sistema de configuração ASP.NET Core faz a leitura da cadeia de caracteres de conexão de banco de dados "MvcMovieContext".

Examinar a cadeia de caracteres de conexão de banco de dados gerada

O scaffolding adicionou uma cadeia de conexão ao arquivo appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

No desenvolvimento local, o sistema de configuração do ASP.NET Core lê a chave ConnectionString do arquivo appsettings.json.

A classe InitialCreate

Examinar o arquivo de migração Migrations/{timestamp}_InitialCreate.cs:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

No código anterior:

  • InitialCreate.Up cria a tabela de Filmes e configura Id como a chave primária.
  • O método InitialCreate.Down reverte as alterações de esquema feitas pela migração de Up.

Injeção de dependência no controlador

Abra o arquivo Controllers/MoviesController.cs e examine o construtor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

O construtor usa a Injeção de Dependência para injetar o contexto de banco de dados (MvcMovieContext) no controlador. O contexto de banco de dados é usado em cada um dos métodos CRUD no controlador.

Teste a página Criar. Inserir e enviar dados.

Teste os links Editar, Detalhes e Excluir.

Modelos fortemente tipados e a diretiva @model

Anteriormente neste tutorial, você viu como um controlador pode passar dados ou objetos para uma exibição usando o dicionário ViewData. O dicionário ViewData é um objeto dinâmico que fornece uma maneira conveniente de associação tardia para passar informações para uma exibição.

O MVC também fornece a capacidade de passar objetos de modelo fortemente tipados para uma exibição. Essa abordagem fortemente tipada permite a verificação de código em tempo de compilação. O mecanismo de scaffolding passou um modelo fortemente tipado na classe e nas exibições de MoviesController.

Examine o método de Details gerado no arquivo Controllers/MoviesController.cs :

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

O parâmetro id geralmente é passado como dados de rota. Por exemplo, https://localhost:5001/movies/details/1 define:

  • O controlador para o controlador movies, o primeiro segmento da URL.
  • A ação para details, o segundo segmento da URL.
  • O idpara 1, o último segmento da URL.

O id pode ser passado com uma cadeia de caracteres de consulta, como no exemplo a seguir:

https://localhost:5001/movies/details?id=1

O parâmetro id é definido como um tipo que permite valor nulo (int?) nos casos em que o valor de id não seja fornecido.

Um expressão lambda é passada para o método FirstOrDefaultAsync para selecionar as entidades de filmes que correspondem ao valor dos dados da rota ou da cadeia de consulta.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Se for encontrado um filme, uma instância do modelo Movie será passada para a exibição Details:

return View(movie);

Examinar o conteúdo do arquivo Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

A instrução @model na parte superior do arquivo de exibição especifica o tipo de objeto que a exibição espera. Quando o controlador de filme foi criado, a seguinte instrução @model foi incluída:

@model MvcMovie.Models.Movie

Essa diretiva @model permite o acesso ao filme que o controlador passou para a exibição. O objeto Model é fortemente tipado. Por exemplo, na exibição Details.cshtml, o código passa cada campo de filme para os Auxiliares de HTML DisplayNameFor e DisplayFor com o objeto fortemente tipado Model. Os métodos Create e Edit e as exibições também passam um objeto de modelo Movie.

Examine a exibição Index.cshtml e o método Index no controlador Filmes. Observe como o código cria um objeto List quando ele chama o método View. O código passa esta lista Movies do método de ação Index para a exibição:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

O código retornará os detalhes do problema se a propriedade Movie do contexto dos dados for nula.

Quando o controlador de filmes foi criado, o scaffolding incluiu a seguinte instrução @model na parte superior do arquivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

A diretiva @model permite acessar a lista de filmes que o controlador passou para a exibição usando um objeto Model fortemente tipado. Por exemplo, na exibição Index.cshtml o código executa um loop pelos filmes com uma instrução foreach no objeto Model fortemente tipado:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Como o objeto Model é fortemente tipado como um objeto IEnumerable<Movie>, cada item no loop é tipado como Movie. Entre outros benefícios, o compilador valida os tipos usados no código.

Recursos adicionais

Neste tutorial, classes são adicionadas para o gerenciamento de filmes em um banco de dados. Essas classes serão a parte do “Modelo” parte do aplicativo MVC.

Essas classes do modelo são usadas com o Entity Framework Core (EF Core) para trabalhar com um banco de dados. O EF Core é uma estrutura ORM (mapeamento relacional de objetos) que simplifica o código de acesso a dados que você precisa escrever.

As classes de modelo criadas são conhecidas como classes POCO , de Plain Old CLR Objects (Bons e velhos objetos do CLR). As classes POCO não têm nenhuma dependência em EF Core. Elas definem as propriedades dos dados a serem armazenados no banco de dados.

Neste tutorial, você escreve as classes de modelo primeiro e o EF Core cria o banco de dados.

Adicionar uma classe de modelo de dados

Clique com o botão direito do mouse na pasta Modelos>Adicionar>Classe. Dê o nome Movie.cs para o arquivo.

Atualize o arquivo Models/Movie.cs com o seguinte código:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models;

public class Movie
{
    public int Id { get; set; }
    public string? Title { get; set; }
    [DataType(DataType.Date)]
    public DateTime ReleaseDate { get; set; }
    public string? Genre { get; set; }
    public decimal Price { get; set; }
}

A classe Movie contém um campo Id, que é exigido pelo banco de dados para a chave primária.

O atributo DataType em ReleaseDate especifica o tipo de dados (Date). Com esse atributo:

  • O usuário não precisa inserir informações de horário no campo de data.
  • Somente a data é exibida, não as informações de tempo.

DataAnnotations são abordados em um tutorial posterior.

O ponto de interrogação após string indica que a propriedade permite valor nulo. Para obter mais informações, confira Tipos de referência anuláveis.

Adicionar pacotes do NuGet

O Visual Studio instala automaticamente os pacotes necessários.

Compile o projeto como uma verificação de erros do compilador.

Aplicar scaffold a páginas de filme

Use a ferramenta scaffolding para produzir páginas CRUD (Create, Read, Update e Delete) para o modelo de filme.

Clique com o botão direito do mouse na pasta Controladores do Gerenciador de Soluções e selecione Adicionar > Novo Item Gerado por Scaffolding.

exibição da etapa acima

No diálogo Adicionar novo item de Scaffold:

  • No painel esquerdo, selecione Instalado>Comum>MVC.
  • Selecione Controlador MVC com exibições, usando o Entity Framework.
  • Selecione Adicionar.

Caixa de diálogo Adicionar Scaffold

Complete a caixa de diálogo Adicionar Controlador MVC com exibições, usando o Entity Framework:

  • Na lista suspensa Classe de modelo, selecione Filme (MvcMovie.Models).
  • Na linha Classe de contexto de dados, selecione o sinal + (adição).
    • Na caixa de diálogo Adicionar Contexto de Dados , o nome da classe MvcMovie.Data.MvcMovieContext é gerado.
    • Selecione Adicionar.
  • Na lista suspensa Provedor de banco de dados, selecione SQL Server.
  • Exibições e Nome do controlador: mantenha o padrão.
  • Selecione Adicionar.

Adicionar contexto de dados mantém os padrões Se você receber uma mensagem de erro, selecione Adicionar uma segunda vez para tentar novamente.

O scaffolding adiciona os seguintes pacotes:

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

O scaffolding cria o seguinte:

  • Um controlador de filmes: Controllers/MoviesController.cs
  • Razor exibir arquivos para páginas Criar, Excluir, Detalhes, Editar e Índice : Views/Movies/*.cshtml
  • Uma classe de contexto de banco de dados: Data/MvcMovieContext.cs

O scaffolding atualiza o seguinte:

  • Insere as referências de pacote necessárias no arquivo de projeto MvcMovie.csproj.
  • Registra o contexto do banco de dados no arquivo Program.cs.
  • Adicionar uma cadeia de caracteres de conexão do banco de dados ao arquivo appsettings.json.

A criação automática desses arquivos e atualizações de arquivos é conhecida como scaffolding.

As páginas com scaffolding ainda não podem ser usadas porque o banco de dados não existe. Executar o aplicativo e selecionar o link Aplicativo de Filme resulta em uma mensagem de erro Não é possível abrir o banco de dados ou nenhuma tabela desse tipo: filme.

Crie o aplicativo para verificar se não há erros.

Migração inicial

Use o recurso EF CoreMigrações para criar o banco de dados. O recurso Migrações é um conjunto de ferramentas que cria e atualiza um banco de dados para corresponder ao modelo de dados.

No menu Ferramentas, selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes.

No PMC (Console do Gerenciador de Pacotes), Insira os seguintes comandos:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: gera um arquivo de migração Migrations/{timestamp}_InitialCreate.cs. O argumento InitialCreate é o nome da migração. Qualquer nome pode ser usado, mas, por convenção, um nome que descreve a migração é selecionado. Como essa é a primeira migração, a classe gerada contém o código para criar o esquema de banco de dados. O esquema de banco de dados é baseado no modelo especificado na classe MvcMovieContext.

  • Update-Database: atualiza o banco de dados para a migração mais recente, que o comando anterior criou. Esse comando executa o método Up no arquivo Migrations/{time-stamp}_InitialCreate.cs, que cria o banco de dados.

O comando Update-Database gera o seguinte aviso:

Nenhum tipo foi especificado para a coluna decimal 'Preço' no tipo de entidade 'Filme'. Isso fará com que valores sejam truncados silenciosamente se não couberem na precisão e na escala padrão. Especifique explicitamente o tipo de coluna do SQL Server que pode acomodar todos os valores usando 'HasColumnType()'.

Ignore o aviso anterior, ele é corrigido em um tutorial posterior.

Para obter mais informações sobre as ferramentas PMC para EF Core, veja a referência de ferramentas de EF Core – PMC no Visual Studio.

Testar o aplicativo

Execute o aplicativo e clique no link Aplicativo de Filme.

Se você receber uma exceção semelhante à seguinte, talvez tenha perdido o comando Update-Database na etapa de migrações:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Observação

Talvez você não consiga inserir casas decimais ou vírgulas no campo Price. Para dar suporte à validação do jQuery para localidades com idiomas diferentes do inglês que usam uma vírgula (",") para um ponto decimal e formatos de data diferentes do inglês dos EUA, o aplicativo precisa ser globalizado. Para obter instruções sobre a globalização, consulte esse problema no GitHub.

Examinar a classe de contexto e o registro do banco de dados gerados

Com o EF Core, o acesso aos dados é executado usando um modelo. Um modelo é feito de classes de entidade e um objeto de contexto que representa uma sessão com o banco de dados. O objeto de contexto permite consultar e salvar dados. O contexto de banco de dados é derivado de Microsoft. EntityFrameworkCore.DbContext e especifica as entidades a serem incluídas no modelo de dados.

O scaffolding cria a classe de contexto do banco de dados Data/MvcMovieContext.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

O código anterior cria uma propriedade DbSet<Movie> que representa os filmes no banco de dados.

Injeção de dependência

O ASP.NET Core foi criado com a DI (injeção de dependência). Serviços, como o contexto do banco de dados, são registrados com DI no Program.cs. Esses serviços são fornecidos aos componentes que necessitam deles através de parâmetros do construtor.

No arquivo Controllers/MoviesController.cs, o construtor usa a Injeção de Dependência para injetar o contexto do banco de dados MvcMovieContext no controlador. O contexto de banco de dados é usado em cada um dos métodos CRUD no controlador.

O scaffolding gerou o seguinte código realçado em Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

O sistema de configuração ASP.NET Core faz a leitura da cadeia de caracteres de conexão de banco de dados "MvcMovieContext".

Examinar a cadeia de caracteres de conexão de banco de dados gerada

O scaffolding adicionou uma cadeia de conexão ao arquivo appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "MvcMovieContext": "Data Source=MvcMovieContext-ea7a4069-f366-4742-bd1c-3f753a804ce1.db"
  }
}

No desenvolvimento local, o sistema de configuração do ASP.NET Core lê a chave ConnectionString do arquivo appsettings.json.

A classe InitialCreate

Examinar o arquivo de migração Migrations/{timestamp}_InitialCreate.cs:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

No código anterior:

  • InitialCreate.Up cria a tabela de Filmes e configura Id como a chave primária.
  • O método InitialCreate.Down reverte as alterações de esquema feitas pela migração de Up.

Injeção de dependência no controlador

Abra o arquivo Controllers/MoviesController.cs e examine o construtor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

O construtor usa a Injeção de Dependência para injetar o contexto de banco de dados (MvcMovieContext) no controlador. O contexto de banco de dados é usado em cada um dos métodos CRUD no controlador.

Teste a página Criar. Inserir e enviar dados.

Teste os links Editar, Detalhes e Excluir.

Modelos fortemente tipados e a diretiva @model

Anteriormente neste tutorial, você viu como um controlador pode passar dados ou objetos para uma exibição usando o dicionário ViewData. O dicionário ViewData é um objeto dinâmico que fornece uma maneira conveniente de associação tardia para passar informações para uma exibição.

O MVC também fornece a capacidade de passar objetos de modelo fortemente tipados para uma exibição. Essa abordagem fortemente tipada permite a verificação de código em tempo de compilação. O mecanismo de scaffolding passou um modelo fortemente tipado na classe e nas exibições de MoviesController.

Examine o método de Details gerado no arquivo Controllers/MoviesController.cs :

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

O parâmetro id geralmente é passado como dados de rota. Por exemplo, https://localhost:5001/movies/details/1 define:

  • O controlador para o controlador movies, o primeiro segmento da URL.
  • A ação para details, o segundo segmento da URL.
  • O idpara 1, o último segmento da URL.

O id pode ser passado com uma cadeia de caracteres de consulta, como no exemplo a seguir:

https://localhost:5001/movies/details?id=1

O parâmetro id é definido como um tipo que permite valor nulo (int?) nos casos em que o valor de id não seja fornecido.

Um expressão lambda é passada para o método FirstOrDefaultAsync para selecionar as entidades de filmes que correspondem ao valor dos dados da rota ou da cadeia de consulta.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Se for encontrado um filme, uma instância do modelo Movie será passada para a exibição Details:

return View(movie);

Examinar o conteúdo do arquivo Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

A instrução @model na parte superior do arquivo de exibição especifica o tipo de objeto que a exibição espera. Quando o controlador de filme foi criado, a seguinte instrução @model foi incluída:

@model MvcMovie.Models.Movie

Essa diretiva @model permite o acesso ao filme que o controlador passou para a exibição. O objeto Model é fortemente tipado. Por exemplo, na exibição Details.cshtml, o código passa cada campo de filme para os Auxiliares de HTML DisplayNameFor e DisplayFor com o objeto fortemente tipado Model. Os métodos Create e Edit e as exibições também passam um objeto de modelo Movie.

Examine a exibição Index.cshtml e o método Index no controlador Filmes. Observe como o código cria um objeto List quando ele chama o método View. O código passa esta lista Movies do método de ação Index para a exibição:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

O código retornará os detalhes do problema se a propriedade Movie do contexto dos dados for nula.

Quando o controlador de filmes foi criado, o scaffolding incluiu a seguinte instrução @model na parte superior do arquivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

A diretiva @model permite acessar a lista de filmes que o controlador passou para a exibição usando um objeto Model fortemente tipado. Por exemplo, na exibição Index.cshtml o código executa um loop pelos filmes com uma instrução foreach no objeto Model fortemente tipado:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Como o objeto Model é fortemente tipado como um objeto IEnumerable<Movie>, cada item no loop é tipado como Movie. Entre outros benefícios, o compilador valida os tipos usados no código.

Recursos adicionais

Neste tutorial, classes são adicionadas para o gerenciamento de filmes em um banco de dados. Essas classes serão a parte do “Modelo” parte do aplicativo MVC.

Essas classes do modelo são usadas com o Entity Framework Core (EF Core) para trabalhar com um banco de dados. O EF Core é uma estrutura ORM (mapeamento relacional de objetos) que simplifica o código de acesso a dados que você precisa escrever.

As classes de modelo criadas são conhecidas como classes POCO , de Plain Old CLR Objects (Bons e velhos objetos do CLR). As classes POCO não têm nenhuma dependência em EF Core. Elas definem as propriedades dos dados a serem armazenados no banco de dados.

Neste tutorial, você escreve as classes de modelo primeiro e o EF Core cria o banco de dados.

Adicionar uma classe de modelo de dados

Clique com o botão direito do mouse na pasta Modelos>Adicionar>Classe. Dê o nome Movie.cs para o arquivo.

Atualize o arquivo Models/Movie.cs com o seguinte código:

using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string? Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string? Genre { get; set; }
        public decimal Price { get; set; }
    }
}

A classe Movie contém um campo Id, que é exigido pelo banco de dados para a chave primária.

O atributo DataType em ReleaseDate especifica o tipo de dados (Date). Com esse atributo:

  • O usuário não precisa inserir informações de horário no campo de data.
  • Somente a data é exibida, não as informações de tempo.

DataAnnotations são abordados em um tutorial posterior.

O ponto de interrogação após string indica que a propriedade permite valor nulo. Para obter mais informações, confira Tipos de referência anuláveis.

Adicionar pacotes do NuGet

No menu Ferramentas, selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes (PMC).

Menu do PMC

No PMC, execute o seguinte comando:

Install-Package Microsoft.EntityFrameworkCore.Design
Install-Package Microsoft.EntityFrameworkCore.SqlServer

Os comandos anteriores adicionam:

  • O provedor EF Core do SQL Server. O pacote do provedor instala o pacote do EF Core como uma dependência.
  • Os utilitários usados pelos pacotes instalados automaticamente na etapa de scaffolding, mais adiante no tutorial.

Compile o projeto como uma verificação de erros do compilador.

Aplicar scaffold a páginas de filme

Use a ferramenta scaffolding para produzir páginas CRUD (Create, Read, Update e Delete) para o modelo de filme.

Clique com o botão direito do mouse na pasta Controladores do Gerenciador de Soluções e selecione Adicionar > Novo Item Gerado por Scaffolding.

exibição da etapa acima

Na caixa de diálogo Adicionar Scaffold, selecione Controlador MVC com exibições, usando o Entity Framework > Adicionar.

Caixa de diálogo Adicionar Scaffold

Complete a caixa de diálogo Adicionar Controlador MVC com exibições, usando o Entity Framework:

  • Na lista suspensa Classe de modelo, selecione Filme (MvcMovie.Models).
  • Na linha Classe de contexto de dados, selecione o sinal + (adição).
    • Na caixa de diálogo Adicionar Contexto de Dados , o nome da classe MvcMovie.Data.MvcMovieContext é gerado.
    • Selecione Adicionar.
  • Exibições e Nome do controlador: mantenha o padrão.
  • Selecione Adicionar.

Adicionar contexto de dados mantém os padrões

Se você receber uma mensagem de erro, selecione Adicionar uma segunda vez para tentar novamente.

O scaffolding atualiza o seguinte:

  • Insere as referências de pacote necessárias no arquivo de projeto MvcMovie.csproj.
  • Registra o contexto do banco de dados no arquivo Program.cs.
  • Adiciona uma cadeia de caracteres de conexão do banco de dados ao arquivo appsettings.json.

O scaffolding cria o seguinte:

  • Um controlador de filmes: Controllers/MoviesController.cs
  • Razor exibir arquivos para páginas Criar, Excluir, Detalhes, Editar e Índice : Views/Movies/*.cshtml
  • Uma classe de contexto de banco de dados: Data/MvcMovieContext.cs

A criação automática desses arquivos e atualizações de arquivos é conhecida como scaffolding.

As páginas com scaffolding ainda não podem ser usadas porque o banco de dados não existe. Executar o aplicativo e selecionar o link Aplicativo de Filme resulta em uma mensagem de erro Não é possível abrir o banco de dados ou nenhuma tabela desse tipo: filme.

Criar o aplicativo

Crie o aplicativo. O compilador gera vários avisos sobre como os valores de null são tratados. Consulte este tópico do GitHub e tipos de referência anuláveis para obter mais informações.

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

<Nullable>enable</Nullable>

Esperamos ter corrigido esse problema na próxima versão.

Migração inicial

Use o recurso EF CoreMigrações para criar o banco de dados. O recurso Migrações é um conjunto de ferramentas que cria e atualiza um banco de dados para corresponder ao modelo de dados.

No menu Ferramentas, selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes.

No PMC (Console do Gerenciador de Pacotes), Insira os seguintes comandos:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: gera um arquivo de migração Migrations/{timestamp}_InitialCreate.cs. O argumento InitialCreate é o nome da migração. Qualquer nome pode ser usado, mas, por convenção, um nome que descreve a migração é selecionado. Como essa é a primeira migração, a classe gerada contém o código para criar o esquema de banco de dados. O esquema de banco de dados é baseado no modelo especificado na classe MvcMovieContext.

  • Update-Database: atualiza o banco de dados para a migração mais recente, que o comando anterior criou. Esse comando executa o método Up no arquivo Migrations/{time-stamp}_InitialCreate.cs, que cria o banco de dados.

O comando Update-Database gera o seguinte aviso:

Nenhum tipo foi especificado para a coluna decimal 'Preço' no tipo de entidade 'Filme'. Isso fará com que valores sejam truncados silenciosamente se não couberem na precisão e na escala padrão. Especifique explicitamente o tipo de coluna do SQL Server que pode acomodar todos os valores usando 'HasColumnType()'.

Ignore o aviso anterior, ele é corrigido em um tutorial posterior.

Para obter mais informações sobre as ferramentas PMC para EF Core, veja a referência de ferramentas de EF Core – PMC no Visual Studio.

Testar o aplicativo

Execute o aplicativo e clique no link Aplicativo de Filme.

Se você receber uma exceção semelhante à seguinte, talvez tenha perdido a etapa de migrações:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Observação

Talvez você não consiga inserir casas decimais ou vírgulas no campo Price. Para dar suporte à validação do jQuery para localidades com idiomas diferentes do inglês que usam uma vírgula (",") para um ponto decimal e formatos de data diferentes do inglês dos EUA, o aplicativo precisa ser globalizado. Para obter instruções sobre a globalização, consulte esse problema no GitHub.

Examinar a classe de contexto e o registro do banco de dados gerados

Com o EF Core, o acesso aos dados é executado usando um modelo. Um modelo é feito de classes de entidade e um objeto de contexto que representa uma sessão com o banco de dados. O objeto de contexto permite consultar e salvar dados. O contexto de banco de dados é derivado de Microsoft. EntityFrameworkCore.DbContext e especifica as entidades a serem incluídas no modelo de dados.

O scaffolding cria a classe de contexto do banco de dados Data/MvcMovieContext.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<MvcMovie.Models.Movie> Movie { get; set; }
    }
}

O código anterior cria uma propriedade DbSet<Movie> que representa os filmes no banco de dados.

Injeção de dependência

O ASP.NET Core foi criado com a DI (injeção de dependência). Serviços, como o contexto do banco de dados, são registrados com DI no Program.cs. Esses serviços são fornecidos aos componentes que necessitam deles através de parâmetros do construtor.

No arquivo Controllers/MoviesController.cs, o construtor usa a Injeção de Dependência para injetar o contexto do banco de dados MvcMovieContext no controlador. O contexto de banco de dados é usado em cada um dos métodos CRUD no controlador.

O scaffolding gerou o seguinte código realçado em Program.cs:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddDbContext<MvcMovieContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("MvcMovieContext")));

O sistema de configuração ASP.NET Core faz a leitura da cadeia de caracteres de conexão de banco de dados "MvcMovieContext".

Examinar a cadeia de caracteres de conexão de banco de dados gerada

O scaffolding adicionou uma cadeia de conexão ao arquivo appsettings.json:

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

No desenvolvimento local, o sistema de configuração do ASP.NET Core lê a chave ConnectionString do arquivo appsettings.json.

A classe InitialCreate

Examinar o arquivo de migração Migrations/{timestamp}_InitialCreate.cs:

using System;
using Microsoft.EntityFrameworkCore.Migrations;

#nullable disable

namespace MvcMovie.Migrations
{
    public partial class InitialCreate : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.CreateTable(
                name: "Movie",
                columns: table => new
                {
                    Id = table.Column<int>(type: "int", nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                    Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                    Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Movie", x => x.Id);
                });
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            migrationBuilder.DropTable(
                name: "Movie");
        }
    }
}

No código anterior:

  • InitialCreate.Up cria a tabela de Filmes e configura Id como a chave primária.
  • O método InitialCreate.Down reverte as alterações de esquema feitas pela migração de Up.

Injeção de dependência no controlador

Abra o arquivo Controllers/MoviesController.cs e examine o construtor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

O construtor usa a Injeção de Dependência para injetar o contexto de banco de dados (MvcMovieContext) no controlador. O contexto de banco de dados é usado em cada um dos métodos CRUD no controlador.

Teste a página Criar. Inserir e enviar dados.

Teste os links Editar, Detalhes e Excluir.

Modelos fortemente tipados e a diretiva @model

Anteriormente neste tutorial, você viu como um controlador pode passar dados ou objetos para uma exibição usando o dicionário ViewData. O dicionário ViewData é um objeto dinâmico que fornece uma maneira conveniente de associação tardia para passar informações para uma exibição.

O MVC também fornece a capacidade de passar objetos de modelo fortemente tipados para uma exibição. Essa abordagem fortemente tipada permite a verificação de código em tempo de compilação. O mecanismo de scaffolding passou um modelo fortemente tipado na classe e nas exibições de MoviesController.

Examine o método de Details gerado no arquivo Controllers/MoviesController.cs :

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

O parâmetro id geralmente é passado como dados de rota. Por exemplo, https://localhost:5001/movies/details/1 define:

  • O controlador para o controlador movies, o primeiro segmento da URL.
  • A ação para details, o segundo segmento da URL.
  • O idpara 1, o último segmento da URL.

O id pode ser passado com uma cadeia de caracteres de consulta, como no exemplo a seguir:

https://localhost:5001/movies/details?id=1

O parâmetro id é definido como um tipo que permite valor nulo (int?) nos casos em que o valor de id não seja fornecido.

Um expressão lambda é passada para o método FirstOrDefaultAsync para selecionar as entidades de filmes que correspondem ao valor dos dados da rota ou da cadeia de consulta.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Se for encontrado um filme, uma instância do modelo Movie será passada para a exibição Details:

return View(movie);

Examinar o conteúdo do arquivo Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class = "col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class = "col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

A instrução @model na parte superior do arquivo de exibição especifica o tipo de objeto que a exibição espera. Quando o controlador de filme foi criado, a seguinte instrução @model foi incluída:

@model MvcMovie.Models.Movie

Essa diretiva @model permite o acesso ao filme que o controlador passou para a exibição. O objeto Model é fortemente tipado. Por exemplo, na exibição Details.cshtml, o código passa cada campo de filme para os Auxiliares de HTML DisplayNameFor e DisplayFor com o objeto fortemente tipado Model. Os métodos Create e Edit e as exibições também passam um objeto de modelo Movie.

Examine a exibição Index.cshtml e o método Index no controlador Filmes. Observe como o código cria um objeto List quando ele chama o método View. O código passa esta lista Movies do método de ação Index para a exibição:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Quando o controlador de filmes foi criado, o scaffolding incluiu a seguinte instrução @model na parte superior do arquivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

A diretiva @model permite acessar a lista de filmes que o controlador passou para a exibição usando um objeto Model fortemente tipado. Por exemplo, na exibição Index.cshtml o código executa um loop pelos filmes com uma instrução foreach no objeto Model fortemente tipado:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Como o objeto Model é fortemente tipado como um objeto IEnumerable<Movie>, cada item no loop é tipado como Movie. Entre outros benefícios, o compilador valida os tipos usados no código.

Recursos adicionais

Neste tutorial, classes são adicionadas para o gerenciamento de filmes em um banco de dados. Essas classes serão a parte do “Modelo” parte do aplicativo MVC.

Essas classes do modelo são usadas com o Entity Framework Core (EF Core) para trabalhar com um banco de dados. O EF Core é uma estrutura ORM (mapeamento relacional de objetos) que simplifica o código de acesso a dados que você precisa escrever.

As classes de modelo criadas são conhecidas como classes POCO , de Plain Old CLR Objects (Bons e velhos objetos do CLR). As classes POCO não têm nenhuma dependência em EF Core. Elas definem as propriedades dos dados a serem armazenados no banco de dados.

Neste tutorial, você escreve as classes de modelo primeiro e o EF Core cria o banco de dados.

Adicionar uma classe de modelo de dados

Clique com o botão direito do mouse na pasta Modelos>Adicionar>Classe. Dê o nome Movie.cs para o arquivo.

Atualize o arquivo Models/Movie.cs com o seguinte código:

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

A classe Movie contém um campo Id, que é exigido pelo banco de dados para a chave primária.

O atributo DataType em ReleaseDate especifica o tipo de dados (Date). Com esse atributo:

  • O usuário não precisa inserir informações de horário no campo de data.
  • Somente a data é exibida, não as informações de tempo.

DataAnnotations são abordados em um tutorial posterior.

Adicionar pacotes do NuGet

No menu Ferramentas, selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes (PMC).

Menu do PMC

No PMC, execute o seguinte comando:

Install-Package Microsoft.EntityFrameworkCore.Design

Os comandos anteriores adicionam:

  • O provedor EF Core do SQL Server. O pacote do provedor instala o pacote do EF Core como uma dependência.
  • Os utilitários usados pelos pacotes instalados automaticamente na etapa de scaffolding, mais adiante no tutorial.

Compile o projeto como uma verificação de erros do compilador.

Aplicar scaffold a páginas de filme

Use a ferramenta scaffolding para produzir páginas CRUD (Create, Read, Update e Delete) para o modelo de filme.

Clique com o botão direito do mouse na pasta Controladores do Gerenciador de Soluções e selecione Adicionar > Novo Item Gerado por Scaffolding.

exibição da etapa acima

Na caixa de diálogo Adicionar Scaffold, selecione Controlador MVC com exibições, usando o Entity Framework > Adicionar.

Caixa de diálogo Adicionar Scaffold

Complete a caixa de diálogo Adicionar Controlador MVC com exibições, usando o Entity Framework:

  • Na lista suspensa Classe de modelo, selecione Filme (MvcMovie.Models).
  • Na linha Classe de contexto de dados, selecione o sinal + (adição).
    • Na caixa de diálogo Adicionar Contexto de Dados , o nome da classe MvcMovie.Data.MvcMovieContext é gerado.
    • Selecione Adicionar.
  • Exibições e Nome do controlador: mantenha o padrão.
  • Selecione Adicionar.

Adicionar contexto de dados mantém os padrões

O scaffolding atualiza o seguinte:

  • Insere as referências de pacote necessárias no arquivo de projeto MvcMovie.csproj.
  • Registra o contexto do banco de dados no Startup.ConfigureServices do arquivo Startup.cs.
  • Adiciona uma cadeia de caracteres de conexão do banco de dados ao arquivo appsettings.json.

O scaffolding cria o seguinte:

  • Um controlador de filmes: Controllers/MoviesController.cs
  • Razor exibir arquivos para páginas Criar, Excluir, Detalhes, Editar e Índice : Views/Movies/*.cshtml
  • Uma classe de contexto de banco de dados: Data/MvcMovieContext.cs

A criação automática desses arquivos e as atualizações de arquivos são conhecidas como scaffolding.

As páginas com scaffolding ainda não podem ser usadas porque o banco de dados não existe. Executar o aplicativo e selecionar o link Aplicativo de Filme resulta em uma mensagem de erro Não é possível abrir o banco de dados ou nenhuma tabela desse tipo: filme.

Migração inicial

Use o recurso EF CoreMigrações para criar o banco de dados. O recurso Migrações é um conjunto de ferramentas que cria e atualiza um banco de dados para corresponder ao modelo de dados.

No menu Ferramentas, selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes.

No PMC (Console do Gerenciador de Pacotes), Insira os seguintes comandos:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: gera um arquivo de migração Migrations/{timestamp}_InitialCreate.cs. O argumento InitialCreate é o nome da migração. Qualquer nome pode ser usado, mas, por convenção, um nome que descreve a migração é selecionado. Como essa é a primeira migração, a classe gerada contém o código para criar o esquema de banco de dados. O esquema de banco de dados é baseado no modelo especificado na classe MvcMovieContext.

  • Update-Database: atualiza o banco de dados para a migração mais recente, que o comando anterior criou. Esse comando executa o método Up no arquivo Migrations/{time-stamp}_InitialCreate.cs, que cria o banco de dados.

O comando Update-Database gera o seguinte aviso:

Nenhum tipo foi especificado para a coluna decimal 'Preço' no tipo de entidade 'Filme'. Isso fará com que valores sejam truncados silenciosamente se não couberem na precisão e na escala padrão. Especifique explicitamente o tipo de coluna do SQL Server que pode acomodar todos os valores usando 'HasColumnType()'.

Ignore o aviso anterior, ele é corrigido em um tutorial posterior.

Para obter mais informações sobre as ferramentas PMC para EF Core, veja a referência de ferramentas de EF Core – PMC no Visual Studio.

Testar o aplicativo

Execute o aplicativo e clique no link Aplicativo de Filme.

Se você receber uma exceção semelhante à seguinte, talvez tenha perdido a etapa de migrações:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Observação

Talvez você não consiga inserir casas decimais ou vírgulas no campo Price. Para dar suporte à validação do jQuery para localidades com idiomas diferentes do inglês que usam uma vírgula (",") para um ponto decimal e formatos de data diferentes do inglês dos EUA, o aplicativo precisa ser globalizado. Para obter instruções sobre a globalização, consulte esse problema no GitHub.

Examinar a classe de contexto e o registro do banco de dados gerados

Com o EF Core, o acesso aos dados é executado usando um modelo. Um modelo é feito de classes de entidade e um objeto de contexto que representa uma sessão com o banco de dados. O objeto de contexto permite consultar e salvar dados. O contexto de banco de dados é derivado de Microsoft. EntityFrameworkCore.DbContext e especifica as entidades a serem incluídas no modelo de dados.

O scaffolding cria a classe de contexto do banco de dados Data/MvcMovieContext.cs:

using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<Movie> Movie { get; set; }
    }
}

O código anterior cria uma propriedade DbSet<Movie> que representa os filmes no banco de dados.

O ASP.NET Core foi criado com a DI (injeção de dependência). Serviços, como o contexto do banco de dados, devem ser registrados com DI no Startup. Os componentes que necessitam desses serviços são fornecidos através dos parâmetros do construtor.

No arquivo Controllers/MoviesController.cs, o construtor usa a Injeção de Dependência para injetar o contexto do banco de dados MvcMovieContext no controlador. O contexto de banco de dados é usado em cada um dos métodos CRUD no controlador.

O scaffolding gerou o seguinte código realçado em Startup.ConfigureServices:

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

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

O sistema de configuração ASP.NET Core faz a leitura da cadeia de caracteres de conexão de banco de dados "MvcMovieContext".

Examinar a cadeia de caracteres de conexão de banco de dados gerada

O scaffolding adicionou uma cadeia de conexão ao arquivo appsettings.json:

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

No desenvolvimento local, o sistema de configuração do ASP.NET Core lê a chave ConnectionString do arquivo appsettings.json.

A classe InitialCreate

Examinar o arquivo de migração Migrations/{timestamp}_InitialCreate.cs:

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(type: "int", nullable: false)
                    .Annotation("SqlServer:Identity", "1, 1"),
                Title = table.Column<string>(type: "nvarchar(max)", nullable: true),
                ReleaseDate = table.Column<DateTime>(type: "datetime2", nullable: false),
                Genre = table.Column<string>(type: "nvarchar(max)", nullable: true),
                Price = table.Column<decimal>(type: "decimal(18,2)", nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

No código anterior:

  • InitialCreate.Up cria a tabela de Filmes e configura Id como a chave primária.
  • O método InitialCreate.Down reverte as alterações de esquema feitas pela migração de Up.

Injeção de dependência no controlador

Abra o arquivo Controllers/MoviesController.cs e examine o construtor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

O construtor usa a Injeção de Dependência para injetar o contexto de banco de dados (MvcMovieContext) no controlador. O contexto de banco de dados é usado em cada um dos métodos CRUD no controlador.

Teste a página Criar. Inserir e enviar dados.

Teste os links Editar, Detalhes e Excluir.

Modelos fortemente tipados e a diretiva @model

Anteriormente neste tutorial, você viu como um controlador pode passar dados ou objetos para uma exibição usando o dicionário ViewData. O dicionário ViewData é um objeto dinâmico que fornece uma maneira conveniente de associação tardia para passar informações para uma exibição.

O MVC também fornece a capacidade de passar objetos de modelo fortemente tipados para uma exibição. Essa abordagem fortemente tipada permite a verificação de código em tempo de compilação. O mecanismo de scaffolding passou um modelo fortemente tipado na classe e nas exibições de MoviesController.

Examine o método de Details gerado no arquivo Controllers/MoviesController.cs :

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

O parâmetro id geralmente é passado como dados de rota. Por exemplo, https://localhost:5001/movies/details/1 define:

  • O controlador para o controlador movies, o primeiro segmento da URL.
  • A ação para details, o segundo segmento da URL.
  • O idpara 1, o último segmento da URL.

O id pode ser passado com uma cadeia de caracteres de consulta, como no exemplo a seguir:

https://localhost:5001/movies/details?id=1

O parâmetro id é definido como um tipo que permite valor nulo (int?) nos casos em que o valor de id não seja fornecido.

Um expressão lambda é passada para o método FirstOrDefaultAsync para selecionar as entidades de filmes que correspondem ao valor dos dados da rota ou da cadeia de consulta.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Se for encontrado um filme, uma instância do modelo Movie será passada para a exibição Details:

return View(movie);

Examinar o conteúdo do arquivo Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

A instrução @model na parte superior do arquivo de exibição especifica o tipo de objeto que a exibição espera. Quando o controlador de filme foi criado, a seguinte instrução @model foi incluída:

@model MvcMovie.Models.Movie

Essa diretiva @model permite o acesso ao filme que o controlador passou para a exibição. O objeto Model é fortemente tipado. Por exemplo, na exibição Details.cshtml, o código passa cada campo de filme para os Auxiliares de HTML DisplayNameFor e DisplayFor com o objeto fortemente tipado Model. Os métodos Create e Edit e as exibições também passam um objeto de modelo Movie.

Examine a exibição Index.cshtml e o método Index no controlador Filmes. Observe como o código cria um objeto List quando ele chama o método View. O código passa esta lista Movies do método de ação Index para a exibição:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Quando o controlador de filmes foi criado, o scaffolding incluiu a seguinte instrução @model na parte superior do arquivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

A diretiva @model permite acessar a lista de filmes que o controlador passou para a exibição usando um objeto Model fortemente tipado. Por exemplo, na exibição Index.cshtml o código executa um loop pelos filmes com uma instrução foreach no objeto Model fortemente tipado:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Como o objeto Model é fortemente tipado como um objeto IEnumerable<Movie>, cada item no loop é tipado como Movie. Entre outros benefícios, o compilador valida os tipos usados no código.

Registro em log do SQL do Entity Framework Core

A configuração de log geralmente é fornecida pela seção Logging dos arquivos appsettings.{Environment}.json. Para registrar instruções SQL em log, adicione "Microsoft.EntityFrameworkCore.Database.Command": "Information" ao arquivo appsettings.Development.json:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=MyDB-2;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
     ,"Microsoft.EntityFrameworkCore.Database.Command": "Information"
    }
  },
  "AllowedHosts": "*"
}

Com o JSON anterior, as instruções SQL são exibidas na linha de comando e na janela de saída do Visual Studio.

Para obter mais informações, consulte Registrando em log no .NET Core e no ASP.NET Core e este tópico do GitHub.

Recursos adicionais

Neste tutorial, classes são adicionadas para o gerenciamento de filmes em um banco de dados. Essas classes serão a parte do “Modelo” parte do aplicativo MVC.

Essas classes do modelo são usadas com o Entity Framework Core (EF Core) para trabalhar com um banco de dados. O EF Core é uma estrutura ORM (mapeamento relacional de objetos) que simplifica o código de acesso a dados que você precisa escrever.

As classes de modelo criadas são conhecidas como classes POCO , de Plain Old CLR Objects (Bons e velhos objetos do CLR). As classes POCO não têm nenhuma dependência em EF Core. Elas definem as propriedades dos dados a serem armazenados no banco de dados.

Neste tutorial, você escreve as classes de modelo primeiro e o EF Core cria o banco de dados.

Adicionar uma classe de modelo de dados

Clique com o botão direito do mouse na pasta Modelos>Adicionar>Classe. Dê o nome Movie.cs para o arquivo.

Atualize o arquivo Movie.cs com o seguinte código:

using System;
using System.ComponentModel.DataAnnotations;

namespace MvcMovie.Models
{
    public class Movie
    {
        public int Id { get; set; }
        public string Title { get; set; }

        [DataType(DataType.Date)]
        public DateTime ReleaseDate { get; set; }
        public string Genre { get; set; }
        public decimal Price { get; set; }
    }
}

A classe Movie contém um campo Id, que é exigido pelo banco de dados para a chave primária.

O atributo DataType em ReleaseDate especifica o tipo de dados (Date). Com esse atributo:

  • O usuário não precisa inserir informações de tempo no campo de data.
  • Somente a data é exibida, não as informações de tempo.

DataAnnotations são abordados em um tutorial posterior.

Adicionar pacotes do NuGet

No menu Ferramentas, selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes (PMC).

Menu do PMC

No PMC, execute o seguinte comando:

Install-Package Microsoft.EntityFrameworkCore.SqlServer

O comando anterior adiciona o provedor SQL Server do EF Core. O pacote do provedor instala o pacote do EF Core como uma dependência. Pacotes adicionais são instalados automaticamente na etapa de scaffolding posteriormente no tutorial.

Criar uma classe de contexto de banco de dados

Uma classe de contexto de banco de dados é necessária para coordenar a funcionalidade do EF Core (Criar, Ler, Atualizar, Excluir) para o modelo Movie. O contexto de banco de dados é derivado de Microsoft.EntityFrameworkCore.DbContext e especifica as entidades a serem incluídas no modelo de dados.

Crie uma pasta de Dados.

Adicione um arquivo Data/MvcMovieContext.cs com o seguinte código:

using Microsoft.EntityFrameworkCore;
using MvcMovie.Models;

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

        public DbSet<Movie> Movie { get; set; }
    }
}

O código anterior cria uma propriedade DbSet<Movie> para o conjunto de entidades. Na terminologia do Entity Framework, um conjunto de entidades normalmente corresponde a uma tabela de banco de dados. Uma entidade corresponde a uma linha da tabela.

Registrar o contexto de banco de dados

O ASP.NET Core foi criado com a DI (injeção de dependência). Os serviços (como o contexto de banco de dados do EF Core) devem ser registrados com a DI durante a inicialização do aplicativo. Os componentes que exigem esses serviços (por exemplo, o Razor Pages) são fornecidos por meio dos parâmetros do construtor. O código de construtor que obtém uma instância de contexto do BD será mostrado mais adiante no tutorial. Nesta seção, você registra o contexto do banco de dados com o contêiner DI.

Adicione as seguintes instruções do using na parte superior do Startup.cs:

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Adicione o código realçado a seguir a Startup.ConfigureServices:

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

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

O nome da cadeia de conexão é passado para o contexto com a chamada de um método em um objeto DbContextOptions. Para o desenvolvimento local, o sistema de configuração do ASP.NET Core lê a cadeia de conexão do arquivo appsettings.json.

Examine a cadeia de caracteres de conexão de banco de dados

Adicione uma cadeia de caracteres de conexão ao arquivo appsettings.json:

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

Compile o projeto como uma verificação de erros do compilador.

Aplicar scaffold a páginas de filme

Use a ferramenta scaffolding para produzir páginas CRUD (criar, ler, atualizar e excluir) para o modelo de filme.

No Gerenciador de Soluções, clique com o botão direito do mouse na pasta Controladores>Adicionar > Novo Item com Scaffolding.

exibição da etapa acima

Na caixa de diálogo Adicionar Scaffold, selecione Controlador MVC com exibições, usando o Entity Framework > Adicionar.

Caixa de diálogo Adicionar Scaffold

Preencha a caixa de diálogo Adicionar Controlador:

  • Classe de modelo:Movie (MvcMovie.Models)
  • Classe de contexto de dados:MvcMovieContext (MvcMovie.Data)

Adicionar contexto de dados

  • Exibições: mantenha o padrão de cada opção marcado
  • Nome do controlador: mantenha o MoviesController padrão
  • Selecione Adicionar

O Visual Studio cria:

  • Um controlador de filmes (Controllers/MoviesController.cs)
  • Exibição de Razor para Criar, Excluir, Detalhes, Editar e Índice de páginas ("Views/Movies/`.cshtml)

A criação automática desses arquivos é conhecida como scaffolding.

Você não pode usar as páginas com scaffold ainda porque o banco de dados não existe. Se você executar o aplicativo e clicar no link Aplicativo de Filme, obterá uma mensagem de erro Não é possível abrir o banco de dados ou Não há uma tabela assim: Filme.

Migração inicial

Use o recurso EF CoreMigrações para criar o banco de dados. As migrações são um conjunto de ferramentas que permitem criar e atualizar um banco de dados para corresponder ao seu modelo de dados.

No menu Ferramentas, selecione Gerenciador de Pacotes NuGet>Console do Gerenciador de Pacotes (PMC).

No PMC, insira os seguintes comandos:

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate: gera um arquivo de migração Migrations/{timestamp}_InitialCreate.cs. O argumento InitialCreate é o nome da migração. Qualquer nome pode ser usado, mas, por convenção, um nome que descreve a migração é selecionado. Como essa é a primeira migração, a classe gerada contém o código para criar o esquema de banco de dados. O esquema de banco de dados é baseado no modelo especificado na classe MvcMovieContext.

  • Update-Database: atualiza o banco de dados para a migração mais recente, que o comando anterior criou. Esse comando executa o método Up no arquivo Migrations/{time-stamp}_InitialCreate.cs, que cria o banco de dados.

    O comando de atualização de banco de dados gera o seguinte aviso:

    Nenhum tipo foi especificado para a coluna decimal 'Preço' no tipo de entidade 'Filme'. Isso fará com que valores sejam truncados silenciosamente se não couberem na precisão e na escala padrão. Especifique explicitamente o tipo de coluna do SQL Server que pode acomodar todos os valores usando 'HasColumnType()'.

    Você pode ignorar esse aviso, ele será corrigido em um tutorial posterior.

Para obter mais informações sobre as ferramentas PMC para EF Core, veja a referência de ferramentas de EF Core – PMC no Visual Studio.

A classe InitialCreate

Examinar o arquivo de migração Migrations/{timestamp}_InitialCreate.cs:

public partial class InitialCreate : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.CreateTable(
            name: "Movie",
            columns: table => new
            {
                Id = table.Column<int>(nullable: false)
                    .Annotation("SqlServer:ValueGenerationStrategy", 
                                 SqlServerValueGenerationStrategy.IdentityColumn),
                Title = table.Column<string>(nullable: true),
                ReleaseDate = table.Column<DateTime>(nullable: false),
                Genre = table.Column<string>(nullable: true),
                Price = table.Column<decimal>(nullable: false)
            },
            constraints: table =>
            {
                table.PrimaryKey("PK_Movie", x => x.Id);
            });
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.DropTable(
            name: "Movie");
    }
}

O método Up cria a tabela de filmes e configura Id como a chave primária. O método Down reverte as alterações de esquema feitas pela migração Up.

Testar o aplicativo

  • Execute o aplicativo e clique no link Aplicativo de Filme.

    Se você receber uma exceção semelhante a uma das seguintes:

SqlException: Cannot open database "MvcMovieContext-1" requested by the login. The login failed.

Você provavelmente perdeu a etapa de migrações.

  • Teste a página Criar. Inserir e enviar dados.

    Observação

    Talvez você não consiga inserir casas decimais ou vírgulas no campo Price. Para dar suporte à validação do jQuery para localidades com idiomas diferentes do inglês que usam uma vírgula (",") para um ponto decimal e formatos de data diferentes do inglês dos EUA, o aplicativo precisa ser globalizado. Para obter instruções sobre a globalização, consulte esse problema no GitHub.

  • Teste os links Editar, Detalhes e Excluir.

Injeção de dependência no controlador

Abra o arquivo Controllers/MoviesController.cs e examine o construtor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

    public MoviesController(MvcMovieContext context)
    {
        _context = context;
    }

O construtor usa a Injeção de Dependência para injetar o contexto de banco de dados (MvcMovieContext) no controlador. O contexto de banco de dados é usado em cada um dos métodos CRUD no controlador.

Modelos fortemente tipados e a palavra-chave @model

Anteriormente neste tutorial, você viu como um controlador pode passar dados ou objetos para uma exibição usando o dicionário ViewData. O dicionário ViewData é um objeto dinâmico que fornece uma maneira conveniente de associação tardia para passar informações para uma exibição.

O MVC também fornece a capacidade de passar objetos de modelo fortemente tipados para uma exibição. Essa abordagem fortemente tipada permite a verificação de código em tempo de compilação. O mecanismo de scaffolding usou essa abordagem (ou seja, passando um modelo fortemente tipado) com a classe MoviesController e as exibições.

Examine o método de Details gerado no arquivo Controllers/MoviesController.cs :

// GET: Movies/Details/5
public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var movie = await _context.Movie
        .FirstOrDefaultAsync(m => m.Id == id);
    if (movie == null)
    {
        return NotFound();
    }

    return View(movie);
}

O parâmetro id geralmente é passado como dados de rota. Por exemplo, https://localhost:5001/movies/details/1 define:

  • O controlador para o controlador movies (o primeiro segmento de URL).
  • A ação para details (o segundo segmento de URL).
  • A ID como 1 (o último segmento de URL).

Você também pode passar a id com uma cadeia de consulta da seguinte maneira:

https://localhost:5001/movies/details?id=1

O parâmetro id é definido como um tipo que permite valor nulo (int?), caso um valor de ID não seja fornecido.

Um expressão lambda é passada para FirstOrDefaultAsync para selecionar as entidades de filmes que correspondem ao valor da cadeia de consulta ou de dados da rota.

var movie = await _context.Movie
    .FirstOrDefaultAsync(m => m.Id == id);

Se for encontrado um filme, uma instância do modelo Movie será passada para a exibição Details:

return View(movie);

Examinar o conteúdo do arquivo Views/Movies/Details.cshtml:

@model MvcMovie.Models.Movie

@{
    ViewData["Title"] = "Details";
}

<h1>Details</h1>

<div>
    <h4>Movie</h4>
    <hr />
    <dl class="row">
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Title)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Title)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.ReleaseDate)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.ReleaseDate)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Genre)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Genre)
        </dd>
        <dt class="col-sm-2">
            @Html.DisplayNameFor(model => model.Price)
        </dt>
        <dd class="col-sm-10">
            @Html.DisplayFor(model => model.Price)
        </dd>
    </dl>
</div>
<div>
    <a asp-action="Edit" asp-route-id="@Model.Id">Edit</a> |
    <a asp-action="Index">Back to List</a>
</div>

A instrução @model na parte superior do arquivo de exibição especifica o tipo de objeto que a exibição espera. Quando o controlador de filme foi criado, a seguinte instrução @model foi incluída:

@model MvcMovie.Models.Movie

Essa diretiva @model permite o acesso ao filme que o controlador passou para a exibição. O objeto Model é fortemente tipado. Por exemplo, na exibição Details.cshtml, o código passa cada campo de filme para os Auxiliares de HTML DisplayNameFor e DisplayFor com o objeto fortemente tipado Model. Os métodos Create e Edit e as exibições também passam um objeto de modelo Movie.

Examine a exibição Index.cshtml e o método Index no controlador Filmes. Observe como o código cria um objeto List quando ele chama o método View. O código passa esta lista Movies do método de ação Index para a exibição:

// GET: Movies
public async Task<IActionResult> Index()
{
    return View(await _context.Movie.ToListAsync());
}

Quando o controlador de filmes foi criado, o scaffolding incluiu a seguinte instrução @model na parte superior do arquivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

A diretiva @model permite acessar a lista de filmes que o controlador passou para a exibição usando um objeto Model fortemente tipado. Por exemplo, na exibição Index.cshtml o código executa um loop pelos filmes com uma instrução foreach no objeto Model fortemente tipado:

@model IEnumerable<MvcMovie.Models.Movie>

@{
    ViewData["Title"] = "Index";
}

<h1>Index</h1>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.ReleaseDate)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Genre)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Price)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
@foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.Title)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.ReleaseDate)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Genre)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.Price)
            </td>
            <td>
                <a asp-action="Edit" asp-route-id="@item.Id">Edit</a> |
                <a asp-action="Details" asp-route-id="@item.Id">Details</a> |
                <a asp-action="Delete" asp-route-id="@item.Id">Delete</a>
            </td>
        </tr>
}
    </tbody>
</table>

Como o objeto Model é fortemente tipado (como um objeto IEnumerable<Movie>), cada item no loop é tipado como Movie. Entre outros benefícios, isso significa que você obtém a verificação do tempo de compilação do código.

Recursos adicionais