Parte 5, atualizar as páginas geradas em um aplicativo ASP.NET Core
Observação
Esta não é a versão mais recente deste artigo. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Aviso
Não há mais suporte para essa versão do ASP.NET Core. Para obter mais informações, confira .NET e a Política de Suporte do .NET Core. Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
Importante
Essas informações relacionam-se ao produto de pré-lançamento, que poderá ser substancialmente modificado antes do lançamento comercial. A Microsoft não oferece nenhuma garantia, explícita ou implícita, quanto às informações fornecidas aqui.
Para informações sobre a versão vigente, confira a Versão do .NET 8 deste artigo.
O aplicativo de filme gerado por scaffolding tem um bom começo, mas a apresentação não é ideal. ReleaseDate deve ser duas palavras, Data de Lançamento.
Atualizar o modelo
Atualize Models/Movie.cs
com o seguinte código realçado:
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; }
}
No código anterior:
- A anotação de dados
[Column(TypeName = "decimal(18, 2)")]
permite que o Entity Framework Core mapeie oPrice
corretamente para a moeda no banco de dados. Para obter mais informações, veja Tipos de Dados. - O atributo [Display] especifica o nome de exibição de um campo. No código anterior,
Release Date
em vez deReleaseDate
. - O atributo [DataType] especifica o tipo de dados (
Date
). As informações de hora armazenadas no campo não são exibidas.
DataAnnotations é abordado no próximo tutorial.
Navegue até Páginas/Filmes e passe o mouse sobre um link Editar para ver a URL de destino.
Os links Editar, Detalhes e Excluir são gerados pelo Auxiliar de Marcação de Âncora no arquivo 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>
Os Auxiliares de Marcação permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos do Razor.
No código anterior, o Auxiliar de Marcação de Âncora gera dinamicamente o valor do atributo href
do HTML da Página do Razor (a rota é relativa), o asp-page
e o identificador de rota (asp-route-id
). Para obter mais informações, confira Geração de URL para Páginas.
Use Exibir Fonte em seu navegador favorito para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:
<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>
Os links gerados dinamicamente passam a ID de filme com uma cadeia de caracteres de consulta. Por exemplo, o trecho ?id=1
em https://localhost:5001/Movies/Details?id=1
.
Adicionar modelo de rota
Atualize as Páginas Editar, Detalhes e Excluir do Razor para que elas usem o modelo de rota {id:int}
. Altere a diretiva de página de cada uma dessas páginas de @page
para @page "{id:int}"
. Execute o aplicativo e, em seguida, exiba o código-fonte.
O HTML gerado adiciona a ID à parte do caminho da URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Uma solicitação para a página com o modelo de rota {id:int}
que não inclui o inteiro retornará um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details
retorna um erro 404. Para tornar a ID opcional, acrescente ?
à restrição de rota:
@page "{id:int?}"
Teste o comportamento de @page "{id:int?}"
:
- Defina a diretiva de página em
Pages/Movies/Details.cshtml
como@page "{id:int?}"
. - Defina um ponto de interrupção em
public async Task<IActionResult> OnGetAsync(int? id)
, emPages/Movies/Details.cshtml.cs
. - Navegue até
https://localhost:5001/Movies/Details/
.
Com a diretiva @page "{id:int}"
, o ponto de interrupção nunca é atingido. O mecanismo de roteamento retorna HTTP 404. Usando @page "{id:int?}"
, o método OnGetAsync
retornará 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();
}
Examinar o tratamento de exceção de simultaneidade
Examine o método OnPostAsync
no arquivo 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);
}
O código anterior detecta exceções de simultaneidade quando um cliente exclui o filme e o outro cliente posta alterações no filme.
Para testar o bloco catch
:
- Defina um ponto de interrupção em
catch (DbUpdateConcurrencyException)
- Selecione Editar para um filme, faça alterações, mas não insira Salvar.
- Em outra janela do navegador, selecione o link Excluir do mesmo filme e, em seguida, exclua o filme.
- Na janela do navegador anterior, poste as alterações no filme.
O código de produção talvez deseje detectar conflitos de simultaneidade. Confira Lidar com conflitos de simultaneidade para obter mais informações.
Análise de postagem e associação
Examine o arquivo 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);
}
Quando uma solicitação HTTP GET for feita para a página Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3
:
- O método
OnGetAsync
busca o filme do banco de dados e retorna o métodoPage
. - O método
Page
renderiza a Página doPages/Movies/Edit.cshtml
Razor. O arquivoPages/Movies/Edit.cshtml
contém a diretiva de modelo@model RazorPagesMovie.Pages.Movies.EditModel
, que disponibiliza o modelo de filme na página. - O formulário Editar é exibido com os valores do filme.
Quando a página Movies/Edit é postada:
Os valores de formulário na página são associados à propriedade
Movie
. O atributo[BindProperty]
habilita o Model binding.[BindProperty] public Movie Movie { get; set; }
Se houver erros no estado do modelo (por exemplo,
ReleaseDate
não pode ser convertido em uma data), o formulário é exibido novamente com os valores enviados.Se não houver erros do modelo, o filme será salvo.
Os métodos HTTP GET nas páginas Índice, Criar e Excluir do Razor seguem um padrão semelhante. O método OnPostAsync
do HTTP POST na página Criar do Razor segue um padrão semelhante ao método OnPostAsync
na página Editar do Razor.
Próximas etapas
O aplicativo de filme gerado por scaffolding tem um bom começo, mas a apresentação não é ideal. ReleaseDate deve ser duas palavras, Data de Lançamento.
Atualizar o modelo
Atualize Models/Movie.cs
com o seguinte código realçado:
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; }
}
No código anterior:
- A anotação de dados
[Column(TypeName = "decimal(18, 2)")]
permite que o Entity Framework Core mapeie oPrice
corretamente para a moeda no banco de dados. Para obter mais informações, veja Tipos de Dados. - O atributo [Display] especifica o nome de exibição de um campo. No código anterior,
Release Date
em vez deReleaseDate
. - O atributo [DataType] especifica o tipo de dados (
Date
). As informações de hora armazenadas no campo não são exibidas.
DataAnnotations é abordado no próximo tutorial.
Navegue até Páginas/Filmes e passe o mouse sobre um link Editar para ver a URL de destino.
Os links Editar, Detalhes e Excluir são gerados pelo Auxiliar de Marcação de Âncora no arquivo 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>
Os Auxiliares de Marcação permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos do Razor.
No código anterior, o Auxiliar de Marcação de Âncora gera dinamicamente o valor do atributo href
do HTML da Página do Razor (a rota é relativa), o asp-page
e o identificador de rota (asp-route-id
). Para obter mais informações, confira Geração de URL para Páginas.
Use Exibir Fonte em seu navegador favorito para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:
<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>
Os links gerados dinamicamente passam a ID de filme com uma cadeia de caracteres de consulta. Por exemplo, o trecho ?id=1
em https://localhost:5001/Movies/Details?id=1
.
Adicionar modelo de rota
Atualize as Páginas Editar, Detalhes e Excluir do Razor para que elas usem o modelo de rota {id:int}
. Altere a diretiva de página de cada uma dessas páginas de @page
para @page "{id:int}"
. Execute o aplicativo e, em seguida, exiba o código-fonte.
O HTML gerado adiciona a ID à parte do caminho da URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Uma solicitação para a página com o modelo de rota {id:int}
que não inclui o inteiro retornará um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details
retorna um erro 404. Para tornar a ID opcional, acrescente ?
à restrição de rota:
@page "{id:int?}"
Teste o comportamento de @page "{id:int?}"
:
- Defina a diretiva de página em
Pages/Movies/Details.cshtml
como@page "{id:int?}"
. - Defina um ponto de interrupção em
public async Task<IActionResult> OnGetAsync(int? id)
, emPages/Movies/Details.cshtml.cs
. - Navegue até
https://localhost:5001/Movies/Details/
.
Com a diretiva @page "{id:int}"
, o ponto de interrupção nunca é atingido. O mecanismo de roteamento retorna HTTP 404. Usando @page "{id:int?}"
, o método OnGetAsync
retornará 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();
}
Examinar o tratamento de exceção de simultaneidade
Examine o método OnPostAsync
no arquivo 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);
}
O código anterior detecta exceções de simultaneidade quando um cliente exclui o filme e o outro cliente posta alterações no filme.
Para testar o bloco catch
:
- Defina um ponto de interrupção em
catch (DbUpdateConcurrencyException)
- Selecione Editar para um filme, faça alterações, mas não insira Salvar.
- Em outra janela do navegador, selecione o link Excluir do mesmo filme e, em seguida, exclua o filme.
- Na janela do navegador anterior, poste as alterações no filme.
O código de produção talvez deseje detectar conflitos de simultaneidade. Confira Lidar com conflitos de simultaneidade para obter mais informações.
Análise de postagem e associação
Examine o arquivo 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);
}
Quando uma solicitação HTTP GET for feita para a página Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3
:
- O método
OnGetAsync
busca o filme do banco de dados e retorna o métodoPage
. - O método
Page
renderiza a Página doPages/Movies/Edit.cshtml
Razor. O arquivoPages/Movies/Edit.cshtml
contém a diretiva de modelo@model RazorPagesMovie.Pages.Movies.EditModel
, que disponibiliza o modelo de filme na página. - O formulário Editar é exibido com os valores do filme.
Quando a página Movies/Edit é postada:
Os valores de formulário na página são associados à propriedade
Movie
. O atributo[BindProperty]
habilita o Model binding.[BindProperty] public Movie Movie { get; set; }
Se houver erros no estado do modelo (por exemplo,
ReleaseDate
não pode ser convertido em uma data), o formulário é exibido novamente com os valores enviados.Se não houver erros do modelo, o filme será salvo.
Os métodos HTTP GET nas páginas Índice, Criar e Excluir do Razor seguem um padrão semelhante. O método OnPostAsync
do HTTP POST na página Criar do Razor segue um padrão semelhante ao método OnPostAsync
na página Editar do Razor.
Próximas etapas
O aplicativo de filme gerado por scaffolding tem um bom começo, mas a apresentação não é ideal. ReleaseDate deve ser duas palavras, Data de Lançamento.
Atualizar o modelo
Atualize Models/Movie.cs
com o seguinte código realçado:
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; }
}
No código anterior:
- A anotação de dados
[Column(TypeName = "decimal(18, 2)")]
permite que o Entity Framework Core mapeie oPrice
corretamente para a moeda no banco de dados. Para obter mais informações, veja Tipos de Dados. - O atributo [Display] especifica o nome de exibição de um campo. No código anterior,
Release Date
em vez deReleaseDate
. - O atributo [DataType] especifica o tipo de dados (
Date
). As informações de hora armazenadas no campo não são exibidas.
DataAnnotations é abordado no próximo tutorial.
Navegue até Páginas/Filmes e passe o mouse sobre um link Editar para ver a URL de destino.
Os links Editar, Detalhes e Excluir são gerados pelo Auxiliar de Marcação de Âncora no arquivo 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>
Os Auxiliares de Marcação permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos do Razor.
No código anterior, o Auxiliar de Marcação de Âncora gera dinamicamente o valor do atributo href
do HTML da Página do Razor (a rota é relativa), o asp-page
e o identificador de rota (asp-route-id
). Para obter mais informações, confira Geração de URL para Páginas.
Use Exibir Fonte em seu navegador favorito para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:
<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>
Os links gerados dinamicamente passam a ID de filme com uma cadeia de caracteres de consulta. Por exemplo, o trecho ?id=1
em https://localhost:5001/Movies/Details?id=1
.
Adicionar modelo de rota
Atualize as Páginas Editar, Detalhes e Excluir do Razor para que elas usem o modelo de rota {id:int}
. Altere a diretiva de página de cada uma dessas páginas de @page
para @page "{id:int}"
. Execute o aplicativo e, em seguida, exiba o código-fonte.
O HTML gerado adiciona a ID à parte do caminho da URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Uma solicitação para a página com o modelo de rota {id:int}
que não inclui o inteiro retornará um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details
retorna um erro 404. Para tornar a ID opcional, acrescente ?
à restrição de rota:
@page "{id:int?}"
Teste o comportamento de @page "{id:int?}"
:
- Defina a diretiva de página em
Pages/Movies/Details.cshtml
como@page "{id:int?}"
. - Defina um ponto de interrupção em
public async Task<IActionResult> OnGetAsync(int? id)
, emPages/Movies/Details.cshtml.cs
. - Navegue até
https://localhost:5001/Movies/Details/
.
Com a diretiva @page "{id:int}"
, o ponto de interrupção nunca é atingido. O mecanismo de roteamento retorna HTTP 404. Usando @page "{id:int?}"
, o método OnGetAsync
retornará 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();
}
Examinar o tratamento de exceção de simultaneidade
Examine o método OnPostAsync
no arquivo 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);
}
O código anterior detecta exceções de simultaneidade quando um cliente exclui o filme e o outro cliente posta alterações no filme.
Para testar o bloco catch
:
- Defina um ponto de interrupção em
catch (DbUpdateConcurrencyException)
- Selecione Editar para um filme, faça alterações, mas não insira Salvar.
- Em outra janela do navegador, selecione o link Excluir do mesmo filme e, em seguida, exclua o filme.
- Na janela do navegador anterior, poste as alterações no filme.
O código de produção talvez deseje detectar conflitos de simultaneidade. Confira Lidar com conflitos de simultaneidade para obter mais informações.
Análise de postagem e associação
Examine o arquivo 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);
}
Quando uma solicitação HTTP GET for feita para a página Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3
:
- O método
OnGetAsync
busca o filme do banco de dados e retorna o métodoPage
. - O método
Page
renderiza a Página doPages/Movies/Edit.cshtml
Razor. O arquivoPages/Movies/Edit.cshtml
contém a diretiva de modelo@model RazorPagesMovie.Pages.Movies.EditModel
, que disponibiliza o modelo de filme na página. - O formulário Editar é exibido com os valores do filme.
Quando a página Movies/Edit é postada:
Os valores de formulário na página são associados à propriedade
Movie
. O atributo[BindProperty]
habilita o Model binding.[BindProperty] public Movie Movie { get; set; }
Se houver erros no estado do modelo (por exemplo,
ReleaseDate
não pode ser convertido em uma data), o formulário é exibido novamente com os valores enviados.Se não houver erros do modelo, o filme será salvo.
Os métodos HTTP GET nas páginas Índice, Criar e Excluir do Razor seguem um padrão semelhante. O método OnPostAsync
do HTTP POST na página Criar do Razor segue um padrão semelhante ao método OnPostAsync
na página Editar do Razor.
Próximas etapas
O aplicativo de filme gerado por scaffolding tem um bom começo, mas a apresentação não é ideal. ReleaseDate deve ser duas palavras, Data de Lançamento.
Atualizar o código gerado
Atualize Models/Movie.cs
com o seguinte código realçado:
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; }
}
}
No código anterior:
- A anotação de dados
[Column(TypeName = "decimal(18, 2)")]
permite que o Entity Framework Core mapeie oPrice
corretamente para a moeda no banco de dados. Para obter mais informações, veja Tipos de Dados. - O atributo [Display] especifica o nome de exibição de um campo. No código anterior, "Data de Lançamento" em vez de "ReleaseDate".
- O atributo [DataType] especifica o tipo de dados (
Date
). As informações de hora armazenadas no campo não são exibidas.
DataAnnotations é abordado no próximo tutorial.
Navegue até Páginas/Filmes e passe o mouse sobre um link Editar para ver a URL de destino.
Os links Editar, Detalhes e Excluir são gerados pelo Auxiliar de Marcação de Âncora no arquivo 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>
Os Auxiliares de Marcação permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos do Razor.
No código anterior, o Auxiliar de Marcação de Âncora gera dinamicamente o valor do atributo href
do HTML da Página do Razor (a rota é relativa), o asp-page
e o identificador de rota (asp-route-id
). Para obter mais informações, confira Geração de URL para Páginas.
Use Exibir Fonte em seu navegador favorito para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:
<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>
Os links gerados dinamicamente passam a ID de filme com uma cadeia de caracteres de consulta. Por exemplo, o trecho ?id=1
em https://localhost:5001/Movies/Details?id=1
.
Adicionar modelo de rota
Atualize as Páginas Editar, Detalhes e Excluir do Razor para que elas usem o modelo de rota {id:int}
. Altere a diretiva de página de cada uma dessas páginas de @page
para @page "{id:int}"
. Execute o aplicativo e, em seguida, exiba o código-fonte.
O HTML gerado adiciona a ID à parte do caminho da URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Uma solicitação para a página com o modelo de rota {id:int}
que não inclui o inteiro retornará um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details
retornará um erro 404. Para tornar a ID opcional, acrescente ?
à restrição de rota:
@page "{id:int?}"
Teste o comportamento de @page "{id:int?}"
:
- Defina a diretiva de página em
Pages/Movies/Details.cshtml
como@page "{id:int?}"
. - Defina um ponto de interrupção em
public async Task<IActionResult> OnGetAsync(int? id)
, emPages/Movies/Details.cshtml.cs
. - Navegue até
https://localhost:5001/Movies/Details/
.
Com a diretiva @page "{id:int}"
, o ponto de interrupção nunca é atingido. O mecanismo de roteamento retorna HTTP 404. Usando @page "{id:int?}"
, o método OnGetAsync
retornará 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();
}
Examinar o tratamento de exceção de simultaneidade
Examine o método OnPostAsync
no arquivo 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();
}
O código anterior detecta exceções de simultaneidade quando um cliente exclui o filme e o outro cliente posta alterações no filme. O código anterior não detecta os conflitos que ocorrem devido a dois ou mais clientes estarem editando o mesmo filme simultaneamente. Nesse caso, as edições de vários clientes são aplicadas na ordem em que SaveChanges
é chamada, e as edições aplicadas posteriormente podem substituir edições anteriores por valores obsoletos.
Para testar o bloco catch
:
- Defina um ponto de interrupção em
catch (DbUpdateConcurrencyException)
- Selecione Editar para um filme, faça alterações, mas não insira Salvar.
- Em outra janela do navegador, selecione o link Excluir do mesmo filme e, em seguida, exclua o filme.
- Na janela do navegador anterior, poste as alterações no filme.
Talvez o código de produção deseje detectar conflitos de simultaneidade adicionais, como vários clientes editando uma entidade ao mesmo tempo. Confira Lidar com conflitos de simultaneidade para obter mais informações.
Análise de postagem e associação
Examine o arquivo 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();
}
Quando uma solicitação HTTP GET for feita para a página Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3
:
- O método
OnGetAsync
busca o filme do banco de dados e retorna o métodoPage
. - O método
Page
renderiza a Página doPages/Movies/Edit.cshtml
Razor. O arquivoPages/Movies/Edit.cshtml
contém a diretiva de modelo@model RazorPagesMovie.Pages.Movies.EditModel
, que disponibiliza o modelo de filme na página. - O formulário Editar é exibido com os valores do filme.
Quando a página Movies/Edit é postada:
Os valores de formulário na página são associados à propriedade
Movie
. O atributo[BindProperty]
habilita o Model binding.[BindProperty] public Movie Movie { get; set; }
Se houver erros no estado do modelo (por exemplo,
ReleaseDate
não pode ser convertido em uma data), o formulário é exibido novamente com os valores enviados.Se não houver erros do modelo, o filme será salvo.
Os métodos HTTP GET nas páginas Índice, Criar e Excluir do Razor seguem um padrão semelhante. O método OnPostAsync
do HTTP POST na página Criar do Razor segue um padrão semelhante ao método OnPostAsync
na página Editar do Razor.
Próximas etapas
O aplicativo de filme gerado por scaffolding tem um bom começo, mas a apresentação não é ideal. ReleaseDate deve ser duas palavras, Data de Lançamento.
Atualizar o código gerado
Abra o arquivo Models/Movie.cs
e adicione as linhas realçadas mostradas no seguinte código:
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; }
}
}
No código anterior:
- A anotação de dados
[Column(TypeName = "decimal(18, 2)")]
permite que o Entity Framework Core mapeie oPrice
corretamente para a moeda no banco de dados. Para obter mais informações, veja Tipos de Dados. - O atributo [Display] especifica o nome de exibição de um campo. No código anterior, "Data de Lançamento" em vez de "ReleaseDate".
- O atributo [DataType] especifica o tipo de dados (
Date
). As informações de hora armazenadas no campo não são exibidas.
DataAnnotations é abordado no próximo tutorial.
Navegue até Páginas/Filmes e passe o mouse sobre um link Editar para ver a URL de destino.
Os links Editar, Detalhes e Excluir são gerados pelo Auxiliar de Marcação de Âncora no arquivo 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>
Os Auxiliares de Marcação permitem que o código do servidor participe da criação e renderização de elementos HTML em arquivos do Razor.
No código anterior, o Auxiliar de Marcação de Âncora gera dinamicamente o valor do atributo href
do HTML da Página do Razor (a rota é relativa), o asp-page
e o identificador de rota (asp-route-id
). Para obter mais informações, confira Geração de URL para Páginas.
Use Exibir Fonte em seu navegador favorito para examinar a marcação gerada. Uma parte do HTML gerado é mostrada abaixo:
<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>
Os links gerados dinamicamente passam a ID de filme com uma cadeia de caracteres de consulta. Por exemplo, o trecho ?id=1
em https://localhost:5001/Movies/Details?id=1
.
Adicionar modelo de rota
Atualize as Páginas Editar, Detalhes e Excluir do Razor para que elas usem o modelo de rota {id:int}
. Altere a diretiva de página de cada uma dessas páginas de @page
para @page "{id:int}"
. Execute o aplicativo e, em seguida, exiba o código-fonte.
O HTML gerado adiciona a ID à parte do caminho da URL:
<td>
<a href="/Movies/Edit/1">Edit</a> |
<a href="/Movies/Details/1">Details</a> |
<a href="/Movies/Delete/1">Delete</a>
</td>
Uma solicitação para a página com o modelo de rota {id:int}
que não inclui o inteiro retornará um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details
retornará um erro 404. Para tornar a ID opcional, acrescente ?
à restrição de rota:
@page "{id:int?}"
Teste o comportamento de @page "{id:int?}"
:
- Defina a diretiva de página em
Pages/Movies/Details.cshtml
como@page "{id:int?}"
. - Defina um ponto de interrupção em
public async Task<IActionResult> OnGetAsync(int? id)
, emPages/Movies/Details.cshtml.cs
. - Navegue até
https://localhost:5001/Movies/Details/
.
Com a diretiva @page "{id:int}"
, o ponto de interrupção nunca é atingido. O mecanismo de roteamento retorna HTTP 404. Usando @page "{id:int?}"
, o método OnGetAsync
retornará 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();
}
Examinar o tratamento de exceção de simultaneidade
Examine o método OnPostAsync
no arquivo 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);
}
O código anterior detecta exceções de simultaneidade quando um cliente exclui o filme e o outro cliente posta alterações no filme.
Para testar o bloco catch
:
- Defina um ponto de interrupção em
catch (DbUpdateConcurrencyException)
- Selecione Editar para um filme, faça alterações, mas não insira Salvar.
- Em outra janela do navegador, selecione o link Excluir do mesmo filme e, em seguida, exclua o filme.
- Na janela do navegador anterior, poste as alterações no filme.
O código de produção talvez deseje detectar conflitos de simultaneidade. Confira Lidar com conflitos de simultaneidade para obter mais informações.
Análise de postagem e associação
Examine o arquivo 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);
}
Quando uma solicitação HTTP GET for feita para a página Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3
:
- O método
OnGetAsync
busca o filme do banco de dados e retorna o métodoPage
. - O método
Page
renderiza a Página doPages/Movies/Edit.cshtml
Razor. O arquivoPages/Movies/Edit.cshtml
contém a diretiva de modelo@model RazorPagesMovie.Pages.Movies.EditModel
, que disponibiliza o modelo de filme na página. - O formulário Editar é exibido com os valores do filme.
Quando a página Movies/Edit é postada:
Os valores de formulário na página são associados à propriedade
Movie
. O atributo[BindProperty]
habilita o Model binding.[BindProperty] public Movie Movie { get; set; }
Se houver erros no estado do modelo (por exemplo,
ReleaseDate
não pode ser convertido em uma data), o formulário é exibido novamente com os valores enviados.Se não houver erros do modelo, o filme será salvo.
Os métodos HTTP GET nas páginas Índice, Criar e Excluir do Razor seguem um padrão semelhante. O método OnPostAsync
do HTTP POST na página Criar do Razor segue um padrão semelhante ao método OnPostAsync
na página Editar do Razor.