Parte 4. Adición de un modelo a una aplicación de ASP.NET Core MVC

Nota:

Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión .NET 8 de este artículo.

Importante

Esta información hace referencia a un producto en versión preliminar, el cual puede sufrir importantes modificaciones antes de que se publique la versión comercial. Microsoft no proporciona ninguna garantía, expresa o implícita, con respecto a la información proporcionada aquí.

Para la versión actual, consulte la versión .NET 8 de este artículo.

Por Rick Anderson y Jon P Smith.

En este tutorial, se agregan clases para administrar películas en una base de datos. Estas clases son el elemento "Model" de la aplicación MVC.

Estas clases de modelo se usan con Entity Framework Core (EF Core) para trabajar con una base de datos. EF Core es un marco de trabajo de asignación relacional de objetos (ORM) que simplifica el código de acceso de datos que se debe escribir.

Las clases de modelo creadas se conocen como clases POCO de Plain Old CLR Objects. Las clases POCO no tienen ninguna dependencia de EF Core. Solo definen las propiedades de los datos que se almacenan en la base de datos.

En este tutorial, primero se crean las clases de modelo, y EF Core crea la base de datos.

Agregar una clase de modelo de datos

Haga clic con el botón derecho en la carpeta Models>Agregar>Clase. Ponga al archivo el nombre Movie.cs.

Actualice el archivo Models/Movie.cs con el código siguiente:

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; }
}

La clase Movie contiene un campo Id, que la base de datos requiere para la clave principal.

El atributo DataType en ReleaseDate especifica el tipo de los datos (Date). Con este atributo:

  • El usuario no tiene que especificar información horaria en el campo de fecha.
  • Solo se muestra la fecha, no información horaria.

Los elementos DataAnnotations se tratan en un tutorial posterior.

El signo de interrogación después de string indica que la propiedad admite un valor NULL. Para más información, consulte Tipos de referencia que admiten un valor NULL.

Adición de paquetes NuGet

Visual Studio instala automáticamente los paquetes necesarios.

Compile el proyecto para comprobar si hay errores del compilador.

Scaffolding de las páginas de películas

Use la herramienta de scaffolding para generar las páginas Create, Read, Update y Delete (CRUD) para el modelo de película.

En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Controladores y seleccione Agregar > Nuevo elemento con scaffolding.

vista del paso anterior

En el cuadro de diálogo Agregar nuevo elemento con scaffolding:

  • En el panel de la izquierda, seleccione Instalado>Común>MVC.
  • Seleccione Controlador MVC con vistas que usan Entity Framework.
  • Seleccione Agregar.

Cuadro de diálogo Agregar scaffold

Complete el cuadro de diálogo Add MVC Controller with views, using Entity Framework (Agregar un controlador de MVC con vistas que usan Entity Framework):

  • En la lista desplegable Clase de modelo, seleccione Movie (MvcMovie.Models) .
  • En la fila Clase de contexto de datos, seleccione el signo + (más).
    • En el cuadro de diálogo Agregar contexto de datos, se genera el nombre de clase MvcMovie.Data.MvcMovieContext.
    • Seleccione Agregar.
  • En la lista desplegable Proveedor de base de datos, seleccione SQL Server.
  • Vistas y Nombre del controlador: mantenga el valor predeterminado.
  • Seleccione Agregar.

Agregar contexto de datos con los valores predeterminados

Si recibe un mensaje de error, seleccione Agregar una segunda vez para intentarlo de nuevo.

Scaffolding agrega los siguientes paquetes:

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

Scaffolding crea lo siguiente:

  • Un controlador de películas: Controllers/MoviesController.cs
  • Archivos de la vista Razor para las páginas Crear, Eliminar, Detalles, Editar e Índice: Views/Movies/*.cshtml
  • Una clase de contexto de datos: Data/MvcMovieContext.cs

Scaffolding actualiza lo siguiente:

  • Inserta las referencias de paquete necesarias en el archivo del proyecto MvcMovie.csproj.
  • Registra el contexto de la base de datos en el archivo Program.cs.
  • Agrega una cadena de conexión de la base de datos al archivo appsettings.json .

La creación automática de estos archivos y actualizaciones de archivos se conoce como scaffolding.

Todavía no se pueden usar las páginas con scaffolding porque la base de datos no existe. La ejecución de la aplicación y la selección del vínculo Movie App (Aplicación de película) genera un mensaje de error No se puede abrir la base de datos o no se encuentra dicha tabla: Movie.

Compile el proyecto para comprobar que no haya ningún error.

Migración inicial

Use la característica EF CoreMigraciones de para crear la base de datos. Las migraciones son un conjunto de herramientas que crean y actualizan una base de datos para que coincida con el modelo de datos.

En el menú Herramientas, seleccione Administrador de paquetes NuGet>Consola del Administrador de paquetes.

En la Consola del Administrador de paquetes (PMC), escriba los comandos siguientes:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Genera un archivo de migración Migrations/{timestamp}_InitialCreate.cs. El argumento InitialCreate es el nombre de la migración. Se puede usar cualquier nombre, pero, por convención, se selecciona uno que describa la migración. Como se trata de la primera migración, la clase generada contiene código para crear el esquema de la base de datos. El esquema de la base de datos se basa en el modelo especificado en la clase MvcMovieContext.

  • Update-Database: actualiza la base de datos a la migración más reciente, que ha creado el comando anterior. El comando ejecuta el método Up en el archivo Migrations/{time-stamp}_InitialCreate.cs, que crea la base de datos.

El comando Update-Database genera la siguiente advertencia:

No se ha especificado ningún tipo en la propiedad decimal "Price". This will cause values to be silently truncated if they do not fit in the default precision and scale. Especifique explícitamente el tipo de columna de SQL Server que puede alojar todos los valores de "OnModelCreating" mediante "HasColumnType", especifique la precisión y la escala mediante "HasPrecision" o configure un convertidor de valores mediante "HasConversion".

Ignore la advertencia anterior, ya que se ha corregido en un tutorial posterior.

Para obtener más información sobre las herramientas de PMC para EF Core, vea Referencia de herramientas de EF Core: consola del Administrador de paquetes en Visual Studio.

Prueba de la aplicación

Ejecute la aplicación y seleccione el vínculo Movie App (Aplicación de película).

Si recibe una excepción similar a la siguiente, es posible que haya perdido el comando Update-Database en el paso de migraciones:

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

Nota

Es posible que no pueda escribir comas decimales en el campo Price. La aplicación debe globalizarse para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos. Para obtener instrucciones sobre la globalización, consulte esta cuestión en GitHub.

Examen del registro y la clase del contexto de la base de datos generados

Con EF Core, el acceso a datos se realiza mediante un modelo. Un modelo se compone de clases de entidad y un objeto de contexto que representa una sesión con la base de datos. Este objeto de contexto permite consultar y guardar datos. El contexto de base de datos se deriva de Microsoft.EntityFrameworkCore.DbContext y especifica las entidades que se van a incluir en el modelo de datos.

Scaffolding crea la clase de contexto de la base de datos 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; }
    }
}

El código anterior crea una propiedad DbSet<Movie> que representa las películas de la base de datos.

Inserción de dependencias

ASP.NET Core integra la inserción de dependencias (DI). Los servicios, como el contexto de la base de datos, se registran con DI en Program.cs. Estos servicios se proporcionan a los componentes que los necesitan a través de parámetros de constructor.

En el archivo Controllers/MoviesController.cs, el constructor usa la inserción de dependencias para insertar el contexto de base de datos MvcMovieContext en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.

El scaffolding generó el siguiente código resaltado en Program.cs:

var builder = WebApplication.CreateBuilder(args);

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

El sistema de configuración de ASP.NET Core lee la cadena de conexión de la base de datos "MvcMovieContext".

Examen de la cadena de conexión de base de datos generada

Scaffolding agregó una cadena de conexión al archivo appsettings.json :

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

Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la clave ConnectionString del archivo appsettings.json .

La clase InitialCreate.

Examine el archivo de migración 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");
        }
    }
}

En el código anterior:

  • InitialCreate.Up crea la tabla Movie y configura Id como la clave principal.
  • InitialCreate.Down revierte los cambios de esquema realizados por la migración Up.

Inserción de dependencias en el controlador

Abra el archivo Controllers/MoviesController.cs y examine el constructor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

El constructor usa la inserción de dependencias para insertar el contexto de base de datos (MvcMovieContext) en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.

Pruebe la página Create. Escriba y envíe los datos.

Pruebe las páginas Edit, Details y Delete.

Modelos fuertemente tipados y la directiva @model

Anteriormente en este tutorial, vimos cómo un controlador puede pasar datos u objetos a una vista mediante el diccionario ViewData. El diccionario ViewData es un objeto dinámico que proporciona una cómoda manera enlazada en tiempo de ejecución de pasar información a una vista.

MVC ofrece la capacidad de pasar objetos de modelo fuertemente tipados a una vista. Este enfoque fuertemente tipado permite comprobar el código en tiempo de compilación. El mecanismo de scaffolding pasó un modelo fuertemente tipado en las vistas y la clase MoviesController.

Examine el método Details en el archivo 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);
}

El parámetro id suele pasarse como datos de ruta. Por ejemplo, https://localhost:5001/movies/details/1 establece:

  • El controlador en el controlador movies, el primer segmento de dirección URL.
  • La acción en details, el segundo segmento de dirección URL.
  • id en 1,el último segmento de dirección URL.

id se puede pasar con una cadena de consulta, como en el ejemplo siguiente:

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

El parámetro id se define como un tipo que acepta valores NULL (int?) en caso de que no se proporcione un valor id.

Se pasa una expresión lambda al método FirstOrDefaultAsync para seleccionar entidades de película que coincidan con los datos de enrutamiento o el valor de consulta de cadena.

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

Si se encuentra una película, se pasa una instancia del modelo Movie a la vista Details:

return View(movie);

Examine el contenido del archivo 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>

La instrucción @model de la parte superior del archivo de vista especifica el tipo de objeto que espera la vista. Cuando se ha creado el controlador de película, se ha incluido la siguiente instrucción @model:

@model MvcMovie.Models.Movie

Esta directiva @model permite el acceso a la película que el controlador ha pasado a la vista. El objeto Model está fuertemente tipado. Por ejemplo, en la vista Details.cshtml, el código pasa cada campo de película a los asistentes de HTML DisplayNameFor y DisplayFor con el objeto Model fuertemente tipado. Los métodos Create y Edit y las vistas también pasan un objeto de modelo Movie.

Examine la vista Index.cshtml y el método Index en el controlador Movies. Observe cómo el código crea un objeto List cuando llama al método View. El código pasa esta lista Movies desde el método de acción Index a la vista:

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

El código devuelve detalles del problema si la propiedad Movie del contexto de datos es null.

Cuando se ha creado el controlador de películas, el scaffolding ha incluido la siguiente instrucción @model en la parte superior del archivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

La directiva @model permite acceder a la lista de películas que el controlador pasó a la vista usando un objeto Model fuertemente tipado. Por ejemplo, en la vista Index.cshtml, el código recorre en bucle las películas con una instrucción foreach sobre el objeto Model fuertemente 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 el objeto Model es fuertemente tipado como un objeto IEnumerable<Movie>, cada elemento del bucle está tipado como Movie. Entre otras ventajas, el compilador valida los tipos usados en el código.

Recursos adicionales

En este tutorial, se agregan clases para administrar películas en una base de datos. Estas clases son el elemento "Model" de la aplicación MVC.

Estas clases de modelo se usan con Entity Framework Core (EF Core) para trabajar con una base de datos. EF Core es un marco de trabajo de asignación relacional de objetos (ORM) que simplifica el código de acceso de datos que se debe escribir.

Las clases de modelo creadas se conocen como clases POCO de Plain Old CLR Objects. Las clases POCO no tienen ninguna dependencia de EF Core. Solo definen las propiedades de los datos que se almacenan en la base de datos.

En este tutorial, primero se crean las clases de modelo, y EF Core crea la base de datos.

Agregar una clase de modelo de datos

Haga clic con el botón derecho en la carpeta Models>Agregar>Clase. Ponga al archivo el nombre Movie.cs.

Actualice el archivo Models/Movie.cs con el código siguiente:

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; }
}

La clase Movie contiene un campo Id, que la base de datos requiere para la clave principal.

El atributo DataType en ReleaseDate especifica el tipo de los datos (Date). Con este atributo:

  • El usuario no tiene que especificar información horaria en el campo de fecha.
  • Solo se muestra la fecha, no información horaria.

Los elementos DataAnnotations se tratan en un tutorial posterior.

El signo de interrogación después de string indica que la propiedad admite un valor NULL. Para más información, consulte Tipos de referencia que admiten un valor NULL.

Adición de paquetes NuGet

Visual Studio instala automáticamente los paquetes necesarios.

Compile el proyecto para comprobar si hay errores del compilador.

Scaffolding de las páginas de películas

Use la herramienta de scaffolding para generar las páginas Create, Read, Update y Delete (CRUD) para el modelo de película.

En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Controladores y seleccione Agregar > Nuevo elemento con scaffolding.

vista del paso anterior

En el cuadro de diálogo Agregar nuevo elemento con scaffolding:

  • En el panel de la izquierda, seleccione Instalado>Común>MVC.
  • Seleccione Controlador MVC con vistas que usan Entity Framework.
  • Seleccione Agregar.

Cuadro de diálogo Agregar scaffold

Complete el cuadro de diálogo Add MVC Controller with views, using Entity Framework (Agregar un controlador de MVC con vistas que usan Entity Framework):

  • En la lista desplegable Clase de modelo, seleccione Movie (MvcMovie.Models) .
  • En la fila Clase de contexto de datos, seleccione el signo + (más).
    • En el cuadro de diálogo Agregar contexto de datos, se genera el nombre de clase MvcMovie.Data.MvcMovieContext.
    • Seleccione Agregar.
  • En la lista desplegable Proveedor de base de datos, seleccione SQL Server.
  • Vistas y Nombre del controlador: mantenga el valor predeterminado.
  • Seleccione Agregar.

Agregar contexto de datos con los valores predeterminados Si aparece un mensaje de error, seleccione Agregar por segunda vez e inténtelo de nuevo.

Scaffolding agrega los siguientes paquetes:

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

Scaffolding crea lo siguiente:

  • Un controlador de películas: Controllers/MoviesController.cs
  • Archivos de la vista Razor para las páginas Crear, Eliminar, Detalles, Editar e Índice: Views/Movies/*.cshtml
  • Una clase de contexto de datos: Data/MvcMovieContext.cs

Scaffolding actualiza lo siguiente:

  • Inserta las referencias de paquete necesarias en el archivo del proyecto MvcMovie.csproj.
  • Registra el contexto de la base de datos en el archivo Program.cs.
  • Agrega una cadena de conexión de la base de datos al archivo appsettings.json .

La creación automática de estos archivos y actualizaciones de archivos se conoce como scaffolding.

Todavía no se pueden usar las páginas con scaffolding porque la base de datos no existe. La ejecución de la aplicación y la selección del vínculo Movie App (Aplicación de película) genera un mensaje de error No se puede abrir la base de datos o no se encuentra dicha tabla: Movie.

Compile el proyecto para comprobar que no haya ningún error.

Migración inicial

Use la característica EF CoreMigraciones de para crear la base de datos. Las migraciones son un conjunto de herramientas que crean y actualizan una base de datos para que coincida con el modelo de datos.

En el menú Herramientas, seleccione Administrador de paquetes NuGet>Consola del Administrador de paquetes.

En la Consola del Administrador de paquetes (PMC), escriba los comandos siguientes:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Genera un archivo de migración Migrations/{timestamp}_InitialCreate.cs. El argumento InitialCreate es el nombre de la migración. Se puede usar cualquier nombre, pero, por convención, se selecciona uno que describa la migración. Como se trata de la primera migración, la clase generada contiene código para crear el esquema de la base de datos. El esquema de la base de datos se basa en el modelo especificado en la clase MvcMovieContext.

  • Update-Database: actualiza la base de datos a la migración más reciente, que ha creado el comando anterior. El comando ejecuta el método Up en el archivo Migrations/{time-stamp}_InitialCreate.cs, que crea la base de datos.

El comando Update-Database genera la siguiente advertencia:

No type was specified for the decimal column "Price" on entity type "Movie" (No se ha especificado ningún tipo en la columna decimal "Price" en el tipo de entidad "Movie"). This will cause values to be silently truncated if they do not fit in the default precision and scale. Explicitly specify the SQL server column type that can accommodate all the values using "HasColumnType()" (Especifique de forma explícita el tipo de columna de SQL Server que pueda acomodar todos los valores mediante "HasColumnType()").

Ignore la advertencia anterior, ya que se ha corregido en un tutorial posterior.

Para obtener más información sobre las herramientas de PMC para EF Core, vea Referencia de herramientas de EF Core: consola del Administrador de paquetes en Visual Studio.

Prueba de la aplicación

Ejecute la aplicación y seleccione el vínculo Movie App (Aplicación de película).

Si recibe una excepción similar a la siguiente, es posible que haya perdido el comando Update-Database en el paso de migraciones:

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

Nota

Es posible que no pueda escribir comas decimales en el campo Price. La aplicación debe globalizarse para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos. Para obtener instrucciones sobre la globalización, consulte esta cuestión en GitHub.

Examen del registro y la clase del contexto de la base de datos generados

Con EF Core, el acceso a datos se realiza mediante un modelo. Un modelo se compone de clases de entidad y un objeto de contexto que representa una sesión con la base de datos. Este objeto de contexto permite consultar y guardar datos. El contexto de base de datos se deriva de Microsoft.EntityFrameworkCore.DbContext y especifica las entidades que se van a incluir en el modelo de datos.

Scaffolding crea la clase de contexto de la base de datos 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; }
    }
}

El código anterior crea una propiedad DbSet<Movie> que representa las películas de la base de datos.

Inserción de dependencias

ASP.NET Core integra la inserción de dependencias (DI). Los servicios, como el contexto de la base de datos, se registran con DI en Program.cs. Estos servicios se proporcionan a los componentes que los necesitan a través de parámetros de constructor.

En el archivo Controllers/MoviesController.cs, el constructor usa la inserción de dependencias para insertar el contexto de base de datos MvcMovieContext en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.

El scaffolding generó el siguiente código resaltado en Program.cs:

var builder = WebApplication.CreateBuilder(args);

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

El sistema de configuración de ASP.NET Core lee la cadena de conexión de la base de datos "MvcMovieContext".

Examen de la cadena de conexión de base de datos generada

Scaffolding agregó una cadena de conexión al archivo appsettings.json :

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

Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la clave ConnectionString del archivo appsettings.json .

La clase InitialCreate.

Examine el archivo de migración 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");
        }
    }
}

En el código anterior:

  • InitialCreate.Up crea la tabla Movie y configura Id como la clave principal.
  • InitialCreate.Down revierte los cambios de esquema realizados por la migración Up.

Inserción de dependencias en el controlador

Abra el archivo Controllers/MoviesController.cs y examine el constructor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

El constructor usa la inserción de dependencias para insertar el contexto de base de datos (MvcMovieContext) en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.

Pruebe la página Create. Escriba y envíe los datos.

Pruebe las páginas Edit, Details y Delete.

Modelos fuertemente tipados y la directiva @model

Anteriormente en este tutorial, vimos cómo un controlador puede pasar datos u objetos a una vista mediante el diccionario ViewData. El diccionario ViewData es un objeto dinámico que proporciona una cómoda manera enlazada en tiempo de ejecución de pasar información a una vista.

MVC ofrece la capacidad de pasar objetos de modelo fuertemente tipados a una vista. Este enfoque fuertemente tipado permite comprobar el código en tiempo de compilación. El mecanismo de scaffolding pasó un modelo fuertemente tipado en las vistas y la clase MoviesController.

Examine el método Details en el archivo 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);
}

El parámetro id suele pasarse como datos de ruta. Por ejemplo, https://localhost:5001/movies/details/1 establece:

  • El controlador en el controlador movies, el primer segmento de dirección URL.
  • La acción en details, el segundo segmento de dirección URL.
  • id en 1,el último segmento de dirección URL.

id se puede pasar con una cadena de consulta, como en el ejemplo siguiente:

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

El parámetro id se define como un tipo que acepta valores NULL (int?) en caso de que no se proporcione un valor id.

Se pasa una expresión lambda al método FirstOrDefaultAsync para seleccionar entidades de película que coincidan con los datos de enrutamiento o el valor de consulta de cadena.

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

Si se encuentra una película, se pasa una instancia del modelo Movie a la vista Details:

return View(movie);

Examine el contenido del archivo 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>

La instrucción @model de la parte superior del archivo de vista especifica el tipo de objeto que espera la vista. Cuando se ha creado el controlador de película, se ha incluido la siguiente instrucción @model:

@model MvcMovie.Models.Movie

Esta directiva @model permite el acceso a la película que el controlador ha pasado a la vista. El objeto Model está fuertemente tipado. Por ejemplo, en la vista Details.cshtml, el código pasa cada campo de película a los asistentes de HTML DisplayNameFor y DisplayFor con el objeto Model fuertemente tipado. Los métodos Create y Edit y las vistas también pasan un objeto de modelo Movie.

Examine la vista Index.cshtml y el método Index en el controlador Movies. Observe cómo el código crea un objeto List cuando llama al método View. El código pasa esta lista Movies desde el método de acción Index a la vista:

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

El código devuelve detalles del problema si la propiedad Movie del contexto de datos es null.

Cuando se ha creado el controlador de películas, el scaffolding ha incluido la siguiente instrucción @model en la parte superior del archivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

La directiva @model permite acceder a la lista de películas que el controlador pasó a la vista usando un objeto Model fuertemente tipado. Por ejemplo, en la vista Index.cshtml, el código recorre en bucle las películas con una instrucción foreach sobre el objeto Model fuertemente 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 el objeto Model es fuertemente tipado como un objeto IEnumerable<Movie>, cada elemento del bucle está tipado como Movie. Entre otras ventajas, el compilador valida los tipos usados en el código.

Recursos adicionales

En este tutorial, se agregan clases para administrar películas en una base de datos. Estas clases son el elemento "Model" de la aplicación MVC.

Estas clases de modelo se usan con Entity Framework Core (EF Core) para trabajar con una base de datos. EF Core es un marco de trabajo de asignación relacional de objetos (ORM) que simplifica el código de acceso de datos que se debe escribir.

Las clases de modelo creadas se conocen como clases POCO de Plain Old CLR Objects. Las clases POCO no tienen ninguna dependencia de EF Core. Solo definen las propiedades de los datos que se almacenan en la base de datos.

En este tutorial, primero se crean las clases de modelo, y EF Core crea la base de datos.

Agregar una clase de modelo de datos

Haga clic con el botón derecho en la carpeta Models>Agregar>Clase. Ponga al archivo el nombre Movie.cs.

Actualice el archivo Models/Movie.cs con el código siguiente:

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; }
    }
}

La clase Movie contiene un campo Id, que la base de datos requiere para la clave principal.

El atributo DataType en ReleaseDate especifica el tipo de los datos (Date). Con este atributo:

  • El usuario no tiene que especificar información horaria en el campo de fecha.
  • Solo se muestra la fecha, no información horaria.

Los elementos DataAnnotations se tratan en un tutorial posterior.

El signo de interrogación después de string indica que la propiedad admite un valor NULL. Para más información, consulte Tipos de referencia que admiten un valor NULL.

Adición de paquetes NuGet

En el menú Herramientas, seleccione Administrador de paquetes NuGet>Consola del Administrador de paquetes (PMC).

Menú de PMC

En la Consola del administrador de paquetes, ejecute el comando siguiente:

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

Los comandos anteriores agregan:

  • Proveedor de SQL Server de EF Core. El paquete de proveedor instala el paquete de EF Core como una dependencia.
  • Las utilidades utilizadas por los paquetes se instalaron de forma automática en el paso de scaffolding, más adelante en el tutorial.

Compile el proyecto para comprobar si hay errores del compilador.

Scaffolding de las páginas de películas

Use la herramienta de scaffolding para generar las páginas Create, Read, Update y Delete (CRUD) para el modelo de película.

En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Controladores y seleccione Agregar > Nuevo elemento con scaffolding.

vista del paso anterior

En el cuadro de diálogo Agregar scaffold, seleccione Controlador de MVC con vistas que usan Entity Framework > Agregar.

Cuadro de diálogo Agregar scaffold

Complete el cuadro de diálogo Add MVC Controller with views, using Entity Framework (Agregar un controlador de MVC con vistas que usan Entity Framework):

  • En la lista desplegable Clase de modelo, seleccione Movie (MvcMovie.Models) .
  • En la fila Clase de contexto de datos, seleccione el signo + (más).
    • En el cuadro de diálogo Agregar contexto de datos, se genera el nombre de clase MvcMovie.Data.MvcMovieContext.
    • Seleccione Agregar.
  • Vistas y Nombre del controlador: mantenga el valor predeterminado.
  • Seleccione Agregar.

Agregar contexto de datos con los valores predeterminados

Si recibe un mensaje de error, seleccione Agregar una segunda vez para intentarlo de nuevo.

Scaffolding actualiza lo siguiente:

  • Inserta las referencias de paquete necesarias en el archivo del proyecto MvcMovie.csproj.
  • Registra el contexto de la base de datos en el archivo Program.cs.
  • Agrega una cadena de conexión de la base de datos al archivo appsettings.json .

Scaffolding crea lo siguiente:

  • Un controlador de películas: Controllers/MoviesController.cs
  • Archivos de la vista Razor para las páginas Crear, Eliminar, Detalles, Editar e Índice: Views/Movies/*.cshtml
  • Una clase de contexto de datos: Data/MvcMovieContext.cs

La creación automática de estos archivos y actualizaciones de archivos se conoce como scaffolding.

Todavía no se pueden usar las páginas con scaffolding porque la base de datos no existe. La ejecución de la aplicación y la selección del vínculo Movie App (Aplicación de película) genera un mensaje de error No se puede abrir la base de datos o no se encuentra dicha tabla: Movie.

Compilar la aplicación

Compile la aplicación. El compilador genera varias advertencias sobre cómo se controlan los valores null. Consulte esta cuestión en GitHub y Tipos de referencia que aceptan valores NULL para obtener más información.

Para eliminar las advertencias de los tipos de referencia que admiten valores NULL, quite la siguiente línea del archivo MvcMovie.csproj:

<Nullable>enable</Nullable>

Esperamos corregir este problema en la próxima versión.

Migración inicial

Use la característica EF CoreMigraciones de para crear la base de datos. Las migraciones son un conjunto de herramientas que crean y actualizan una base de datos para que coincida con el modelo de datos.

En el menú Herramientas, seleccione Administrador de paquetes NuGet>Consola del Administrador de paquetes.

En la Consola del Administrador de paquetes (PMC), escriba los comandos siguientes:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Genera un archivo de migración Migrations/{timestamp}_InitialCreate.cs. El argumento InitialCreate es el nombre de la migración. Se puede usar cualquier nombre, pero, por convención, se selecciona uno que describa la migración. Como se trata de la primera migración, la clase generada contiene código para crear el esquema de la base de datos. El esquema de la base de datos se basa en el modelo especificado en la clase MvcMovieContext.

  • Update-Database: actualiza la base de datos a la migración más reciente, que ha creado el comando anterior. El comando ejecuta el método Up en el archivo Migrations/{time-stamp}_InitialCreate.cs, que crea la base de datos.

El comando Update-Database genera la siguiente advertencia:

No type was specified for the decimal column "Price" on entity type "Movie" (No se ha especificado ningún tipo en la columna decimal "Price" en el tipo de entidad "Movie"). This will cause values to be silently truncated if they do not fit in the default precision and scale. Explicitly specify the SQL server column type that can accommodate all the values using "HasColumnType()" (Especifique de forma explícita el tipo de columna de SQL Server que pueda acomodar todos los valores mediante "HasColumnType()").

Ignore la advertencia anterior, ya que se ha corregido en un tutorial posterior.

Para obtener más información sobre las herramientas de PMC para EF Core, vea Referencia de herramientas de EF Core: consola del Administrador de paquetes en Visual Studio.

Prueba de la aplicación

Ejecute la aplicación y seleccione el vínculo Movie App (Aplicación de película).

Si recibe una excepción similar a la siguiente, es posible que haya perdido el paso de migraciones:

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

Nota

Es posible que no pueda escribir comas decimales en el campo Price. La aplicación debe globalizarse para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos. Para obtener instrucciones sobre la globalización, consulte esta cuestión en GitHub.

Examen del registro y la clase del contexto de la base de datos generados

Con EF Core, el acceso a datos se realiza mediante un modelo. Un modelo se compone de clases de entidad y un objeto de contexto que representa una sesión con la base de datos. Este objeto de contexto permite consultar y guardar datos. El contexto de base de datos se deriva de Microsoft.EntityFrameworkCore.DbContext y especifica las entidades que se van a incluir en el modelo de datos.

Scaffolding crea la clase de contexto de la base de datos 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; }
    }
}

El código anterior crea una propiedad DbSet<Movie> que representa las películas de la base de datos.

Inserción de dependencias

ASP.NET Core integra la inserción de dependencias (DI). Los servicios, como el contexto de la base de datos, se registran con DI en Program.cs. Estos servicios se proporcionan a los componentes que los necesitan a través de parámetros de constructor.

En el archivo Controllers/MoviesController.cs, el constructor usa la inserción de dependencias para insertar el contexto de base de datos MvcMovieContext en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.

El scaffolding generó el siguiente código resaltado en Program.cs:

var builder = WebApplication.CreateBuilder(args);

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

El sistema de configuración de ASP.NET Core lee la cadena de conexión de la base de datos "MvcMovieContext".

Examen de la cadena de conexión de base de datos generada

Scaffolding agregó una cadena de conexión al archivo appsettings.json :

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

Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la clave ConnectionString del archivo appsettings.json .

La clase InitialCreate.

Examine el archivo de migración 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");
        }
    }
}

En el código anterior:

  • InitialCreate.Up crea la tabla Movie y configura Id como la clave principal.
  • InitialCreate.Down revierte los cambios de esquema realizados por la migración Up.

Inserción de dependencias en el controlador

Abra el archivo Controllers/MoviesController.cs y examine el constructor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

El constructor usa la inserción de dependencias para insertar el contexto de base de datos (MvcMovieContext) en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.

Pruebe la página Create. Escriba y envíe los datos.

Pruebe las páginas Edit, Details y Delete.

Modelos fuertemente tipados y la directiva @model

Anteriormente en este tutorial, vimos cómo un controlador puede pasar datos u objetos a una vista mediante el diccionario ViewData. El diccionario ViewData es un objeto dinámico que proporciona una cómoda manera enlazada en tiempo de ejecución de pasar información a una vista.

MVC ofrece la capacidad de pasar objetos de modelo fuertemente tipados a una vista. Este enfoque fuertemente tipado permite comprobar el código en tiempo de compilación. El mecanismo de scaffolding pasó un modelo fuertemente tipado en las vistas y la clase MoviesController.

Examine el método Details en el archivo 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);
}

El parámetro id suele pasarse como datos de ruta. Por ejemplo, https://localhost:5001/movies/details/1 establece:

  • El controlador en el controlador movies, el primer segmento de dirección URL.
  • La acción en details, el segundo segmento de dirección URL.
  • id en 1,el último segmento de dirección URL.

id se puede pasar con una cadena de consulta, como en el ejemplo siguiente:

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

El parámetro id se define como un tipo que acepta valores NULL (int?) en caso de que no se proporcione un valor id.

Se pasa una expresión lambda al método FirstOrDefaultAsync para seleccionar entidades de película que coincidan con los datos de enrutamiento o el valor de consulta de cadena.

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

Si se encuentra una película, se pasa una instancia del modelo Movie a la vista Details:

return View(movie);

Examine el contenido del archivo 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>

La instrucción @model de la parte superior del archivo de vista especifica el tipo de objeto que espera la vista. Cuando se ha creado el controlador de película, se ha incluido la siguiente instrucción @model:

@model MvcMovie.Models.Movie

Esta directiva @model permite el acceso a la película que el controlador ha pasado a la vista. El objeto Model está fuertemente tipado. Por ejemplo, en la vista Details.cshtml, el código pasa cada campo de película a los asistentes de HTML DisplayNameFor y DisplayFor con el objeto Model fuertemente tipado. Los métodos Create y Edit y las vistas también pasan un objeto de modelo Movie.

Examine la vista Index.cshtml y el método Index en el controlador Movies. Observe cómo el código crea un objeto List cuando llama al método View. El código pasa esta lista Movies desde el método de acción Index a la vista:

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

Cuando se ha creado el controlador de películas, el scaffolding ha incluido la siguiente instrucción @model en la parte superior del archivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

La directiva @model permite acceder a la lista de películas que el controlador pasó a la vista usando un objeto Model fuertemente tipado. Por ejemplo, en la vista Index.cshtml, el código recorre en bucle las películas con una instrucción foreach sobre el objeto Model fuertemente 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 el objeto Model es fuertemente tipado como un objeto IEnumerable<Movie>, cada elemento del bucle está tipado como Movie. Entre otras ventajas, el compilador valida los tipos usados en el código.

Recursos adicionales

En este tutorial, se agregan clases para administrar películas en una base de datos. Estas clases son el elemento "Model" de la aplicación MVC.

Estas clases de modelo se usan con Entity Framework Core (EF Core) para trabajar con una base de datos. EF Core es un marco de trabajo de asignación relacional de objetos (ORM) que simplifica el código de acceso de datos que se debe escribir.

Las clases de modelo creadas se conocen como clases POCO de Plain Old CLR Objects. Las clases POCO no tienen ninguna dependencia de EF Core. Solo definen las propiedades de los datos que se almacenan en la base de datos.

En este tutorial, primero se crean las clases de modelo, y EF Core crea la base de datos.

Agregar una clase de modelo de datos

Haga clic con el botón derecho en la carpeta Models>Agregar>Clase. Ponga al archivo el nombre Movie.cs.

Actualice el archivo Models/Movie.cs con el código siguiente:

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; }
    }
}

La clase Movie contiene un campo Id, que la base de datos requiere para la clave principal.

El atributo DataType en ReleaseDate especifica el tipo de los datos (Date). Con este atributo:

  • El usuario no tiene que especificar información horaria en el campo de fecha.
  • Solo se muestra la fecha, no información horaria.

Los elementos DataAnnotations se tratan en un tutorial posterior.

Adición de paquetes NuGet

En el menú Herramientas, seleccione Administrador de paquetes NuGet>Consola del Administrador de paquetes (PMC).

Menú de PMC

En la Consola del administrador de paquetes, ejecute el comando siguiente:

Install-Package Microsoft.EntityFrameworkCore.Design

Los comandos anteriores agregan:

  • Proveedor de SQL Server de EF Core. El paquete de proveedor instala el paquete de EF Core como una dependencia.
  • Las utilidades utilizadas por los paquetes se instalaron de forma automática en el paso de scaffolding, más adelante en el tutorial.

Compile el proyecto para comprobar si hay errores del compilador.

Scaffolding de las páginas de películas

Use la herramienta de scaffolding para generar las páginas Create, Read, Update y Delete (CRUD) para el modelo de película.

En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Controladores y seleccione Agregar > Nuevo elemento con scaffolding.

vista del paso anterior

En el cuadro de diálogo Agregar scaffold, seleccione Controlador de MVC con vistas que usan Entity Framework > Agregar.

Cuadro de diálogo Agregar scaffold

Complete el cuadro de diálogo Add MVC Controller with views, using Entity Framework (Agregar un controlador de MVC con vistas que usan Entity Framework):

  • En la lista desplegable Clase de modelo, seleccione Movie (MvcMovie.Models) .
  • En la fila Clase de contexto de datos, seleccione el signo + (más).
    • En el cuadro de diálogo Agregar contexto de datos, se genera el nombre de clase MvcMovie.Data.MvcMovieContext.
    • Seleccione Agregar.
  • Vistas y Nombre del controlador: mantenga el valor predeterminado.
  • Seleccione Agregar.

Agregar contexto de datos con los valores predeterminados

Scaffolding actualiza lo siguiente:

  • Inserta las referencias de paquete necesarias en el archivo del proyecto MvcMovie.csproj.
  • Registra el contexto de la base de datos en Startup.ConfigureServices del archivo Startup.cs.
  • Agrega una cadena de conexión de la base de datos al archivo appsettings.json .

Scaffolding crea lo siguiente:

  • Un controlador de películas: Controllers/MoviesController.cs
  • Archivos de la vista Razor para las páginas Crear, Eliminar, Detalles, Editar e Índice: Views/Movies/*.cshtml
  • Una clase de contexto de datos: Data/MvcMovieContext.cs

La creación automática de estos archivos y actualizaciones de archivos se conoce como scaffolding.

Todavía no se pueden usar las páginas con scaffolding porque la base de datos no existe. La ejecución de la aplicación y la selección del vínculo Movie App (Aplicación de película) genera un mensaje de error No se puede abrir la base de datos o no se encuentra dicha tabla: Movie.

Migración inicial

Use la característica EF CoreMigraciones de para crear la base de datos. Las migraciones son un conjunto de herramientas que crean y actualizan una base de datos para que coincida con el modelo de datos.

En el menú Herramientas, seleccione Administrador de paquetes NuGet>Consola del Administrador de paquetes.

En la Consola del Administrador de paquetes (PMC), escriba los comandos siguientes:

Add-Migration InitialCreate
Update-Database

  • Add-Migration InitialCreate: Genera un archivo de migración Migrations/{timestamp}_InitialCreate.cs. El argumento InitialCreate es el nombre de la migración. Se puede usar cualquier nombre, pero, por convención, se selecciona uno que describa la migración. Como se trata de la primera migración, la clase generada contiene código para crear el esquema de la base de datos. El esquema de la base de datos se basa en el modelo especificado en la clase MvcMovieContext.

  • Update-Database: actualiza la base de datos a la migración más reciente, que ha creado el comando anterior. El comando ejecuta el método Up en el archivo Migrations/{time-stamp}_InitialCreate.cs, que crea la base de datos.

El comando Update-Database genera la siguiente advertencia:

No type was specified for the decimal column "Price" on entity type "Movie" (No se ha especificado ningún tipo en la columna decimal "Price" en el tipo de entidad "Movie"). This will cause values to be silently truncated if they do not fit in the default precision and scale. Explicitly specify the SQL server column type that can accommodate all the values using "HasColumnType()" (Especifique de forma explícita el tipo de columna de SQL Server que pueda acomodar todos los valores mediante "HasColumnType()").

Ignore la advertencia anterior, ya que se ha corregido en un tutorial posterior.

Para obtener más información sobre las herramientas de PMC para EF Core, vea Referencia de herramientas de EF Core: consola del Administrador de paquetes en Visual Studio.

Prueba de la aplicación

Ejecute la aplicación y seleccione el vínculo Movie App (Aplicación de película).

Si recibe una excepción similar a la siguiente, es posible que haya perdido el paso de migraciones:

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

Nota

Es posible que no pueda escribir comas decimales en el campo Price. La aplicación debe globalizarse para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos. Para obtener instrucciones sobre la globalización, consulte esta cuestión en GitHub.

Examen del registro y la clase del contexto de la base de datos generados

Con EF Core, el acceso a datos se realiza mediante un modelo. Un modelo se compone de clases de entidad y un objeto de contexto que representa una sesión con la base de datos. Este objeto de contexto permite consultar y guardar datos. El contexto de base de datos se deriva de Microsoft.EntityFrameworkCore.DbContext y especifica las entidades que se van a incluir en el modelo de datos.

Scaffolding crea la clase de contexto de la base de datos 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; }
    }
}

El código anterior crea una propiedad DbSet<Movie> que representa las películas de la base de datos.

ASP.NET Core integra la inserción de dependencias (DI). Los servicios, como el contexto de la base de datos, deben registrarse con DI en Startup. Los componentes que estos servicios requieren se proporcionan a través de parámetros de constructor.

En el archivo Controllers/MoviesController.cs, el constructor usa la inserción de dependencias para insertar el contexto de base de datos MvcMovieContext en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.

El scaffolding generó el siguiente código resaltado en Startup.ConfigureServices:

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

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

El sistema de configuración de ASP.NET Core lee la cadena de conexión de la base de datos "MvcMovieContext".

Examen de la cadena de conexión de base de datos generada

Scaffolding agregó una cadena de conexión al archivo 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"
  }
}

Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la clave ConnectionString del archivo appsettings.json .

La clase InitialCreate.

Examine el archivo de migración 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");
    }
}

En el código anterior:

  • InitialCreate.Up crea la tabla Movie y configura Id como la clave principal.
  • InitialCreate.Down revierte los cambios de esquema realizados por la migración Up.

Inserción de dependencias en el controlador

Abra el archivo Controllers/MoviesController.cs y examine el constructor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

El constructor usa la inserción de dependencias para insertar el contexto de base de datos (MvcMovieContext) en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.

Pruebe la página Create. Escriba y envíe los datos.

Pruebe las páginas Edit, Details y Delete.

Modelos fuertemente tipados y la directiva @model

Anteriormente en este tutorial, vimos cómo un controlador puede pasar datos u objetos a una vista mediante el diccionario ViewData. El diccionario ViewData es un objeto dinámico que proporciona una cómoda manera enlazada en tiempo de ejecución de pasar información a una vista.

MVC ofrece la capacidad de pasar objetos de modelo fuertemente tipados a una vista. Este enfoque fuertemente tipado permite comprobar el código en tiempo de compilación. El mecanismo de scaffolding pasó un modelo fuertemente tipado en las vistas y la clase MoviesController.

Examine el método Details en el archivo 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);
}

El parámetro id suele pasarse como datos de ruta. Por ejemplo, https://localhost:5001/movies/details/1 establece:

  • El controlador en el controlador movies, el primer segmento de dirección URL.
  • La acción en details, el segundo segmento de dirección URL.
  • id en 1,el último segmento de dirección URL.

id se puede pasar con una cadena de consulta, como en el ejemplo siguiente:

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

El parámetro id se define como un tipo que acepta valores NULL (int?) en caso de que no se proporcione un valor id.

Se pasa una expresión lambda al método FirstOrDefaultAsync para seleccionar entidades de película que coincidan con los datos de enrutamiento o el valor de consulta de cadena.

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

Si se encuentra una película, se pasa una instancia del modelo Movie a la vista Details:

return View(movie);

Examine el contenido del archivo 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>

La instrucción @model de la parte superior del archivo de vista especifica el tipo de objeto que espera la vista. Cuando se ha creado el controlador de película, se ha incluido la siguiente instrucción @model:

@model MvcMovie.Models.Movie

Esta directiva @model permite el acceso a la película que el controlador ha pasado a la vista. El objeto Model está fuertemente tipado. Por ejemplo, en la vista Details.cshtml, el código pasa cada campo de película a los asistentes de HTML DisplayNameFor y DisplayFor con el objeto Model fuertemente tipado. Los métodos Create y Edit y las vistas también pasan un objeto de modelo Movie.

Examine la vista Index.cshtml y el método Index en el controlador Movies. Observe cómo el código crea un objeto List cuando llama al método View. El código pasa esta lista Movies desde el método de acción Index a la vista:

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

Cuando se ha creado el controlador de películas, el scaffolding ha incluido la siguiente instrucción @model en la parte superior del archivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

La directiva @model permite acceder a la lista de películas que el controlador pasó a la vista usando un objeto Model fuertemente tipado. Por ejemplo, en la vista Index.cshtml, el código recorre en bucle las películas con una instrucción foreach sobre el objeto Model fuertemente 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 el objeto Model es fuertemente tipado como un objeto IEnumerable<Movie>, cada elemento del bucle está tipado como Movie. Entre otras ventajas, el compilador valida los tipos usados en el código.

Registros SQL de Entity Framework Core

La configuración de registros suele proporcionarla la sección Logging de los archivos appsettings.{Environment}.json. Para registrar instrucciones SQL, agregue "Microsoft.EntityFrameworkCore.Database.Command": "Information" al archivo appsettings.Development.json:

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

Con el archivo JSON anterior, las instrucciones SQL se muestran en la línea de comandos y en ventana de salida de Visual Studio.

Para más información, vea Registros en .NET Core y ASP.NET Core y esta incidencia de GitHub.

Recursos adicionales

En este tutorial, se agregan clases para administrar películas en una base de datos. Estas clases son el elemento "Model" de la aplicación MVC.

Estas clases de modelo se usan con Entity Framework Core (EF Core) para trabajar con una base de datos. EF Core es un marco de trabajo de asignación relacional de objetos (ORM) que simplifica el código de acceso de datos que se debe escribir.

Las clases de modelo creadas se conocen como clases POCO de Plain Old CLR Objects. Las clases POCO no tienen ninguna dependencia de EF Core. Solo definen las propiedades de los datos que se almacenan en la base de datos.

En este tutorial, primero se crean las clases de modelo, y EF Core crea la base de datos.

Agregar una clase de modelo de datos

Haga clic con el botón derecho en la carpeta Models>Agregar>Clase. Ponga al archivo el nombre Movie.cs.

Actualice el archivo Movie.cs con el código siguiente:

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; }
    }
}

La clase Movie contiene un campo Id, que la base de datos requiere para la clave principal.

El atributo DataType en ReleaseDate especifica el tipo de los datos (Date). Con este atributo:

  • El usuario no tiene que especificar información horaria en el campo de fecha.
  • Solo se muestra la fecha, no información horaria.

Los elementos DataAnnotations se tratan en un tutorial posterior.

Adición de paquetes NuGet

En el menú Herramientas, seleccione Administrador de paquetes NuGet>Consola del Administrador de paquetes (PMC).

Menú de PMC

En la Consola del administrador de paquetes, ejecute el comando siguiente:

Install-Package Microsoft.EntityFrameworkCore.SqlServer

El comando anterior agrega el proveedor de SQL Server de EF Core. El paquete de proveedor instala el paquete de EF Core como una dependencia. Los paquetes adicionales se instalan de forma automática en el paso de scaffolding más adelante en el tutorial.

Creación de una clase de contexto de base de datos

Se necesita una clase de contexto de base de datos para coordinar la funcionalidad de EF Core (crear, leer, actualizar, eliminar) para el modelo Movie. El contexto de base de datos se deriva de Microsoft.EntityFrameworkCore.DbContext y especifica las entidades que se van a incluir en el modelo de datos.

Cree una carpeta Data.

Agregue un archivo Data/MvcMovieContext.cs con el código siguiente:

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; }
    }
}

En el código anterior se crea una propiedad DbSet<Movie> para el conjunto de entidades. En la terminología de Entity Framework, un conjunto de entidades suele corresponder a una tabla de base de datos. Una entidad se corresponde con una fila de la tabla.

Registro del contexto de base de datos

ASP.NET Core integra la inserción de dependencias (DI). Los servicios (como el contexto de base de datos de EF Core) se deben registrar con la inserción de dependencias durante el inicio de la aplicación. Estos servicios se proporcionan a los componentes que los necesitan (como páginas Razor) a través de parámetros de constructor. El código de constructor que obtiene una instancia de contexto de base de datos se muestra más adelante en el tutorial. En esta sección, se registra el contexto de base de datos con el contenedor de inserción de dependencias.

Agregue las instrucciones using siguientes en la parte superior de Startup.cs:

using MvcMovie.Data;
using Microsoft.EntityFrameworkCore;

Agregue el código resaltado siguiente en Startup.ConfigureServices:

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

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

El nombre de la cadena de conexión se pasa al contexto mediante una llamada a un método en un objeto DbContextOptions. Para el desarrollo local, el sistema de configuración de ASP.NET Core lee la cadena de conexión desde el archivo appsettings.json.

Examen de la cadena de conexión de base de datos

Agregue una cadena de conexión al 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 el proyecto para comprobar si hay errores del compilador.

Scaffolding de las páginas de películas

Use la herramienta de scaffolding para crear páginas de creación, lectura, actualización y eliminación (CRUD) del modelo de película.

En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Controladores> Agregar > Nuevo elemento con scaffolding.

vista del paso anterior

En el cuadro de diálogo Agregar scaffold, seleccione Controlador de MVC con vistas que usan Entity Framework > Agregar.

Cuadro de diálogo Agregar scaffold

Rellene el cuadro de diálogo Agregar controlador:

  • Clase de modelo:Movie (MvcMovie.Models).
  • Clase de contexto de datos:MvcMovieContext (MvcMovie.Data)

Adición de contexto de datos

  • Vistas: conserve el valor predeterminado de cada opción activada.
  • Nombre del controlador: conserve el valor predeterminado MoviesController.
  • Seleccione Agregar.

Visual Studio crea:

  • Un controlador Movies (Controllers/MoviesController.cs)
  • Archivos de vistas de Razor para las páginas de creación, eliminación, detalles, edición e índice (*Views/Movies/`.cshtml`)

La creación automática de estos archivos se conoce como scaffolding.

Todavía no se pueden usar las páginas con scaffolding porque la base de datos no existe. Si ejecuta la aplicación y hace clic en el vínculo Movie App, obtendrá un mensaje de error Cannot open database (No se puede abrir la base de datos) o no such table: Movie (no existe la tabla: Movie).

Migración inicial

Use la característica EF CoreMigraciones de para crear la base de datos. Las migraciones son un conjunto de herramientas que permiten crear y actualizar una base de datos para que coincida con el modelo de datos.

En el menú Herramientas, seleccione Administrador de paquetes NuGet>Consola del Administrador de paquetes (PMC).

En PCM, escriba los siguientes comandos:

Add-Migration InitialCreate
Update-Database
  • Add-Migration InitialCreate: Genera un archivo de migración Migrations/{timestamp}_InitialCreate.cs. El argumento InitialCreate es el nombre de la migración. Se puede usar cualquier nombre, pero, por convención, se selecciona uno que describa la migración. Como se trata de la primera migración, la clase generada contiene código para crear el esquema de la base de datos. El esquema de la base de datos se basa en el modelo especificado en la clase MvcMovieContext.

  • Update-Database: actualiza la base de datos a la migración más reciente, que ha creado el comando anterior. El comando ejecuta el método Up en el archivo Migrations/{time-stamp}_InitialCreate.cs, que crea la base de datos.

    El comando de actualización de la base de datos genera la advertencia siguiente:

    No type was specified for the decimal column "Price" on entity type "Movie" (No se ha especificado ningún tipo en la columna decimal "Price" en el tipo de entidad "Movie"). This will cause values to be silently truncated if they do not fit in the default precision and scale. Explicitly specify the SQL server column type that can accommodate all the values using "HasColumnType()" (Especifique de forma explícita el tipo de columna de SQL Server que pueda acomodar todos los valores mediante "HasColumnType()").

    Puede omitir dicha advertencia, ya que se corregirá en un tutorial posterior.

Para obtener más información sobre las herramientas de PMC para EF Core, vea Referencia de herramientas de EF Core: consola del Administrador de paquetes en Visual Studio.

La clase InitialCreate

Examine el archivo de migración 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");
    }
}

El método Up crea la tabla Movie y configura Id como la clave principal. El método Down revierte los cambios de esquema realizados por la migración Up.

Prueba de la aplicación

  • Ejecute la aplicación y haga clic en el vínculo Movie App.

    Si obtiene una excepción similar a una de las siguientes:

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

Probablemente haya omitido el paso de migraciones.

  • Pruebe la página Create. Escriba y envíe los datos.

    Nota

    Es posible que no pueda escribir comas decimales en el campo Price. La aplicación debe globalizarse para que la validación de jQuery sea compatible con configuraciones regionales distintas del inglés que usan una coma (",") en lugar de un punto decimal y formatos de fecha distintos del de Estados Unidos. Para obtener instrucciones sobre la globalización, consulte esta cuestión en GitHub.

  • Pruebe las páginas Edit, Details y Delete.

Inserción de dependencias en el controlador

Abra el archivo Controllers/MoviesController.cs y examine el constructor:

public class MoviesController : Controller
{
    private readonly MvcMovieContext _context;

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

El constructor usa la inserción de dependencias para insertar el contexto de base de datos (MvcMovieContext) en el controlador. El contexto de base de datos se usa en cada uno de los métodos CRUD del controlador.

Modelos fuertemente tipados y la palabra clave @model

Anteriormente en este tutorial, vimos cómo un controlador puede pasar datos u objetos a una vista mediante el diccionario ViewData. El diccionario ViewData es un objeto dinámico que proporciona una cómoda manera enlazada en tiempo de ejecución de pasar información a una vista.

MVC también ofrece la capacidad de pasar objetos de modelo fuertemente tipados a una vista. Este enfoque fuertemente tipado permite comprobar el código en tiempo de compilación. En el mecanismo de scaffolding se ha usado este enfoque (que consiste en pasar un modelo fuertemente tipado) con la clase MoviesController y las vistas.

Examine el método Details en el archivo 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);
}

El parámetro id suele pasarse como datos de ruta. Por ejemplo, https://localhost:5001/movies/details/1 establece:

  • El controlador en el controlador movies (el primer segmento de dirección URL).
  • La acción en details (el segundo segmento de dirección URL).
  • El identificador en 1 (el último segmento de dirección URL).

También puede pasar id con una cadena de consulta como se indica a continuación:

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

El parámetro id se define como un tipo que acepta valores NULL (int?) en caso de que no se proporcione un valor de identificador.

Se pasa una expresión lambda a FirstOrDefaultAsync para seleccionar entidades de película que coincidan con los datos de enrutamiento o el valor de consulta de cadena.

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

Si se encuentra una película, se pasa una instancia del modelo Movie a la vista Details:

return View(movie);

Examine el contenido del archivo 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>

La instrucción @model de la parte superior del archivo de vista especifica el tipo de objeto que espera la vista. Cuando se ha creado el controlador de película, se ha incluido la siguiente instrucción @model:

@model MvcMovie.Models.Movie

Esta directiva @model permite el acceso a la película que el controlador ha pasado a la vista. El objeto Model está fuertemente tipado. Por ejemplo, en la vista Details.cshtml, el código pasa cada campo de película a los asistentes de HTML DisplayNameFor y DisplayFor con el objeto Model fuertemente tipado. Los métodos Create y Edit y las vistas también pasan un objeto de modelo Movie.

Examine la vista Index.cshtml y el método Index en el controlador Movies. Observe cómo el código crea un objeto List cuando llama al método View. El código pasa esta lista Movies desde el método de acción Index a la vista:

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

Cuando se ha creado el controlador de películas, el scaffolding ha incluido la siguiente instrucción @model en la parte superior del archivo Index.cshtml:

@model IEnumerable<MvcMovie.Models.Movie>

Esta directiva @model permite acceder a la lista de películas que el controlador pasó a la vista usando un objeto Model fuertemente tipado. Por ejemplo, en la vista Index.cshtml, el código recorre en bucle las películas con una instrucción foreach sobre el objeto Model fuertemente 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 el objeto Model es fuertemente tipado (como un objeto IEnumerable<Movie>), cada elemento del bucle está tipado como Movie. Entre otras ventajas, esto implica que se obtiene la comprobación del código en tiempo de compilación.

Recursos adicionales