Partilhar via


Parte 5, atualizar as páginas geradas numa aplicação ASP.NET Core

Observação

Esta não é a versão mais recente deste artigo. Para a versão atual, consulte a versão .NET 10 deste artigo.

Advertência

Esta versão do ASP.NET Core não é mais suportada. Para obter mais informações, consulte a Política de suporte do .NET e do .NET Core. Para a versão atual, consulte a versão .NET 10 deste artigo.

A aplicação de filmes com andaime tem um bom início, mas a apresentação não é ideal. Data de Lançamento deve ser duas palavras: Data de Lançamento.

Aplicação de filmes aberta no Chrome

Atualizar o modelo

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

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 [Column(TypeName = "decimal(18, 2)")] anotação de dados permite que o Entity Framework Core mapeie Price corretamente para a moeda na base de dados. Para obter mais informações, consulte Tipos de dados.
  • O atributo [Display] especifica o nome de exibição de um campo. No código anterior, Release Date em vez de ReleaseDate.
  • O atributo [DataType] especifica o tipo dos dados (Date). A informação de hora armazenada no campo não é apresentada.

O DataAnnotations é abordado no próximo tutorial.

Navegue até Páginas/Filmes e passe o rato sobre um link de Editar para ver o URL alvo.

Janela do navegador com o rato sobre o link Editar e é mostrada uma URL do link https://localhost:1234/Movies/Edit/5

Os links Editar, Detalhes e Eliminar são gerados pelo Anchor Tag Helper no Pages/Movies/Index.cshtml ficheiro.

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

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor.

No código anterior, o Anchor Tag Helper gera dinamicamente o valor do atributo HTML href a partir da Razor Página (a rota é relativa), do asp-page, e do identificador da rota (asp-route-id). Para mais informações, consulte geração de URL para Páginas.

Utilize Ver Fonte no navegador 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>

As ligações geradas dinamicamente passam o ID do filme com uma string de consulta. Por exemplo, o ?id=1 em https://localhost:5001/Movies/Details?id=1.

Adicionar modelo de rota

Atualize as páginas de Edição, Detalhes e Eliminação Razor para utilizar o modelo de rota {id:int}. Mude a diretiva de página para cada uma destas páginas de @page para @page "{id:int}". Abre a aplicação e depois vê a fonte.

O HTML gerado acrescenta o ID à secção do caminho do URL.

<td>
  <a href="/Movies/Edit/1">Edit</a> |
  <a href="/Movies/Details/1">Details</a> |
  <a href="/Movies/Delete/1">Delete</a>
</td>

Um pedido à página com a rota template {id:int} que não inclui o parâmetro inteiro retorna um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details devolve um erro 404. Para tornar o ID opcional, acrescente ? à restrição de rota:

@page "{id:int?}"

Testar o comportamento de @page "{id:int?}":

  1. Defina a diretiva de página em Pages/Movies/Details.cshtml para @page "{id:int?}".
  2. Defina um ponto de rutura em public async Task<IActionResult> OnGetAsync(int? id), em Pages/Movies/Details.cshtml.cs.
  3. Navegue até https://localhost:5001/Movies/Details/.

Com a @page "{id:int}" diretiva, o ponto de ruptura nunca é atingido. O motor de encaminhamento devolve HTTP 404. Usando @page "{id:int?}", o OnGetAsync método retorna 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();
}

Revisão do tratamento das exceções de concorrência

Revise o OnPostAsync método no Pages/Movies/Edit.cshtml.cs ficheiro:

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 deteta exceções de concorrência quando um cliente apaga o filme e o outro cliente publica alterações ao filme.

Para testar o bloco catch:

  1. Defina um ponto de quebra em catch (DbUpdateConcurrencyException).
  2. Selecione Editar para um filme, faça alterações, mas não selecione Guardar.
  3. Noutra janela do navegador, selecione o link Apagar para o mesmo filme e depois apague o filme.
  4. Na janela anterior do navegador, publique alterações ao filme.

O código de produção pode querer detetar conflitos de concorrência. Consulte Lidar com conflitos de concorrência para mais informações.

Publicação e revisão vinculativa

Veja o Pages/Movies/Edit.cshtml.cs ficheiro:

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

Quando um pedido HTTP GET é feito na página de Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3:

  • O OnGetAsync método recolhe o filme da base de dados e devolve o Page método.
  • O Page método renderiza a Pages/Movies/Edit.cshtmlRazor Página. O Pages/Movies/Edit.cshtml ficheiro contém a diretiva @model RazorPagesMovie.Pages.Movies.EditModel do modelo, que torna o modelo do filme disponível na página.
  • O formulário de Editar é exibido com os valores do filme.

Quando a página de Filmes/Editar é publicada:

  • Os valores do formulário na página estão ligados à Movie propriedade. O [BindProperty] atributo permite a ligação por modelo.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se houver erros no estado do modelo, por exemplo, se ReleaseDate não puder ser convertido em uma data, o formulário é reapresentado com os valores introduzidos.

  • Se não houver erros de modelo, o filme é guardado.

Os métodos HTTP GET nas páginas Index, Create e Delete Razor seguem um padrão semelhante. O método HTTP POST OnPostAsync na Página de Criação Razor segue um padrão semelhante ao método OnPostAsync na Página de Edição Razor.

Próximos passos

A aplicação de filmes com andaime tem um bom início, mas a apresentação não é ideal. Data de Lançamento deve ser duas palavras: Data de Lançamento.

Aplicação de filmes aberta no Chrome

Atualizar o modelo

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

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 [Column(TypeName = "decimal(18, 2)")] anotação de dados permite que o Entity Framework Core mapeie Price corretamente para a moeda na base de dados. Para obter mais informações, consulte Tipos de dados.
  • O atributo [Display] especifica o nome de exibição de um campo. No código anterior, Release Date em vez de ReleaseDate.
  • O atributo [DataType] especifica o tipo dos dados (Date). A informação de hora armazenada no campo não é apresentada.

O DataAnnotations é abordado no próximo tutorial.

Navegue até Páginas/Filmes e passe o rato sobre um link de Editar para ver o URL alvo.

Janela do navegador com o rato sobre o link Editar e é mostrada uma URL do link https://localhost:1234/Movies/Edit/5

Os links Editar, Detalhes e Eliminar são gerados pelo Anchor Tag Helper no Pages/Movies/Index.cshtml ficheiro.

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

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor.

No código anterior, o Anchor Tag Helper gera dinamicamente o valor do atributo HTML href a partir da Razor Página (a rota é relativa), do asp-page, e do identificador da rota (asp-route-id). Para mais informações, consulte geração de URL para Páginas.

Utilize Ver Fonte no navegador 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>

As ligações geradas dinamicamente passam o ID do filme com uma string de consulta. Por exemplo, o ?id=1 em https://localhost:5001/Movies/Details?id=1.

Adicionar modelo de rota

Atualize as páginas de Edição, Detalhes e Eliminação Razor para utilizar o modelo de rota {id:int}. Mude a diretiva de página para cada uma destas páginas de @page para @page "{id:int}". Abre a aplicação e depois vê a fonte.

O HTML gerado acrescenta o ID à secção do caminho do URL.

<td>
  <a href="/Movies/Edit/1">Edit</a> |
  <a href="/Movies/Details/1">Details</a> |
  <a href="/Movies/Delete/1">Delete</a>
</td>

Um pedido à página com a rota template {id:int} que não inclui o parâmetro inteiro retorna um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details devolve um erro 404. Para tornar o ID opcional, acrescente ? à restrição de rota:

@page "{id:int?}"

Testar o comportamento de @page "{id:int?}":

  1. Defina a diretiva de página em Pages/Movies/Details.cshtml para @page "{id:int?}".
  2. Defina um ponto de rutura em public async Task<IActionResult> OnGetAsync(int? id), em Pages/Movies/Details.cshtml.cs.
  3. Navegue até https://localhost:5001/Movies/Details/.

Com a @page "{id:int}" diretiva, o ponto de ruptura nunca é atingido. O motor de encaminhamento devolve HTTP 404. Usando @page "{id:int?}", o OnGetAsync método retorna 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();
}

Revisão do tratamento das exceções de concorrência

Revise o OnPostAsync método no Pages/Movies/Edit.cshtml.cs ficheiro:

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 deteta exceções de concorrência quando um cliente apaga o filme e o outro cliente publica alterações ao filme.

Para testar o bloco catch:

  1. Defina um ponto de quebra em catch (DbUpdateConcurrencyException).
  2. Selecione Editar para um filme, faça alterações, mas não selecione Guardar.
  3. Noutra janela do navegador, selecione o link Apagar para o mesmo filme e depois apague o filme.
  4. Na janela anterior do navegador, publique alterações ao filme.

O código de produção pode querer detetar conflitos de concorrência. Consulte Lidar com conflitos de concorrência para mais informações.

Publicação e revisão vinculativa

Veja o Pages/Movies/Edit.cshtml.cs ficheiro:

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 um pedido HTTP GET é feito na página de Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3:

  • O OnGetAsync método recolhe o filme da base de dados e devolve o Page método.
  • O Page método renderiza a Pages/Movies/Edit.cshtmlRazor Página. O Pages/Movies/Edit.cshtml ficheiro contém a diretiva @model RazorPagesMovie.Pages.Movies.EditModel do modelo, que torna o modelo do filme disponível na página.
  • O formulário de Editar é exibido com os valores do filme.

Quando a página de Filmes/Editar é publicada:

  • Os valores do formulário na página estão ligados à Movie propriedade. O [BindProperty] atributo permite a ligação por modelo.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se houver erros no estado do modelo, por exemplo, se ReleaseDate não puder ser convertido em uma data, o formulário é reapresentado com os valores introduzidos.

  • Se não houver erros de modelo, o filme é guardado.

Os métodos HTTP GET nas páginas Index, Create e Delete Razor seguem um padrão semelhante. O método HTTP POST OnPostAsync na Página de Criação Razor segue um padrão semelhante ao método OnPostAsync na Página de Edição Razor.

Próximos passos

A aplicação de filmes com andaime tem um bom início, mas a apresentação não é ideal. Data de Lançamento deve ser duas palavras: Data de Lançamento.

Aplicação de filmes aberta no Chrome

Atualizar o modelo

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

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 [Column(TypeName = "decimal(18, 2)")] anotação de dados permite que o Entity Framework Core mapeie Price corretamente para a moeda na base de dados. Para obter mais informações, consulte Tipos de dados.
  • O atributo [Display] especifica o nome de exibição de um campo. No código anterior, Release Date em vez de ReleaseDate.
  • O atributo [DataType] especifica o tipo dos dados (Date). A informação de hora armazenada no campo não é apresentada.

O DataAnnotations é abordado no próximo tutorial.

Navegue até Páginas/Filmes e passe o rato sobre um link de Editar para ver o URL alvo.

Janela do navegador com o rato sobre o link Editar e é mostrada uma URL do link https://localhost:1234/Movies/Edit/5

Os links Editar, Detalhes e Eliminar são gerados pelo Anchor Tag Helper no Pages/Movies/Index.cshtml ficheiro.

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

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor.

No código anterior, o Anchor Tag Helper gera dinamicamente o valor do atributo HTML href a partir da Razor Página (a rota é relativa), do asp-page, e do identificador da rota (asp-route-id). Para mais informações, consulte geração de URL para Páginas.

Utilize Ver Fonte no navegador 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>

As ligações geradas dinamicamente passam o ID do filme com uma string de consulta. Por exemplo, o ?id=1 em https://localhost:5001/Movies/Details?id=1.

Adicionar modelo de rota

Atualize as páginas de Edição, Detalhes e Eliminação Razor para utilizar o modelo de rota {id:int}. Mude a diretiva de página para cada uma destas páginas de @page para @page "{id:int}". Abre a aplicação e depois vê a fonte.

O HTML gerado acrescenta o ID à secção do caminho do URL.

<td>
  <a href="/Movies/Edit/1">Edit</a> |
  <a href="/Movies/Details/1">Details</a> |
  <a href="/Movies/Delete/1">Delete</a>
</td>

Um pedido à página com a rota template {id:int} que não inclui o parâmetro inteiro retorna um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details devolve um erro 404. Para tornar o ID opcional, acrescente ? à restrição de rota:

@page "{id:int?}"

Testar o comportamento de @page "{id:int?}":

  1. Defina a diretiva de página em Pages/Movies/Details.cshtml para @page "{id:int?}".
  2. Defina um ponto de rutura em public async Task<IActionResult> OnGetAsync(int? id), em Pages/Movies/Details.cshtml.cs.
  3. Navegue até https://localhost:5001/Movies/Details/.

Com a @page "{id:int}" diretiva, o ponto de ruptura nunca é atingido. O motor de encaminhamento devolve HTTP 404. Usando @page "{id:int?}", o OnGetAsync método retorna 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();
}

Revisão do tratamento das exceções de concorrência

Revise o OnPostAsync método no Pages/Movies/Edit.cshtml.cs ficheiro:

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 deteta exceções de concorrência quando um cliente apaga o filme e o outro cliente publica alterações ao filme.

Para testar o bloco catch:

  1. Defina um ponto de quebra em catch (DbUpdateConcurrencyException).
  2. Selecione Editar para um filme, faça alterações, mas não selecione Guardar.
  3. Noutra janela do navegador, selecione o link Apagar para o mesmo filme e depois apague o filme.
  4. Na janela anterior do navegador, publique alterações ao filme.

O código de produção pode querer detetar conflitos de concorrência. Consulte Lidar com conflitos de concorrência para mais informações.

Publicação e revisão vinculativa

Veja o Pages/Movies/Edit.cshtml.cs ficheiro:

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 um pedido HTTP GET é feito na página de Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3:

  • O OnGetAsync método recolhe o filme da base de dados e devolve o Page método.
  • O Page método renderiza a Pages/Movies/Edit.cshtmlRazor Página. O Pages/Movies/Edit.cshtml ficheiro contém a diretiva @model RazorPagesMovie.Pages.Movies.EditModel do modelo, que torna o modelo do filme disponível na página.
  • O formulário de Editar é exibido com os valores do filme.

Quando a página de Filmes/Editar é publicada:

  • Os valores do formulário na página estão ligados à Movie propriedade. O [BindProperty] atributo permite a ligação por modelo.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se houver erros no estado do modelo, por exemplo, se ReleaseDate não puder ser convertido em uma data, o formulário é reapresentado com os valores introduzidos.

  • Se não houver erros de modelo, o filme é guardado.

Os métodos HTTP GET nas páginas Index, Create e Delete Razor seguem um padrão semelhante. O método HTTP POST OnPostAsync na Página de Criação Razor segue um padrão semelhante ao método OnPostAsync na Página de Edição Razor.

Próximos passos

A aplicação de filmes com andaime tem um bom início, mas a apresentação não é ideal. Data de Lançamento deve ser duas palavras: Data de Lançamento.

Aplicação de filmes aberta no Chrome

Atualize o código gerado

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

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 [Column(TypeName = "decimal(18, 2)")] anotação de dados permite que o Entity Framework Core mapeie Price corretamente para a moeda na base de dados. Para obter mais informações, consulte 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 "DataDeLançamento".
  • O atributo [DataType] especifica o tipo dos dados (Date). A informação de hora armazenada no campo não é apresentada.

O DataAnnotations é abordado no próximo tutorial.

Navegue até Páginas/Filmes e passe o rato sobre um link de Editar para ver o URL alvo.

Janela do navegador com o rato sobre o link Editar e é mostrada uma URL do link https://localhost:1234/Movies/Edit/5

Os links Editar, Detalhes e Eliminar são gerados pelo Anchor Tag Helper no Pages/Movies/Index.cshtml ficheiro.

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

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor.

No código anterior, o Anchor Tag Helper gera dinamicamente o valor do atributo HTML href a partir da Razor Página (a rota é relativa), do asp-page, e do identificador da rota (asp-route-id). Para mais informações, consulte geração de URL para Páginas.

Utilize Ver Fonte no navegador 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>

As ligações geradas dinamicamente passam o ID do filme com uma string de consulta. Por exemplo, o ?id=1 em https://localhost:5001/Movies/Details?id=1.

Adicionar modelo de rota

Atualize as páginas de Edição, Detalhes e Eliminação Razor para utilizar o modelo de rota {id:int}. Mude a diretiva de página para cada uma destas páginas de @page para @page "{id:int}". Abre a aplicação e depois vê a fonte.

O HTML gerado acrescenta o ID à secção do caminho do URL.

<td>
  <a href="/Movies/Edit/1">Edit</a> |
  <a href="/Movies/Details/1">Details</a> |
  <a href="/Movies/Delete/1">Delete</a>
</td>

Um pedido à página que não inclui o inteiro no {id:int} modelo de rota devolverá um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details devolve um erro 404. Para tornar o ID opcional, acrescente ? à restrição de rota:

@page "{id:int?}"

Testar o comportamento de @page "{id:int?}":

  1. Defina a diretiva de página em Pages/Movies/Details.cshtml para @page "{id:int?}".
  2. Defina um ponto de rutura em public async Task<IActionResult> OnGetAsync(int? id), em Pages/Movies/Details.cshtml.cs.
  3. Navegue até https://localhost:5001/Movies/Details/.

Com a @page "{id:int}" diretiva, o ponto de ruptura nunca é atingido. O motor de encaminhamento devolve HTTP 404. Usando @page "{id:int?}", o OnGetAsync método retorna 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();
}

Revisão do tratamento das exceções de concorrência

Revise o OnPostAsync método no Pages/Movies/Edit.cshtml.cs ficheiro:

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 deteta exceções de concorrência quando um cliente apaga o filme e o outro cliente publica alterações ao filme. O código anterior não deteta conflitos que ocorrem devido a dois ou mais clientes editarem o mesmo filme em simultâneo. Neste caso, as edições feitas por múltiplos clientes são aplicadas na ordem em que SaveChanges é chamado e as edições aplicadas posteriormente podem sobrescrever edições anteriores com valores obsoletos.

Para testar o bloco catch:

  1. Defina um ponto de quebra em catch (DbUpdateConcurrencyException).
  2. Selecione Editar para um filme, faça alterações, mas não selecione Guardar.
  3. Noutra janela do navegador, selecione o link Apagar para o mesmo filme e depois apague o filme.
  4. Na janela anterior do navegador, publique alterações ao filme.

O código de produção pode querer detetar conflitos adicionais de concorrência, como múltiplos clientes a editar uma entidade ao mesmo tempo. Consulte Lidar com conflitos de concorrência para mais informações.

Publicação e revisão vinculativa

Veja o Pages/Movies/Edit.cshtml.cs ficheiro:

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 um pedido HTTP GET é feito na página de Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3:

  • O OnGetAsync método recolhe o filme da base de dados e devolve o Page método.
  • O Page método renderiza a Pages/Movies/Edit.cshtmlRazor Página. O Pages/Movies/Edit.cshtml ficheiro contém a diretiva @model RazorPagesMovie.Pages.Movies.EditModel do modelo, que torna o modelo do filme disponível na página.
  • O formulário de Editar é exibido com os valores do filme.

Quando a página de Filmes/Editar é publicada:

  • Os valores do formulário na página estão ligados à Movie propriedade. O [BindProperty] atributo permite a ligação por modelo.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se houver erros no estado do modelo, por exemplo, se ReleaseDate não puder ser convertido em uma data, o formulário é reapresentado com os valores introduzidos.

  • Se não houver erros de modelo, o filme é guardado.

Os métodos HTTP GET nas páginas Index, Create e Delete Razor seguem um padrão semelhante. O método HTTP POST OnPostAsync na Página de Criação Razor segue um padrão semelhante ao método OnPostAsync na Página de Edição Razor.

Próximos passos

A aplicação de filmes com andaime tem um bom início, mas a apresentação não é ideal. Data de Lançamento deve ser duas palavras: Data de Lançamento.

Aplicação de filmes aberta no Chrome

Atualize o código gerado

Abra o Models/Movie.cs ficheiro e adicione as linhas destacadas 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 [Column(TypeName = "decimal(18, 2)")] anotação de dados permite que o Entity Framework Core mapeie Price corretamente para a moeda na base de dados. Para obter mais informações, consulte 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 "DataDeLançamento".
  • O atributo [DataType] especifica o tipo dos dados (Date). A informação de hora armazenada no campo não é apresentada.

O DataAnnotations é abordado no próximo tutorial.

Navegue até Páginas/Filmes e passe o rato sobre um link de Editar para ver o URL alvo.

Janela do navegador com o rato sobre o link Editar e é mostrada uma URL do link https://localhost:1234/Movies/Edit/5

Os links Editar, Detalhes e Eliminar são gerados pelo Anchor Tag Helper no Pages/Movies/Index.cshtml ficheiro.

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

Tag Helpers permitem que o código do lado do servidor participe na criação e renderização de elementos HTML em ficheiros Razor.

No código anterior, o Anchor Tag Helper gera dinamicamente o valor do atributo HTML href a partir da Razor Página (a rota é relativa), do asp-page, e do identificador da rota (asp-route-id). Para mais informações, consulte geração de URL para Páginas.

Utilize Ver Fonte no navegador 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>

As ligações geradas dinamicamente passam o ID do filme com uma string de consulta. Por exemplo, o ?id=1 em https://localhost:5001/Movies/Details?id=1.

Adicionar modelo de rota

Atualize as páginas de Edição, Detalhes e Eliminação Razor para utilizar o modelo de rota {id:int}. Mude a diretiva de página para cada uma destas páginas de @page para @page "{id:int}". Abre a aplicação e depois vê a fonte.

O HTML gerado acrescenta o ID à secção do caminho do URL.

<td>
  <a href="/Movies/Edit/1">Edit</a> |
  <a href="/Movies/Details/1">Details</a> |
  <a href="/Movies/Delete/1">Delete</a>
</td>

Um pedido à página que não inclui o inteiro no {id:int} modelo de rota devolverá um erro HTTP 404 (não encontrado). Por exemplo, https://localhost:5001/Movies/Details devolve um erro 404. Para tornar o ID opcional, acrescente ? à restrição de rota:

@page "{id:int?}"

Testar o comportamento de @page "{id:int?}":

  1. Defina a diretiva de página em Pages/Movies/Details.cshtml para @page "{id:int?}".
  2. Defina um ponto de rutura em public async Task<IActionResult> OnGetAsync(int? id), em Pages/Movies/Details.cshtml.cs.
  3. Navegue até https://localhost:5001/Movies/Details/.

Com a @page "{id:int}" diretiva, o ponto de ruptura nunca é atingido. O motor de encaminhamento devolve HTTP 404. Usando @page "{id:int?}", o OnGetAsync método retorna 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();
}

Revisão do tratamento das exceções de concorrência

Revise o OnPostAsync método no Pages/Movies/Edit.cshtml.cs ficheiro:

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 deteta exceções de concorrência quando um cliente apaga o filme e o outro cliente publica alterações ao filme.

Para testar o bloco catch:

  1. Defina um ponto de quebra em catch (DbUpdateConcurrencyException).
  2. Selecione Editar para um filme, faça alterações, mas não selecione Guardar.
  3. Noutra janela do navegador, selecione o link Apagar para o mesmo filme e depois apague o filme.
  4. Na janela anterior do navegador, publique alterações ao filme.

O código de produção pode querer detetar conflitos de concorrência. Consulte Lidar com conflitos de concorrência para mais informações.

Publicação e revisão vinculativa

Veja o Pages/Movies/Edit.cshtml.cs ficheiro:

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 um pedido HTTP GET é feito na página de Filmes/Editar, por exemplo, https://localhost:5001/Movies/Edit/3:

  • O OnGetAsync método recolhe o filme da base de dados e devolve o Page método.
  • O Page método renderiza a Pages/Movies/Edit.cshtmlRazor Página. O Pages/Movies/Edit.cshtml ficheiro contém a diretiva @model RazorPagesMovie.Pages.Movies.EditModel do modelo, que torna o modelo do filme disponível na página.
  • O formulário de Editar é exibido com os valores do filme.

Quando a página de Filmes/Editar é publicada:

  • Os valores do formulário na página estão ligados à Movie propriedade. O [BindProperty] atributo permite a ligação por modelo.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se houver erros no estado do modelo, por exemplo, se ReleaseDate não puder ser convertido em uma data, o formulário é reapresentado com os valores introduzidos.

  • Se não houver erros de modelo, o filme é guardado.

Os métodos HTTP GET nas páginas Index, Create e Delete Razor seguem um padrão semelhante. O método HTTP POST OnPostAsync na Página de Criação Razor segue um padrão semelhante ao método OnPostAsync na Página de Edição Razor.

Próximos passos