Parte 5, aggiornare le pagine generate in un'app ASP.NET Core

Nota

Questa non è la versione più recente di questo articolo. Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Avviso

Questa versione di ASP.NET Core non è più supportata. Per altre informazioni, vedere Criteri di supporto di .NET e .NET Core. Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Importante

Queste informazioni si riferiscono a un prodotto non definitive che può essere modificato in modo sostanziale prima che venga rilasciato commercialmente. Microsoft non riconosce alcuna garanzia, espressa o implicita, in merito alle informazioni qui fornite.

Per la versione corrente, vedere la versione .NET 8 di questo articolo.

Le operazioni iniziali con l'app per i film creata con scaffolding sono state efficaci, ma la presentazione non è ottimale. ReleaseDate deve essere di due parole, data di rilascio.

App per i film aperta in Chrome

Aggiornare il modello

Eseguire l'aggiornamento Models/Movie.cs con il codice evidenziato seguente:

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

Nel codice precedente:

  • L'annotazione dei dati [Column(TypeName = "decimal(18, 2)")] consente a Entity Framework Core di eseguire correttamente il mapping di Price nella valuta del database. Per altre informazioni, vedere Tipi di dati.
  • L'attributo [Display] specifica il nome visualizzato di un campo. Nel codice precedente, Release Date anziché ReleaseDate.
  • L'attributo [DataType] specifica il tipo dei dati (Date). Le informazioni sull'ora archiviate nel campo non vengono visualizzate.

L'attributo DataAnnotations viene esaminato nell'esercitazione successiva.

Passare a Pagine/Film e passare il puntatore del mouse su un collegamento Modifica per visualizzare l'URL di destinazione.

Finestra del browser con il passaggio del mouse sul collegamento Edit (Modifica) e un URL di collegamento di https://localhost:1234/Movies/Edit/5

I collegamenti Modifica, Dettagli ed Elimina vengono generati dall'helper tag di ancoraggio nel Pages/Movies/Index.cshtml file.

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

Gli helper tag consentono al codice lato server di partecipare alla creazione e al rendering di elementi HTML nei file Razor.

Nel codice precedente, l'helper tag di ancoraggio genera dinamicamente il valore dell'attributo HTML href dalla Razor pagina (la route è relativa), l'identificatore asp-pagee l'identificatore di route (asp-route-id). Per altre informazioni, vedere Generazione di URL per Le pagine.

Usare Visualizza origine da un browser per esaminare il markup generato. Di seguito è riportata una parte del codice HTML generato:

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

I collegamenti generati dinamicamente passano l'ID filmato con una stringa di query. Ad esempio, in ?id=1 https://localhost:5001/Movies/Details?id=1.

Aggiungere un modello di route

Aggiornare le pagine Modifica, Dettagli ed Elimina Razor per usare il {id:int} modello di route. Modificare la direttiva page per ognuna di queste pagine da @page a @page "{id:int}". Eseguire l'app e quindi visualizzare l'origine.

Il codice HTML generato aggiunge l'ID alla parte di percorso dell'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 richiesta alla pagina con il {id:int} modello di route che non include l'intero restituisce un errore HTTP 404 (non trovato). Ad esempio, https://localhost:5001/Movies/Details restituisce un errore 404. Per rendere l'ID facoltativo, aggiungere ? al vincolo di route:

@page "{id:int?}"

Testare il comportamento di @page "{id:int?}":

  1. Impostare la direttiva page in su Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Impostare un punto di interruzione in public async Task<IActionResult> OnGetAsync(int? id), in Pages/Movies/Details.cshtml.cs.
  3. Accedere a https://localhost:5001/Movies/Details/.

Con l'istruzione @page "{id:int}", il punto di interruzione non viene mai raggiunto. Il motore di routing restituisce HTTP 404. Usando @page "{id:int?}", il OnGetAsync metodo restituisce 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();
}

Verificare la gestione delle eccezioni di concorrenza

Esaminare il OnPostAsync metodo nel Pages/Movies/Edit.cshtml.cs file:

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

Il codice precedente rileva eccezioni di concorrenza quando un client elimina il filmato e l'altro client pubblica modifiche al film.

Per testare il blocco catch:

  1. Impostare un punto di interruzione su catch (DbUpdateConcurrencyException).
  2. Selezionare Edit (Modifica) per un film, apportare modifiche, ma non immettere Save (Salva).
  3. In un'altra finestra del browser, selezionare il collegamento Delete (Elimina) per lo stesso film e quindi eliminare il film.
  4. Nella finestra del browser precedente inviare le modifiche al film.

In alcuni casi, il codice utilizzabile in ambienti di produzione potrebbe voler rilevare i conflitti di concorrenza. Per altre informazioni, vedere Gestire i conflitti di concorrenza.

Invio di post e analisi delle associazioni

Esaminare il Pages/Movies/Edit.cshtml.cs file:

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 viene effettuata una richiesta HTTP GET alla pagina Film/Modifica, ad esempio : https://localhost:5001/Movies/Edit/3

  • Il metodo OnGetAsync recupera il film dal database e restituisce il metodo Page.
  • Il Page metodo esegue il rendering di Pages/Movies/Edit.cshtmlRazor Page. Il Pages/Movies/Edit.cshtml file contiene la direttiva @model RazorPagesMovie.Pages.Movies.EditModelmodel , che rende disponibile il modello di film nella pagina.
  • Il modulo Edit (Modifica) viene visualizzato con i valori dal film.

Quando viene inviata la pagina Movies/Edit (Film/Modifica):

  • I valori del modulo nella pagina vengono associati alla proprietà Movie. L'attributo [BindProperty] abilita l'associazione di modelli.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se si verificano errori nello stato del modello, ad esempio, ReleaseDate non può essere convertito in una data, il modulo viene riprodotto con i valori inviati.

  • Se non sono presenti errori del modello, il film viene salvato.

I metodi HTTP GET nelle pagine Index, Create e Delete Razor seguono un modello simile. Il metodo HTTP POST OnPostAsync nella pagina Crea Razor segue un modello simile al OnPostAsync metodo nella pagina Di modifica Razor .

Passaggi successivi

Le operazioni iniziali con l'app per i film creata con scaffolding sono state efficaci, ma la presentazione non è ottimale. ReleaseDate deve essere di due parole, data di rilascio.

App per i film aperta in Chrome

Aggiornare il modello

Eseguire l'aggiornamento Models/Movie.cs con il codice evidenziato seguente:

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

Nel codice precedente:

  • L'annotazione dei dati [Column(TypeName = "decimal(18, 2)")] consente a Entity Framework Core di eseguire correttamente il mapping di Price nella valuta del database. Per altre informazioni, vedere Tipi di dati.
  • L'attributo [Display] specifica il nome visualizzato di un campo. Nel codice precedente, Release Date anziché ReleaseDate.
  • L'attributo [DataType] specifica il tipo dei dati (Date). Le informazioni sull'ora archiviate nel campo non vengono visualizzate.

L'attributo DataAnnotations viene esaminato nell'esercitazione successiva.

Passare a Pagine/Film e passare il puntatore del mouse su un collegamento Modifica per visualizzare l'URL di destinazione.

Finestra del browser con il passaggio del mouse sul collegamento Edit (Modifica) e un URL di collegamento di https://localhost:1234/Movies/Edit/5

I collegamenti Modifica, Dettagli ed Elimina vengono generati dall'helper tag di ancoraggio nel Pages/Movies/Index.cshtml file.

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

Gli helper tag consentono al codice lato server di partecipare alla creazione e al rendering di elementi HTML nei file Razor.

Nel codice precedente, l'helper tag di ancoraggio genera dinamicamente il valore dell'attributo HTML href dalla Razor pagina (la route è relativa), l'identificatore asp-pagee l'identificatore di route (asp-route-id). Per altre informazioni, vedere Generazione di URL per Le pagine.

Usare Visualizza origine da un browser per esaminare il markup generato. Di seguito è riportata una parte del codice HTML generato:

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

I collegamenti generati dinamicamente passano l'ID filmato con una stringa di query. Ad esempio, in ?id=1 https://localhost:5001/Movies/Details?id=1.

Aggiungere un modello di route

Aggiornare le pagine Modifica, Dettagli ed Elimina Razor per usare il {id:int} modello di route. Modificare la direttiva page per ognuna di queste pagine da @page a @page "{id:int}". Eseguire l'app e quindi visualizzare l'origine.

Il codice HTML generato aggiunge l'ID alla parte di percorso dell'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 richiesta alla pagina con il {id:int} modello di route che non include l'intero restituisce un errore HTTP 404 (non trovato). Ad esempio, https://localhost:5001/Movies/Details restituisce un errore 404. Per rendere l'ID facoltativo, aggiungere ? al vincolo di route:

@page "{id:int?}"

Testare il comportamento di @page "{id:int?}":

  1. Impostare la direttiva page in su Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Impostare un punto di interruzione in public async Task<IActionResult> OnGetAsync(int? id), in Pages/Movies/Details.cshtml.cs.
  3. Accedere a https://localhost:5001/Movies/Details/.

Con l'istruzione @page "{id:int}", il punto di interruzione non viene mai raggiunto. Il motore di routing restituisce HTTP 404. Usando @page "{id:int?}", il OnGetAsync metodo restituisce 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();
}

Verificare la gestione delle eccezioni di concorrenza

Esaminare il OnPostAsync metodo nel Pages/Movies/Edit.cshtml.cs file:

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

Il codice precedente rileva eccezioni di concorrenza quando un client elimina il filmato e l'altro client pubblica modifiche al film.

Per testare il blocco catch:

  1. Impostare un punto di interruzione su catch (DbUpdateConcurrencyException).
  2. Selezionare Edit (Modifica) per un film, apportare modifiche, ma non immettere Save (Salva).
  3. In un'altra finestra del browser, selezionare il collegamento Delete (Elimina) per lo stesso film e quindi eliminare il film.
  4. Nella finestra del browser precedente inviare le modifiche al film.

In alcuni casi, il codice utilizzabile in ambienti di produzione potrebbe voler rilevare i conflitti di concorrenza. Per altre informazioni, vedere Gestire i conflitti di concorrenza.

Invio di post e analisi delle associazioni

Esaminare il Pages/Movies/Edit.cshtml.cs file:

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 viene effettuata una richiesta HTTP GET alla pagina Film/Modifica, ad esempio : https://localhost:5001/Movies/Edit/3

  • Il metodo OnGetAsync recupera il film dal database e restituisce il metodo Page.
  • Il Page metodo esegue il rendering di Pages/Movies/Edit.cshtmlRazor Page. Il Pages/Movies/Edit.cshtml file contiene la direttiva @model RazorPagesMovie.Pages.Movies.EditModelmodel , che rende disponibile il modello di film nella pagina.
  • Il modulo Edit (Modifica) viene visualizzato con i valori dal film.

Quando viene inviata la pagina Movies/Edit (Film/Modifica):

  • I valori del modulo nella pagina vengono associati alla proprietà Movie. L'attributo [BindProperty] abilita l'associazione di modelli.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se si verificano errori nello stato del modello, ad esempio, ReleaseDate non può essere convertito in una data, il modulo viene riprodotto con i valori inviati.

  • Se non sono presenti errori del modello, il film viene salvato.

I metodi HTTP GET nelle pagine Index, Create e Delete Razor seguono un modello simile. Il metodo HTTP POST OnPostAsync nella pagina Crea Razor segue un modello simile al OnPostAsync metodo nella pagina Di modifica Razor .

Passaggi successivi

Le operazioni iniziali con l'app per i film creata con scaffolding sono state efficaci, ma la presentazione non è ottimale. ReleaseDate deve essere di due parole, data di rilascio.

App per i film aperta in Chrome

Aggiornare il modello

Eseguire l'aggiornamento Models/Movie.cs con il codice evidenziato seguente:

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

Nel codice precedente:

  • L'annotazione dei dati [Column(TypeName = "decimal(18, 2)")] consente a Entity Framework Core di eseguire correttamente il mapping di Price nella valuta del database. Per altre informazioni, vedere Tipi di dati.
  • L'attributo [Display] specifica il nome visualizzato di un campo. Nel codice precedente, Release Date anziché ReleaseDate.
  • L'attributo [DataType] specifica il tipo dei dati (Date). Le informazioni sull'ora archiviate nel campo non vengono visualizzate.

L'attributo DataAnnotations viene esaminato nell'esercitazione successiva.

Passare a Pagine/Film e passare il puntatore del mouse su un collegamento Modifica per visualizzare l'URL di destinazione.

Finestra del browser con il passaggio del mouse sul collegamento Edit (Modifica) e un URL di collegamento di https://localhost:1234/Movies/Edit/5

I collegamenti Modifica, Dettagli ed Elimina vengono generati dall'helper tag di ancoraggio nel Pages/Movies/Index.cshtml file.

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

Gli helper tag consentono al codice lato server di partecipare alla creazione e al rendering di elementi HTML nei file Razor.

Nel codice precedente, l'helper tag di ancoraggio genera dinamicamente il valore dell'attributo HTML href dalla Razor pagina (la route è relativa), l'identificatore asp-pagee l'identificatore di route (asp-route-id). Per altre informazioni, vedere Generazione di URL per Le pagine.

Usare Visualizza origine da un browser per esaminare il markup generato. Di seguito è riportata una parte del codice HTML generato:

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

I collegamenti generati dinamicamente passano l'ID filmato con una stringa di query. Ad esempio, in ?id=1 https://localhost:5001/Movies/Details?id=1.

Aggiungere un modello di route

Aggiornare le pagine Modifica, Dettagli ed Elimina Razor per usare il {id:int} modello di route. Modificare la direttiva page per ognuna di queste pagine da @page a @page "{id:int}". Eseguire l'app e quindi visualizzare l'origine.

Il codice HTML generato aggiunge l'ID alla parte di percorso dell'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 richiesta alla pagina con il {id:int} modello di route che non include l'intero restituisce un errore HTTP 404 (non trovato). Ad esempio, https://localhost:5001/Movies/Details restituisce un errore 404. Per rendere l'ID facoltativo, aggiungere ? al vincolo di route:

@page "{id:int?}"

Testare il comportamento di @page "{id:int?}":

  1. Impostare la direttiva page in su Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Impostare un punto di interruzione in public async Task<IActionResult> OnGetAsync(int? id), in Pages/Movies/Details.cshtml.cs.
  3. Accedere a https://localhost:5001/Movies/Details/.

Con l'istruzione @page "{id:int}", il punto di interruzione non viene mai raggiunto. Il motore di routing restituisce HTTP 404. Usando @page "{id:int?}", il OnGetAsync metodo restituisce 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();
}

Verificare la gestione delle eccezioni di concorrenza

Esaminare il OnPostAsync metodo nel Pages/Movies/Edit.cshtml.cs file:

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

Il codice precedente rileva eccezioni di concorrenza quando un client elimina il filmato e l'altro client pubblica modifiche al film.

Per testare il blocco catch:

  1. Impostare un punto di interruzione su catch (DbUpdateConcurrencyException).
  2. Selezionare Edit (Modifica) per un film, apportare modifiche, ma non immettere Save (Salva).
  3. In un'altra finestra del browser, selezionare il collegamento Delete (Elimina) per lo stesso film e quindi eliminare il film.
  4. Nella finestra del browser precedente inviare le modifiche al film.

In alcuni casi, il codice utilizzabile in ambienti di produzione potrebbe voler rilevare i conflitti di concorrenza. Per altre informazioni, vedere Gestire i conflitti di concorrenza.

Invio di post e analisi delle associazioni

Esaminare il Pages/Movies/Edit.cshtml.cs file:

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 viene effettuata una richiesta HTTP GET alla pagina Film/Modifica, ad esempio : https://localhost:5001/Movies/Edit/3

  • Il metodo OnGetAsync recupera il film dal database e restituisce il metodo Page.
  • Il Page metodo esegue il rendering di Pages/Movies/Edit.cshtmlRazor Page. Il Pages/Movies/Edit.cshtml file contiene la direttiva @model RazorPagesMovie.Pages.Movies.EditModelmodel , che rende disponibile il modello di film nella pagina.
  • Il modulo Edit (Modifica) viene visualizzato con i valori dal film.

Quando viene inviata la pagina Movies/Edit (Film/Modifica):

  • I valori del modulo nella pagina vengono associati alla proprietà Movie. L'attributo [BindProperty] abilita l'associazione di modelli.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se si verificano errori nello stato del modello, ad esempio, ReleaseDate non può essere convertito in una data, il modulo viene riprodotto con i valori inviati.

  • Se non sono presenti errori del modello, il film viene salvato.

I metodi HTTP GET nelle pagine Index, Create e Delete Razor seguono un modello simile. Il metodo HTTP POST OnPostAsync nella pagina Crea Razor segue un modello simile al OnPostAsync metodo nella pagina Di modifica Razor .

Passaggi successivi

Le operazioni iniziali con l'app per i film creata con scaffolding sono state efficaci, ma la presentazione non è ottimale. ReleaseDate deve essere di due parole, data di rilascio.

App per i film aperta in Chrome

Aggiornare il codice generato

Eseguire l'aggiornamento Models/Movie.cs con il codice evidenziato seguente:

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

Nel codice precedente:

  • L'annotazione dei dati [Column(TypeName = "decimal(18, 2)")] consente a Entity Framework Core di eseguire correttamente il mapping di Price nella valuta del database. Per altre informazioni, vedere Tipi di dati.
  • L'attributo [Display] specifica il nome visualizzato di un campo. Nel codice precedente" "Data di rilascio" anziché "ReleaseDate".
  • L'attributo [DataType] specifica il tipo dei dati (Date). Le informazioni sull'ora archiviate nel campo non vengono visualizzate.

L'attributo DataAnnotations viene esaminato nell'esercitazione successiva.

Passare a Pagine/Film e passare il puntatore del mouse su un collegamento Modifica per visualizzare l'URL di destinazione.

Finestra del browser con il passaggio del mouse sul collegamento Edit (Modifica) e un URL di collegamento di https://localhost:1234/Movies/Edit/5

I collegamenti Modifica, Dettagli ed Elimina vengono generati dall'helper tag di ancoraggio nel Pages/Movies/Index.cshtml file.

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

Gli helper tag consentono al codice lato server di partecipare alla creazione e al rendering di elementi HTML nei file Razor.

Nel codice precedente, l'helper tag di ancoraggio genera dinamicamente il valore dell'attributo HTML href dalla Razor pagina (la route è relativa), l'identificatore asp-pagee l'identificatore di route (asp-route-id). Per altre informazioni, vedere Generazione di URL per Le pagine.

Usare Visualizza origine da un browser per esaminare il markup generato. Di seguito è riportata una parte del codice HTML generato:

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

I collegamenti generati dinamicamente passano l'ID filmato con una stringa di query. Ad esempio, in ?id=1 https://localhost:5001/Movies/Details?id=1.

Aggiungere un modello di route

Aggiornare le pagine Modifica, Dettagli ed Elimina Razor per usare il {id:int} modello di route. Modificare la direttiva page per ognuna di queste pagine da @page a @page "{id:int}". Eseguire l'app e quindi visualizzare l'origine.

Il codice HTML generato aggiunge l'ID alla parte di percorso dell'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 richiesta alla pagina con il {id:int} modello di route che non include l'intero restituirà un errore HTTP 404 (non trovato). Ad esempio, https://localhost:5001/Movies/Details restituirà un errore 404. Per rendere l'ID facoltativo, aggiungere ? al vincolo di route:

@page "{id:int?}"

Testare il comportamento di @page "{id:int?}":

  1. Impostare la direttiva page in su Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Impostare un punto di interruzione in public async Task<IActionResult> OnGetAsync(int? id), in Pages/Movies/Details.cshtml.cs.
  3. Accedere a https://localhost:5001/Movies/Details/.

Con l'istruzione @page "{id:int}", il punto di interruzione non viene mai raggiunto. Il motore di routing restituisce HTTP 404. Usando @page "{id:int?}", il OnGetAsync metodo restituisce 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();
}

Verificare la gestione delle eccezioni di concorrenza

Esaminare il OnPostAsync metodo nel Pages/Movies/Edit.cshtml.cs file:

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

Il codice precedente rileva eccezioni di concorrenza quando un client elimina il filmato e l'altro client pubblica modifiche al film. Il codice precedente non rileva conflitti che si verificano a causa di due o più client che modificano lo stesso film contemporaneamente. In questo caso, le modifiche apportate da più client vengono applicate nell'ordine SaveChanges chiamato e le modifiche applicate in un secondo momento possono sovrascrivere le modifiche precedenti con valori non aggiornati.

Per testare il blocco catch:

  1. Impostare un punto di interruzione su catch (DbUpdateConcurrencyException).
  2. Selezionare Edit (Modifica) per un film, apportare modifiche, ma non immettere Save (Salva).
  3. In un'altra finestra del browser, selezionare il collegamento Delete (Elimina) per lo stesso film e quindi eliminare il film.
  4. Nella finestra del browser precedente inviare le modifiche al film.

Il codice di produzione può voler rilevare conflitti di concorrenza aggiuntivi, ad esempio più client che modificano un'entità contemporaneamente. Per altre informazioni, vedere Gestire i conflitti di concorrenza.

Invio di post e analisi delle associazioni

Esaminare il Pages/Movies/Edit.cshtml.cs file:

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 viene effettuata una richiesta HTTP GET alla pagina Film/Modifica, ad esempio : https://localhost:5001/Movies/Edit/3

  • Il metodo OnGetAsync recupera il film dal database e restituisce il metodo Page.
  • Il Page metodo esegue il rendering di Pages/Movies/Edit.cshtmlRazor Page. Il Pages/Movies/Edit.cshtml file contiene la direttiva @model RazorPagesMovie.Pages.Movies.EditModelmodel , che rende disponibile il modello di film nella pagina.
  • Il modulo Edit (Modifica) viene visualizzato con i valori dal film.

Quando viene inviata la pagina Movies/Edit (Film/Modifica):

  • I valori del modulo nella pagina vengono associati alla proprietà Movie. L'attributo [BindProperty] abilita l'associazione di modelli.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se si verificano errori nello stato del modello, ad esempio, ReleaseDate non può essere convertito in una data, il modulo viene riprodotto con i valori inviati.

  • Se non sono presenti errori del modello, il film viene salvato.

I metodi HTTP GET nelle pagine Index, Create e Delete Razor seguono un modello simile. Il metodo HTTP POST OnPostAsync nella pagina Crea Razor segue un modello simile al OnPostAsync metodo nella pagina Di modifica Razor .

Passaggi successivi

Le operazioni iniziali con l'app per i film creata con scaffolding sono state efficaci, ma la presentazione non è ottimale. ReleaseDate deve essere di due parole, data di rilascio.

App per i film aperta in Chrome

Aggiornare il codice generato

Aprire il Models/Movie.cs file e aggiungere le righe evidenziate illustrate nel codice seguente:

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

Nel codice precedente:

  • L'annotazione dei dati [Column(TypeName = "decimal(18, 2)")] consente a Entity Framework Core di eseguire correttamente il mapping di Price nella valuta del database. Per altre informazioni, vedere Tipi di dati.
  • L'attributo [Display] specifica il nome visualizzato di un campo. Nel codice precedente" "Data di rilascio" anziché "ReleaseDate".
  • L'attributo [DataType] specifica il tipo dei dati (Date). Le informazioni sull'ora archiviate nel campo non vengono visualizzate.

L'attributo DataAnnotations viene esaminato nell'esercitazione successiva.

Passare a Pagine/Film e passare il puntatore del mouse su un collegamento Modifica per visualizzare l'URL di destinazione.

Finestra del browser con il passaggio del mouse sul collegamento Edit (Modifica) e un URL di collegamento di https://localhost:1234/Movies/Edit/5

I collegamenti Modifica, Dettagli ed Elimina vengono generati dall'helper tag di ancoraggio nel Pages/Movies/Index.cshtml file.

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

Gli helper tag consentono al codice lato server di partecipare alla creazione e al rendering di elementi HTML nei file Razor.

Nel codice precedente, l'helper tag di ancoraggio genera dinamicamente il valore dell'attributo HTML href dalla Razor pagina (la route è relativa), l'identificatore asp-pagee l'identificatore di route (asp-route-id). Per altre informazioni, vedere Generazione di URL per Le pagine.

Usare Visualizza origine da un browser per esaminare il markup generato. Di seguito è riportata una parte del codice HTML generato:

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

I collegamenti generati dinamicamente passano l'ID filmato con una stringa di query. Ad esempio, in ?id=1 https://localhost:5001/Movies/Details?id=1.

Aggiungere un modello di route

Aggiornare le pagine Modifica, Dettagli ed Elimina Razor per usare il {id:int} modello di route. Modificare la direttiva page per ognuna di queste pagine da @page a @page "{id:int}". Eseguire l'app e quindi visualizzare l'origine.

Il codice HTML generato aggiunge l'ID alla parte di percorso dell'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 richiesta alla pagina con il {id:int} modello di route che non include l'intero restituirà un errore HTTP 404 (non trovato). Ad esempio, https://localhost:5001/Movies/Details restituirà un errore 404. Per rendere l'ID facoltativo, aggiungere ? al vincolo di route:

@page "{id:int?}"

Testare il comportamento di @page "{id:int?}":

  1. Impostare la direttiva page in su Pages/Movies/Details.cshtml @page "{id:int?}".
  2. Impostare un punto di interruzione in public async Task<IActionResult> OnGetAsync(int? id), in Pages/Movies/Details.cshtml.cs.
  3. Accedere a https://localhost:5001/Movies/Details/.

Con l'istruzione @page "{id:int}", il punto di interruzione non viene mai raggiunto. Il motore di routing restituisce HTTP 404. Usando @page "{id:int?}", il OnGetAsync metodo restituisce 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();
}

Verificare la gestione delle eccezioni di concorrenza

Esaminare il OnPostAsync metodo nel Pages/Movies/Edit.cshtml.cs file:

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

Il codice precedente rileva eccezioni di concorrenza quando un client elimina il filmato e l'altro client pubblica modifiche al film.

Per testare il blocco catch:

  1. Impostare un punto di interruzione su catch (DbUpdateConcurrencyException).
  2. Selezionare Edit (Modifica) per un film, apportare modifiche, ma non immettere Save (Salva).
  3. In un'altra finestra del browser, selezionare il collegamento Delete (Elimina) per lo stesso film e quindi eliminare il film.
  4. Nella finestra del browser precedente inviare le modifiche al film.

In alcuni casi, il codice utilizzabile in ambienti di produzione potrebbe voler rilevare i conflitti di concorrenza. Per altre informazioni, vedere Gestire i conflitti di concorrenza.

Invio di post e analisi delle associazioni

Esaminare il Pages/Movies/Edit.cshtml.cs file:

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 viene effettuata una richiesta HTTP GET alla pagina Film/Modifica, ad esempio : https://localhost:5001/Movies/Edit/3

  • Il metodo OnGetAsync recupera il film dal database e restituisce il metodo Page.
  • Il Page metodo esegue il rendering di Pages/Movies/Edit.cshtmlRazor Page. Il Pages/Movies/Edit.cshtml file contiene la direttiva @model RazorPagesMovie.Pages.Movies.EditModelmodel , che rende disponibile il modello di film nella pagina.
  • Il modulo Edit (Modifica) viene visualizzato con i valori dal film.

Quando viene inviata la pagina Movies/Edit (Film/Modifica):

  • I valori del modulo nella pagina vengono associati alla proprietà Movie. L'attributo [BindProperty] abilita l'associazione di modelli.

    [BindProperty]
    public Movie Movie { get; set; }
    
  • Se si verificano errori nello stato del modello, ad esempio, ReleaseDate non può essere convertito in una data, il modulo viene riprodotto con i valori inviati.

  • Se non sono presenti errori del modello, il film viene salvato.

I metodi HTTP GET nelle pagine Index, Create e Delete Razor seguono un modello simile. Il metodo HTTP POST OnPostAsync nella pagina Crea Razor segue un modello simile al OnPostAsync metodo nella pagina Di modifica Razor .

Passaggi successivi