Parte 4, adicionar um modelo a um ASP.NET Core MVC

Por Rick Anderson e Jon P Smith.

Nesta seção, classes são adicionadas para o gerenciamento de filmes em um banco de dados. Essas classes são a parte "M odel" do aplicativo M VC.

Essas classes de modelo são usadas com Entity Framework Core (EF Core) para trabalhar com um banco de dados. O EF Core é uma estrutura ORM (de 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 *POCO _ classes, de _*P**lain O ld C LR O laects. As classes POCO não têm nenhuma dependência de EF Core. Eles definem apenas as propriedades dos dados a serem armazenados no banco de dados.

Neste tutorial, as classes de modelo são criadas primeiro e EF Core cria o banco de dados.

Adicionar uma classe de modelo de dados

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

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 DataType atributo em especifica o tipo dos dados ( ReleaseDate Date ). Com esse atributo:

  • O usuário não precisa inserir informações de hora 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 NuGet

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

Menu do PMC

No PMC, execute o seguinte comando:

Install-Package Microsoft.EntityFrameworkCore.Design

Os comandos anteriores adicionam:

  • O provedor EF Core SQL Server dados. 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, posteriormente no tutorial.

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

Aplicar scaffold a páginas de filme

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

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

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

Conclua a caixa de diálogo Adicionar Controlador MVC com exibições Entity Framework:

  • Na lista de seleção Classe de modelo, selecione Movie (MvcMovie.Models).
  • Na linha Classe de contexto de dados, selecione o sinal de + (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 padrões de manter o contexto de dados

O scaffolding atualiza o seguinte:

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

O scaffolding cria o seguinte:

  • Um controlador de filmes: Controllers/MoviesController.cs
  • Razorexibir arquivos para as 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 Scaffold ainda não podem ser usadas porque o banco de dados não existe. Executar o aplicativo e selecionar o link do aplicativo de filme resulta em uma mensagem de erro não é possível abrir o banco de dados ou nenhuma tabela: filme .

Migração inicial

Use o recurso Migrações do EF Core para criar o banco de dados. As migrações são um conjunto de ferramentas que criam e atualizam um banco de dados para corresponder ao modelo.

no menu ferramentas , selecione NuGet Gerenciador de Pacotes > Console de 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 de migrações/{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 Update-Database comando 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()'.

Ignorar o aviso anterior, 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.

Testar o aplicativo

Execute o aplicativo e selecione o link do 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 do banco de dados gerada e o registro

Com o EF Core, o acesso a dados é executado usando um modelo. Um modelo é composto de classes de entidade e um objeto de contexto que representa uma sessão com o banco de dados. O objeto Context 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.

Scaffolding cria a classe de contexto de 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 <Movie> DbSet 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 exigem esses serviços são fornecidos por meio de parâmetros de construtor.

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

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 de ASP.NET Core lê a cadeia de conexão do banco de dados "MvcMovieContext".

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

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

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

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

A classe InitialCreate

Examine 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.
  • InitialCreate.Down reverte as alterações de esquema feitas pela Up migração.

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 @model diretiva

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 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 scaffolding passou um modelo fortemente tipado na MoviesController classe e nos modos de exibição.

Examine o método 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 movies controlador, o primeiro segmento de URL.
  • A ação para details , o segundo segmento de URL.
  • O id para 1, o último segmento de 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 id parâmetro é definido como um tipo anulável ( int? ) em casos em que o id valor não é fornecido.

Uma expressão lambda é passada para o FirstOrDefaultAsync método para selecionar entidades de filme que correspondam aos dados de rota ou ao valor da cadeia de caracteres 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);

Examine 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 Model fortemente tipado. 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 Movies. 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 @model diretiva permite o acesso à lista de filmes que o controlador passou para a exibição usando um Model objeto 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 Model objeto é fortemente digitado como IEnumerable<Movie> um objeto, cada item no loop é digitado como Movie . Entre outros benefícios, o compilador valida os tipos usados no código.

SQL Registro em log Entity Framework Core

A configuração de registro em log normalmente é fornecida Logging pela seção de appsettings. {Environment} Arquivos .json. Para registrar SQL, adicione "Microsoft.EntityFrameworkCore.Database.Command": "Information" ao arquivoappsettings.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, SQL instruções são exibidas na linha de comando e na Visual Studio de saída.

Para obter mais informações, Como fazer registro em log no .NET Core e no ASP.NET Core consulte e este GitHub problema.

Recursos adicionais

Adicionar uma classe de modelo de dados

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

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 DataType atributo em especifica o tipo dos dados ( ReleaseDate Date ). Com esse atributo:

  • O usuário não precisa inserir informações de hora 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 NuGet

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

Menu do PMC

No PMC, execute o seguinte comando:

Install-Package Microsoft.EntityFrameworkCore.Design

Os comandos anteriores adicionam:

  • O provedor EF Core SQL Server dados. 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, posteriormente no tutorial.

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

Aplicar scaffold a páginas de filme

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

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

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

Conclua a caixa de diálogo Adicionar Controlador MVC com exibições Entity Framework:

  • Na lista de seleção Classe de modelo, selecione Movie (MvcMovie.Models).
  • Na linha Classe de contexto de dados, selecione o sinal de + (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 padrões de manter o contexto de dados

O scaffolding atualiza o seguinte:

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

O scaffolding cria o seguinte:

  • Um controlador de filmes: Controllers/MoviesController.cs
  • Razorexibir arquivos para as 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 arquivo é conhecida como scaffolding.

As páginas com scaffolding ainda não podem ser usadas porque o banco de dados não existe. A execução do aplicativo e a seleção do link Aplicativo de Filme resulta em uma mensagem de erro Não é possível abrir o banco de dados ou nenhuma tabela: Mensagem de erro de filme.

Migração inicial

Use o recurso Migrações do EF Core para criar o banco de dados. As migrações são um conjunto de ferramentas que criam e atualizam um banco de dados para corresponder ao modelo de dados.

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

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 Update-Database comando 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 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.

Testar o aplicativo

Execute o aplicativo e selecione o 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 de banco de dados gerada e o registro

Com o EF Core, o acesso a 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 de 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 exigem esses serviços são fornecidos por meio de parâmetros de construtor.

No arquivo Controllers/MoviesController.cs, o construtor usa a Injeção de Dependência para injetar o contexto do banco MvcMovieContext de dados 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 ASP.NET Core configuração do sistema lê a cadeia de conexão de banco de dados "MvcMovieContext".

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

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

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

Para o desenvolvimento local, ASP.NET Core sistema de configuraçãoConnectionString a chave do appsettings.json arquivo.

A classe InitialCreate

Examine 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 Movie e configura Id como a chave primária.
  • InitialCreate.Down reverte as alterações de esquema feitas pela Up migração.

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 digitados e a @model diretiva

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 fornece a capacidade de passar objetos de modelo fortemente digitados 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 digitado na MoviesController classe e nas exibições.

Examine o método 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 movies controlador, o primeiro segmento de URL.
  • A ação para details , o segundo segmento de URL.
  • O id para 1, o último segmento de 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 é definido como um tipo que pode ser anulado id ( ) nos casos em que o valor não é int? id fornecido.

Uma expressão lambda é passada para o método para selecionar entidades de filme que corresponderem aos dados de rota ou FirstOrDefaultAsync ao valor da cadeia de caracteres 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);

Examine 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 Model fortemente tipado. 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 Movies. 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 permite o acesso à lista de filmes que o controlador passou para a exibição usando um objeto @model Model fortemente digitado. 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 Model objeto é fortemente digitado como IEnumerable<Movie> um objeto, cada item no loop é digitado como Movie . Entre outros benefícios, o compilador valida os tipos usados no código.

SQL Registro em log Entity Framework Core

A configuração de registro em log normalmente é fornecida Logging pela seção de appsettings. {Environment} Arquivos .json. Para registrar SQL, adicione "Microsoft.EntityFrameworkCore.Database.Command": "Information" ao arquivoappsettings.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, SQL instruções são exibidas na linha de comando e na Visual Studio de saída.

Para obter mais informações, Como fazer registro em log no .NET Core e no ASP.NET Core consulte e este GitHub problema.

Recursos adicionais

Adicionar uma classe de modelo de dados

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

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 DataType atributo 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 NuGet

no menu ferramentas , selecione NuGet Gerenciador de Pacotes > Console de 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 <Movie> DbSet 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 do banco de dados

O ASP.NET Core foi criado com a DI (injeção de dependência). Os serviços (como o contexto de BD do EF Core) devem ser registrados com a DI durante a inicialização do aplicativo. Os componentes que exigem esses serviços (como Razor páginas) são fornecidos por meio de parâmetros de 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 os demonstrativos do using a seguir à 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 ASP.NET Core lê a cadeia de conexão do appsettings.json arquivo.

Examinar a cadeia de conexão do banco de dados

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

{
  "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 Scaffold**.

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: filme (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)
  • Razor exibir arquivos para criar, excluir, detalhes, editar e indexar páginas (exibições/filmes/ * . 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 do aplicativo de filme , receberá uma mensagem de erro não é possível abrir o banco de dados ou nenhuma tabela: filme .

Migração inicial

Use o recurso Migrações do EF Core 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 NuGet Gerenciador de Pacotes > Console de Gerenciador de Pacotes (PMC).

No PMC, insira os seguintes comandos:

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate: Gera um arquivo de migração de migrações/{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

Examine 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 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 id parâmetro é definido como um tipo anulável ( int? ) no caso de um valor de ID não ser 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);

Examine 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 Model fortemente tipado. 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 Movies. 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