Parte 5. Actualización de las páginas generadas en una aplicación de ASP.NET Core
Nota:
Esta no es la versión más reciente de este artículo. Para la versión actual, consulte la versión de .NET 9 de este artículo.
Advertencia
Esta versión de ASP.NET Core ya no se admite. Para obtener más información, consulta la Directiva de soporte técnico de .NET y .NET Core. Para la versión actual, consulta 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 de .NET 9 de este artículo.
La aplicación de películas con scaffolding pinta bien, pero la presentación no es ideal. ReleaseDate debe tener dos palabras: Release Date (Fecha lanzamiento).
Actualizar el modelo
Actualice Models/Movie.cs
con el siguiente código resaltado:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models;
public class Movie
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; } = string.Empty;
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
En el código anterior:
- La anotación de datos
[Column(TypeName = "decimal(18, 2)")]
permite que Entity Framework Core asigne correctamentePrice
a la moneda en la base de datos. Para más información, vea Tipos de datos. - El atributo [Display] especifica el nombre para mostrar de un campo. En el código anterior,
Release Date
en lugar deReleaseDate
. - El atributo [DataType] especifica el tipo de datos (
Date
). No se muestra la información de hora almacenada en el campo.
En el próximo tutorial, hablaremos de DataAnnotations.
Vaya a Pages/Movies y mantenga el mouse sobre un vínculo de edición para ver la dirección URL de destino.
Los vínculos de edición, detalles y eliminación se generan mediante el Asistente de etiquetas delimitadoras en el archivo Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Las aplicaciones auxiliares de etiquetas permiten que el código de servidor participe en la creación y la representación de elementos HTML en archivos de Razor.
En el código anterior, el asistente de etiquetas de delimitador genera de forma dinámica el valor del atributo href
HTML desde la página de Razor Pages (la ruta es relativa), el elemento asp-page
y el identificador de ruta (asp-route-id
). Vea Generación de direcciones URL para las páginas para obtener más información.
Use Ver código fuente en un explorador para examinar el marcado generado. A continuación se muestra una parte del HTML generado:
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Los vínculos generados de forma dinámica pasan el identificador de la película con una cadena de consulta. Por ejemplo, ?id=1
en https://localhost:5001/Movies/Details?id=1
.
Adición de la plantilla de ruta
Actualice las páginas de Razor Edit, Details y Delete para usar la plantilla de ruta {id:int}
. Cambie la directiva de página de cada una de estas páginas de @page
a @page "{id:int}"
. Ejecute la aplicación y luego vea el origen.
El HTML generado agrega el identificador a la parte de la ruta de acceso de la dirección URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Una solicitud a la página con la plantilla de ruta {id:int}
que no incluya el entero devuelve un error HTTP 404 (no encontrado). Por ejemplo, https://localhost:5001/Movies/Details
devuelve un error 404. Para que el identificador sea opcional, anexe ?
a la restricción de ruta:
@page "{id:int?}"
Para probar el comportamiento de @page "{id:int?}"
:
- Establezca la directiva de página de
Pages/Movies/Details.cshtml
en@page "{id:int?}"
. - Establezca un punto de interrupción en
public async Task<IActionResult> OnGetAsync(int? id)
(enPages/Movies/Details.cshtml.cs
). - Navegue a
https://localhost:5001/Movies/Details/
.
Con la directiva @page "{id:int}"
, el punto de interrupción nunca se alcanza. El motor de enrutamiento devuelve HTTP 404. Con @page "{id:int?}"
, el método OnGetAsync
devuelve NotFound
(HTTP 404):
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
else
{
Movie = movie;
}
return Page();
}
Revisión del control de excepciones de simultaneidad
Revise el método OnPostAsync
en el archivo Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
El código anterior detecta las excepciones de simultaneidad cuando un cliente elimina la película y el otro cliente publica cambios en ella.
Para probar el bloque catch
:
- establezca un punto de interrupción en
catch (DbUpdateConcurrencyException)
. - Seleccione Editar para una película y realice cambios, pero no seleccione Guardar.
- En otra ventana del explorador, seleccione el vínculo de eliminación de la misma película y luego elimínela.
- En la ventana anterior del explorador, publique los cambios en la película.
Es posible que el código de producción quiera detectar conflictos de simultaneidad. Vea Administración de conflictos de simultaneidad para más información.
Revisión de publicaciones y enlaces
Examine el archivo Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
Cuando se realiza una solicitud HTTP GET a la página Movies/Edit (por ejemplo, https://localhost:5001/Movies/Edit/3
):
- El método
OnGetAsync
obtiene la película en la base de datos y devuelve el métodoPage
. - El método
Page
representa la página dePages/Movies/Edit.cshtml
Razor. El archivoPages/Movies/Edit.cshtml
contiene la directiva de modelo (@model RazorPagesMovie.Pages.Movies.EditModel
), que hace que el modelo de película esté disponible en la página. - Se abre el formulario de edición con los valores de la película.
Cuando se publica la página Movies/Edit:
Los valores del formulario de la página se enlazan a la propiedad
Movie
. El atributo[BindProperty]
habilita el enlace de modelos.[BindProperty] public Movie Movie { get; set; }
Si hay errores en el estado del modelo (por ejemplo,
ReleaseDate
no se puede convertir en una fecha), el formulario se vuelve a mostrar con los valores enviados.Si no hay ningún error en el modelo, se guarda la película.
Los métodos HTTP GET de las páginas de Razor Index, Create y Delete siguen un patrón similar. El método OnPostAsync
HTTP POST de la página de Razor Create sigue un patrón similar al del método OnPostAsync
de la página de Razor Edit.
Pasos siguientes
La aplicación de películas con scaffolding pinta bien, pero la presentación no es ideal. ReleaseDate debe tener dos palabras: Release Date (Fecha lanzamiento).
Actualizar el modelo
Actualice Models/Movie.cs
con el siguiente código resaltado:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models;
public class Movie
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; } = string.Empty;
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
En el código anterior:
- La anotación de datos
[Column(TypeName = "decimal(18, 2)")]
permite que Entity Framework Core asigne correctamentePrice
a la moneda en la base de datos. Para más información, vea Tipos de datos. - El atributo [Display] especifica el nombre para mostrar de un campo. En el código anterior,
Release Date
en lugar deReleaseDate
. - El atributo [DataType] especifica el tipo de datos (
Date
). No se muestra la información de hora almacenada en el campo.
En el próximo tutorial, hablaremos de DataAnnotations.
Vaya a Pages/Movies y mantenga el mouse sobre un vínculo de edición para ver la dirección URL de destino.
Los vínculos de edición, detalles y eliminación se generan mediante el Asistente de etiquetas delimitadoras en el archivo Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Las aplicaciones auxiliares de etiquetas permiten que el código de servidor participe en la creación y la representación de elementos HTML en archivos de Razor.
En el código anterior, el asistente de etiquetas de delimitador genera de forma dinámica el valor del atributo href
HTML desde la página de Razor Pages (la ruta es relativa), el elemento asp-page
y el identificador de ruta (asp-route-id
). Vea Generación de direcciones URL para las páginas para obtener más información.
Use Ver código fuente en un explorador para examinar el marcado generado. A continuación se muestra una parte del HTML generado:
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Los vínculos generados de forma dinámica pasan el identificador de la película con una cadena de consulta. Por ejemplo, ?id=1
en https://localhost:5001/Movies/Details?id=1
.
Adición de la plantilla de ruta
Actualice las páginas de Razor Edit, Details y Delete para usar la plantilla de ruta {id:int}
. Cambie la directiva de página de cada una de estas páginas de @page
a @page "{id:int}"
. Ejecute la aplicación y luego vea el origen.
El HTML generado agrega el identificador a la parte de la ruta de acceso de la dirección URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Una solicitud a la página con la plantilla de ruta {id:int}
que no incluya el entero devuelve un error HTTP 404 (no encontrado). Por ejemplo, https://localhost:5001/Movies/Details
devuelve un error 404. Para que el identificador sea opcional, anexe ?
a la restricción de ruta:
@page "{id:int?}"
Para probar el comportamiento de @page "{id:int?}"
:
- Establezca la directiva de página de
Pages/Movies/Details.cshtml
en@page "{id:int?}"
. - Establezca un punto de interrupción en
public async Task<IActionResult> OnGetAsync(int? id)
(enPages/Movies/Details.cshtml.cs
). - Navegue a
https://localhost:5001/Movies/Details/
.
Con la directiva @page "{id:int}"
, el punto de interrupción nunca se alcanza. El motor de enrutamiento devuelve HTTP 404. Con @page "{id:int?}"
, el método OnGetAsync
devuelve NotFound
(HTTP 404):
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
Revisión del control de excepciones de simultaneidad
Revise el método OnPostAsync
en el archivo Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
El código anterior detecta las excepciones de simultaneidad cuando un cliente elimina la película y el otro cliente publica cambios en ella.
Para probar el bloque catch
:
- establezca un punto de interrupción en
catch (DbUpdateConcurrencyException)
. - Seleccione Editar para una película y realice cambios, pero no seleccione Guardar.
- En otra ventana del explorador, seleccione el vínculo de eliminación de la misma película y luego elimínela.
- En la ventana anterior del explorador, publique los cambios en la película.
Es posible que el código de producción quiera detectar conflictos de simultaneidad. Vea Administración de conflictos de simultaneidad para más información.
Revisión de publicaciones y enlaces
Examine el archivo Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
Cuando se realiza una solicitud HTTP GET a la página Movies/Edit (por ejemplo, https://localhost:5001/Movies/Edit/3
):
- El método
OnGetAsync
obtiene la película en la base de datos y devuelve el métodoPage
. - El método
Page
representa la página dePages/Movies/Edit.cshtml
Razor. El archivoPages/Movies/Edit.cshtml
contiene la directiva de modelo (@model RazorPagesMovie.Pages.Movies.EditModel
), que hace que el modelo de película esté disponible en la página. - Se abre el formulario de edición con los valores de la película.
Cuando se publica la página Movies/Edit:
Los valores del formulario de la página se enlazan a la propiedad
Movie
. El atributo[BindProperty]
habilita el enlace de modelos.[BindProperty] public Movie Movie { get; set; }
Si hay errores en el estado del modelo (por ejemplo,
ReleaseDate
no se puede convertir en una fecha), el formulario se vuelve a mostrar con los valores enviados.Si no hay ningún error en el modelo, se guarda la película.
Los métodos HTTP GET de las páginas de Razor Index, Create y Delete siguen un patrón similar. El método OnPostAsync
HTTP POST de la página de Razor Create sigue un patrón similar al del método OnPostAsync
de la página de Razor Edit.
Pasos siguientes
La aplicación de películas con scaffolding pinta bien, pero la presentación no es ideal. ReleaseDate debe tener dos palabras: Release Date (Fecha lanzamiento).
Actualizar el modelo
Actualice Models/Movie.cs
con el siguiente código resaltado:
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models;
public class Movie
{
public int Id { get; set; }
public string Title { get; set; } = string.Empty;
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; } = string.Empty;
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
En el código anterior:
- La anotación de datos
[Column(TypeName = "decimal(18, 2)")]
permite que Entity Framework Core asigne correctamentePrice
a la moneda en la base de datos. Para más información, vea Tipos de datos. - El atributo [Display] especifica el nombre para mostrar de un campo. En el código anterior,
Release Date
en lugar deReleaseDate
. - El atributo [DataType] especifica el tipo de datos (
Date
). No se muestra la información de hora almacenada en el campo.
En el próximo tutorial, hablaremos de DataAnnotations.
Vaya a Pages/Movies y mantenga el mouse sobre un vínculo de edición para ver la dirección URL de destino.
Los vínculos de edición, detalles y eliminación se generan mediante el Asistente de etiquetas delimitadoras en el archivo Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Las aplicaciones auxiliares de etiquetas permiten que el código de servidor participe en la creación y la representación de elementos HTML en archivos de Razor.
En el código anterior, el asistente de etiquetas de delimitador genera de forma dinámica el valor del atributo href
HTML desde la página de Razor Pages (la ruta es relativa), el elemento asp-page
y el identificador de ruta (asp-route-id
). Vea Generación de direcciones URL para las páginas para obtener más información.
Use Ver código fuente en un explorador para examinar el marcado generado. A continuación se muestra una parte del HTML generado:
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Los vínculos generados de forma dinámica pasan el identificador de la película con una cadena de consulta. Por ejemplo, ?id=1
en https://localhost:5001/Movies/Details?id=1
.
Adición de la plantilla de ruta
Actualice las páginas de Razor Edit, Details y Delete para usar la plantilla de ruta {id:int}
. Cambie la directiva de página de cada una de estas páginas de @page
a @page "{id:int}"
. Ejecute la aplicación y luego vea el origen.
El HTML generado agrega el identificador a la parte de la ruta de acceso de la dirección URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Una solicitud a la página con la plantilla de ruta {id:int}
que no incluya el entero devuelve un error HTTP 404 (no encontrado). Por ejemplo, https://localhost:5001/Movies/Details
devuelve un error 404. Para que el identificador sea opcional, anexe ?
a la restricción de ruta:
@page "{id:int?}"
Para probar el comportamiento de @page "{id:int?}"
:
- Establezca la directiva de página de
Pages/Movies/Details.cshtml
en@page "{id:int?}"
. - Establezca un punto de interrupción en
public async Task<IActionResult> OnGetAsync(int? id)
(enPages/Movies/Details.cshtml.cs
). - Navegue a
https://localhost:5001/Movies/Details/
.
Con la directiva @page "{id:int}"
, el punto de interrupción nunca se alcanza. El motor de enrutamiento devuelve HTTP 404. Con @page "{id:int?}"
, el método OnGetAsync
devuelve NotFound
(HTTP 404):
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
Revisión del control de excepciones de simultaneidad
Revise el método OnPostAsync
en el archivo Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
El código anterior detecta las excepciones de simultaneidad cuando un cliente elimina la película y el otro cliente publica cambios en ella.
Para probar el bloque catch
:
- establezca un punto de interrupción en
catch (DbUpdateConcurrencyException)
. - Seleccione Editar para una película y realice cambios, pero no seleccione Guardar.
- En otra ventana del explorador, seleccione el vínculo de eliminación de la misma película y luego elimínela.
- En la ventana anterior del explorador, publique los cambios en la película.
Es posible que el código de producción quiera detectar conflictos de simultaneidad. Vea Administración de conflictos de simultaneidad para más información.
Revisión de publicaciones y enlaces
Examine el archivo Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.Id == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.Id))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.Id == id);
}
Cuando se realiza una solicitud HTTP GET a la página Movies/Edit (por ejemplo, https://localhost:5001/Movies/Edit/3
):
- El método
OnGetAsync
obtiene la película en la base de datos y devuelve el métodoPage
. - El método
Page
representa la página dePages/Movies/Edit.cshtml
Razor. El archivoPages/Movies/Edit.cshtml
contiene la directiva de modelo (@model RazorPagesMovie.Pages.Movies.EditModel
), que hace que el modelo de película esté disponible en la página. - Se abre el formulario de edición con los valores de la película.
Cuando se publica la página Movies/Edit:
Los valores del formulario de la página se enlazan a la propiedad
Movie
. El atributo[BindProperty]
habilita el enlace de modelos.[BindProperty] public Movie Movie { get; set; }
Si hay errores en el estado del modelo (por ejemplo,
ReleaseDate
no se puede convertir en una fecha), el formulario se vuelve a mostrar con los valores enviados.Si no hay ningún error en el modelo, se guarda la película.
Los métodos HTTP GET de las páginas de Razor Index, Create y Delete siguen un patrón similar. El método OnPostAsync
HTTP POST de la página de Razor Create sigue un patrón similar al del método OnPostAsync
de la página de Razor Edit.
Pasos siguientes
La aplicación de películas con scaffolding pinta bien, pero la presentación no es ideal. ReleaseDate debe tener dos palabras: Release Date (Fecha lanzamiento).
Actualización del código generado
Actualice Models/Movie.cs
con el siguiente código resaltado:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; } = string.Empty;
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; } = string.Empty;
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
}
En el código anterior:
- La anotación de datos
[Column(TypeName = "decimal(18, 2)")]
permite que Entity Framework Core asigne correctamentePrice
a la moneda en la base de datos. Para más información, vea Tipos de datos. - El atributo [Display] especifica el nombre para mostrar de un campo. En el código anterior, "Release Date" en lugar de "ReleaseDate".
- El atributo [DataType] especifica el tipo de datos (
Date
). No se muestra la información de hora almacenada en el campo.
En el próximo tutorial, hablaremos de DataAnnotations.
Vaya a Pages/Movies y mantenga el mouse sobre un vínculo de edición para ver la dirección URL de destino.
Los vínculos de edición, detalles y eliminación se generan mediante el Asistente de etiquetas delimitadoras en el archivo Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Las aplicaciones auxiliares de etiquetas permiten que el código de servidor participe en la creación y la representación de elementos HTML en archivos de Razor.
En el código anterior, el asistente de etiquetas de delimitador genera de forma dinámica el valor del atributo href
HTML desde la página de Razor Pages (la ruta es relativa), el elemento asp-page
y el identificador de ruta (asp-route-id
). Vea Generación de direcciones URL para las páginas para obtener más información.
Use Ver código fuente en un explorador para examinar el marcado generado. A continuación se muestra una parte del HTML generado:
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Los vínculos generados de forma dinámica pasan el identificador de la película con una cadena de consulta. Por ejemplo, ?id=1
en https://localhost:5001/Movies/Details?id=1
.
Adición de la plantilla de ruta
Actualice las páginas de Razor Edit, Details y Delete para usar la plantilla de ruta {id:int}
. Cambie la directiva de página de cada una de estas páginas de @page
a @page "{id:int}"
. Ejecute la aplicación y luego vea el origen.
El HTML generado agrega el identificador a la parte de la ruta de acceso de la dirección URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Una solicitud a la página con la plantilla de ruta {id:int}
que no incluya el entero devolverá un error HTTP 404 (no encontrado). Por ejemplo, https://localhost:5001/Movies/Details
devolverá un error 404. Para que el identificador sea opcional, anexe ?
a la restricción de ruta:
@page "{id:int?}"
Para probar el comportamiento de @page "{id:int?}"
:
- Establezca la directiva de página de
Pages/Movies/Details.cshtml
en@page "{id:int?}"
. - Establezca un punto de interrupción en
public async Task<IActionResult> OnGetAsync(int? id)
(enPages/Movies/Details.cshtml.cs
). - Navegue a
https://localhost:5001/Movies/Details/
.
Con la directiva @page "{id:int}"
, el punto de interrupción nunca se alcanza. El motor de enrutamiento devuelve HTTP 404. Con @page "{id:int?}"
, el método OnGetAsync
devuelve NotFound
(HTTP 404):
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
Revisión del control de excepciones de simultaneidad
Revise el método OnPostAsync
en el archivo Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return (_context.Movie?.Any(e => e.ID == id)).GetValueOrDefault();
}
El código anterior detecta las excepciones de simultaneidad cuando un cliente elimina la película y el otro cliente publica cambios en ella. El código anterior no detecta los conflictos que se producen debido a que dos o más clientes editan la misma película simultáneamente. En este caso, las ediciones de varios clientes se aplican en el orden en que se llama a SaveChanges
y las ediciones que se aplican más adelante pueden sobrescribir las ediciones anteriores con valores obsoletos.
Para probar el bloque catch
:
- establezca un punto de interrupción en
catch (DbUpdateConcurrencyException)
. - Seleccione Editar para una película y realice cambios, pero no seleccione Guardar.
- En otra ventana del explorador, seleccione el vínculo de eliminación de la misma película y luego elimínela.
- En la ventana anterior del explorador, publique los cambios en la película.
Es posible que el código de producción quiera detectar conflictos de simultaneidad adicionales, como varios clientes que editan una entidad al mismo tiempo. Vea Administración de conflictos de simultaneidad para más información.
Revisión de publicaciones y enlaces
Examine el archivo Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; } = default!;
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null || _context.Movie == null)
{
return NotFound();
}
var movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (movie == null)
{
return NotFound();
}
Movie = movie;
return Page();
}
// To protect from overposting attacks, enable the specific properties you want to bind to.
// For more details, see https://aka.ms/RazorPagesCRUD.
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return (_context.Movie?.Any(e => e.ID == id)).GetValueOrDefault();
}
Cuando se realiza una solicitud HTTP GET a la página Movies/Edit (por ejemplo, https://localhost:5001/Movies/Edit/3
):
- El método
OnGetAsync
obtiene la película en la base de datos y devuelve el métodoPage
. - El método
Page
representa la página dePages/Movies/Edit.cshtml
Razor. El archivoPages/Movies/Edit.cshtml
contiene la directiva de modelo (@model RazorPagesMovie.Pages.Movies.EditModel
), que hace que el modelo de película esté disponible en la página. - Se abre el formulario de edición con los valores de la película.
Cuando se publica la página Movies/Edit:
Los valores del formulario de la página se enlazan a la propiedad
Movie
. El atributo[BindProperty]
habilita el enlace de modelos.[BindProperty] public Movie Movie { get; set; }
Si hay errores en el estado del modelo (por ejemplo,
ReleaseDate
no se puede convertir en una fecha), el formulario se vuelve a mostrar con los valores enviados.Si no hay ningún error en el modelo, se guarda la película.
Los métodos HTTP GET de las páginas de Razor Index, Create y Delete siguen un patrón similar. El método OnPostAsync
HTTP POST de la página de Razor Create sigue un patrón similar al del método OnPostAsync
de la página de Razor Edit.
Pasos siguientes
La aplicación de películas con scaffolding pinta bien, pero la presentación no es ideal. ReleaseDate debe tener dos palabras: Release Date (Fecha lanzamiento).
Actualización del código generado
Abra el archivo Models/Movie.cs
y agregue las líneas resaltadas mostradas en el código siguiente:
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace RazorPagesMovie.Models
{
public class Movie
{
public int ID { get; set; }
public string Title { get; set; }
[Display(Name = "Release Date")]
[DataType(DataType.Date)]
public DateTime ReleaseDate { get; set; }
public string Genre { get; set; }
[Column(TypeName = "decimal(18, 2)")]
public decimal Price { get; set; }
}
}
En el código anterior:
- La anotación de datos
[Column(TypeName = "decimal(18, 2)")]
permite que Entity Framework Core asigne correctamentePrice
a la moneda en la base de datos. Para más información, vea Tipos de datos. - El atributo [Display] especifica el nombre para mostrar de un campo. En el código anterior, "Release Date" en lugar de "ReleaseDate".
- El atributo [DataType] especifica el tipo de datos (
Date
). No se muestra la información de hora almacenada en el campo.
En el próximo tutorial, hablaremos de DataAnnotations.
Vaya a Pages/Movies y mantenga el mouse sobre un vínculo de edición para ver la dirección URL de destino.
Los vínculos de edición, detalles y eliminación se generan mediante el Asistente de etiquetas delimitadoras en el archivo Pages/Movies/Index.cshtml
.
@foreach (var item in Model.Movie) {
<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-page="./Edit" asp-route-id="@item.Id">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.Id">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.Id">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Las aplicaciones auxiliares de etiquetas permiten que el código de servidor participe en la creación y la representación de elementos HTML en archivos de Razor.
En el código anterior, el asistente de etiquetas de delimitador genera de forma dinámica el valor del atributo href
HTML desde la página de Razor Pages (la ruta es relativa), el elemento asp-page
y el identificador de ruta (asp-route-id
). Vea Generación de direcciones URL para las páginas para obtener más información.
Use Ver código fuente en un explorador para examinar el marcado generado. A continuación se muestra una parte del HTML generado:
<td>
<a href="/Movies/Edit?id=1">Edit</a> |
<a href="/Movies/Details?id=1">Details</a> |
<a href="/Movies/Delete?id=1">Delete</a>
</td>
Los vínculos generados de forma dinámica pasan el identificador de la película con una cadena de consulta. Por ejemplo, ?id=1
en https://localhost:5001/Movies/Details?id=1
.
Adición de la plantilla de ruta
Actualice las páginas de Razor Edit, Details y Delete para usar la plantilla de ruta {id:int}
. Cambie la directiva de página de cada una de estas páginas de @page
a @page "{id:int}"
. Ejecute la aplicación y luego vea el origen.
El HTML generado agrega el identificador a la parte de la ruta de acceso de la dirección URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Una solicitud a la página con la plantilla de ruta {id:int}
que no incluya el entero devolverá un error HTTP 404 (no encontrado). Por ejemplo, https://localhost:5001/Movies/Details
devolverá un error 404. Para que el identificador sea opcional, anexe ?
a la restricción de ruta:
@page "{id:int?}"
Para probar el comportamiento de @page "{id:int?}"
:
- Establezca la directiva de página de
Pages/Movies/Details.cshtml
en@page "{id:int?}"
. - Establezca un punto de interrupción en
public async Task<IActionResult> OnGetAsync(int? id)
(enPages/Movies/Details.cshtml.cs
). - Navegue a
https://localhost:5001/Movies/Details/
.
Con la directiva @page "{id:int}"
, el punto de interrupción nunca se alcanza. El motor de enrutamiento devuelve HTTP 404. Con @page "{id:int?}"
, el método OnGetAsync
devuelve NotFound
(HTTP 404):
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
Revisión del control de excepciones de simultaneidad
Revise el método OnPostAsync
en el archivo Pages/Movies/Edit.cshtml.cs
:
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.ID == id);
}
El código anterior detecta las excepciones de simultaneidad cuando un cliente elimina la película y el otro cliente publica cambios en ella.
Para probar el bloque catch
:
- establezca un punto de interrupción en
catch (DbUpdateConcurrencyException)
. - Seleccione Editar para una película y realice cambios, pero no seleccione Guardar.
- En otra ventana del explorador, seleccione el vínculo de eliminación de la misma película y luego elimínela.
- En la ventana anterior del explorador, publique los cambios en la película.
Es posible que el código de producción quiera detectar conflictos de simultaneidad. Vea Administración de conflictos de simultaneidad para más información.
Revisión de publicaciones y enlaces
Examine el archivo Pages/Movies/Edit.cshtml.cs
:
public class EditModel : PageModel
{
private readonly RazorPagesMovie.Data.RazorPagesMovieContext _context;
public EditModel(RazorPagesMovie.Data.RazorPagesMovieContext context)
{
_context = context;
}
[BindProperty]
public Movie Movie { get; set; }
public async Task<IActionResult> OnGetAsync(int? id)
{
if (id == null)
{
return NotFound();
}
Movie = await _context.Movie.FirstOrDefaultAsync(m => m.ID == id);
if (Movie == null)
{
return NotFound();
}
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Attach(Movie).State = EntityState.Modified;
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MovieExists(Movie.ID))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MovieExists(int id)
{
return _context.Movie.Any(e => e.ID == id);
}
Cuando se realiza una solicitud HTTP GET a la página Movies/Edit (por ejemplo, https://localhost:5001/Movies/Edit/3
):
- El método
OnGetAsync
obtiene la película en la base de datos y devuelve el métodoPage
. - El método
Page
representa la página dePages/Movies/Edit.cshtml
Razor. El archivoPages/Movies/Edit.cshtml
contiene la directiva de modelo (@model RazorPagesMovie.Pages.Movies.EditModel
), que hace que el modelo de película esté disponible en la página. - Se abre el formulario de edición con los valores de la película.
Cuando se publica la página Movies/Edit:
Los valores del formulario de la página se enlazan a la propiedad
Movie
. El atributo[BindProperty]
habilita el enlace de modelos.[BindProperty] public Movie Movie { get; set; }
Si hay errores en el estado del modelo (por ejemplo,
ReleaseDate
no se puede convertir en una fecha), el formulario se vuelve a mostrar con los valores enviados.Si no hay ningún error en el modelo, se guarda la película.
Los métodos HTTP GET de las páginas de Razor Index, Create y Delete siguen un patrón similar. El método OnPostAsync
HTTP POST de la página de Razor Create sigue un patrón similar al del método OnPostAsync
de la página de Razor Edit.