Skip to content

Commit 66e15cb

Browse files
authored
Merge pull request #36492 from dotnet/main
2 parents 972f6a4 + 5692e25 commit 66e15cb

3 files changed

Lines changed: 110 additions & 49 deletions

File tree

aspnetcore/blazor/tutorials/movie-database-app/part-1.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,16 @@ if (!app.Environment.IsDevelopment())
299299
}
300300
```
301301

302+
:::moniker range=">= aspnetcore-10.0"
303+
304+
By default, an ASP.NET Core app doesn't provide a status code page for HTTP error status codes, such as *404 - Not Found*. When the app sets an HTTP 400-599 error status code without a body, it returns the status code and an empty response body. However, an app generated from the Blazor Web App project template calls <xref:Microsoft.AspNetCore.Builder.StatusCodePagesExtensions.UseStatusCodePagesWithReExecute%2A> to add Status Code Pages Middleware to the request pipeline for pages that aren't found, which generates the response body by re-executing the request pipeline using the path to the Not Found error page (`/not-found`):
305+
306+
```csharp
307+
app.UseStatusCodePagesWithReExecute("/not-found", createScopeForStatusCodePages: true);
308+
```
309+
310+
:::moniker-end
311+
302312
HTTPS Redirection Middleware (<xref:Microsoft.AspNetCore.Builder.HttpsPolicyBuilderExtensions.UseHttpsRedirection%2A>) enforces the HTTPS protocol by redirecting HTTP requests to HTTPS if an HTTPS port is available:
303313

304314
```csharp

aspnetcore/blazor/tutorials/movie-database-app/part-3.md

Lines changed: 98 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,30 @@ Add a space to the content of the description term element (`<dt>`) for the movi
399399

400400
Examine the C# of the component's `@code` block:
401401

402+
:::moniker range=">= aspnetcore-10.0"
403+
404+
```csharp
405+
private Movie? movie;
406+
407+
[SupplyParameterFromQuery]
408+
private int Id { get; set; }
409+
410+
protected override async Task OnInitializedAsync()
411+
{
412+
using var context = DbFactory.CreateDbContext();
413+
movie = await context.Movie.FirstOrDefaultAsync(m => m.Id == Id);
414+
415+
if (movie is null)
416+
{
417+
NavigationManager.NotFound();
418+
}
419+
}
420+
```
421+
422+
:::moniker-end
423+
424+
:::moniker range="< aspnetcore-10.0"
425+
402426
```csharp
403427
private Movie? movie;
404428

@@ -417,27 +441,17 @@ protected override async Task OnInitializedAsync()
417441
}
418442
```
419443

444+
:::moniker-end
445+
420446
The `movie` variable is a private field of type `Movie`, which is a null-reference type (`?`), meaning that `movie` might be set to `null`.
421447

422448
The `Id` is a *component parameter* supplied from the component's query string due to the presence of the [`[SupplyParameterFromQuery]` attribute](xref:Microsoft.AspNetCore.Components.SupplyParameterFromQueryAttribute). If the identifier is missing, `Id` defaults to zero (`0`).
423449

424450
`OnInitializedAsync` is the first component lifecycle method that we've seen. This method is executed when the component loads. <xref:Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.FirstOrDefaultAsync%2A> is called on the database set (`DbSet<Movie>`) to retrieve the movie entity with an `Id` equal to the `Id` parameter that was set by the query string. If `movie` is `null`, <xref:Microsoft.AspNetCore.Components.NavigationManager.NavigateTo%2A?displayProperty=nameWithType> is used to navigate to a `notfound` endpoint.
425451

426-
<!-- UPDATE 10.0 - Update for scaffolder changes when they
427-
appear: https://github.com/dotnet/Scaffolding/issues/3177 -->
428-
429452
:::moniker range=">= aspnetcore-10.0"
430453

431-
The .NET scaffolder currently doesn't implement .NET 10's Not Found feature, but it can be implemented manually.
432-
433-
Update the line that navigates to a non-existent `notfound` endpoint. Change the line to call <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A?displayProperty=nameWithType>:
434-
435-
```diff
436-
- NavigationManager.NavigateTo("notfound");
437-
+ NavigationManager.NotFound();
438-
```
439-
440-
When a movie isn't found, this line change results in rendering the `NotFound` component, which produces a Not Found page in the browser with a 404 (Not Found) status code.
454+
When a movie isn't found, calling <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A?displayProperty=nameWithType> renders the `NotFound` component, which produces a Not Found page in the browser with a 404 (Not Found) status code.
441455

442456
:::moniker-end
443457

@@ -509,21 +523,42 @@ The `AddMovie` method:
509523
* <xref:Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync%2A> is called on the database context to save the movie.
510524
* <xref:Microsoft.AspNetCore.Components.NavigationManager> is used to return the user to the movies `Index` page.
511525

526+
:::moniker range=">= aspnetcore-10.0"
527+
512528
```csharp
513-
@code {
514-
[SupplyParameterFromForm]
515-
private Movie Movie { get; set; } = new();
529+
[SupplyParameterFromForm]
530+
private Movie Movie { get; set; } = default!;
516531

517-
private async Task AddMovie()
518-
{
519-
using var context = DbFactory.CreateDbContext();
520-
context.Movie.Add(Movie);
521-
await context.SaveChangesAsync();
522-
NavigationManager.NavigateTo("/movies");
523-
}
532+
protected override void OnInitialized() => Movie ??= new();
533+
534+
private async Task AddMovie()
535+
{
536+
using var context = DbFactory.CreateDbContext();
537+
context.Movie.Add(Movie);
538+
await context.SaveChangesAsync();
539+
NavigationManager.NavigateTo("/movies");
540+
}
541+
```
542+
543+
:::moniker-end
544+
545+
:::moniker range="< aspnetcore-10.0"
546+
547+
```csharp
548+
[SupplyParameterFromForm]
549+
private Movie Movie { get; set; } = new();
550+
551+
private async Task AddMovie()
552+
{
553+
using var context = DbFactory.CreateDbContext();
554+
context.Movie.Add(Movie);
555+
await context.SaveChangesAsync();
556+
NavigationManager.NavigateTo("/movies");
524557
}
525558
```
526559

560+
:::moniker-end
561+
527562
> [!WARNING]
528563
> Although it isn't a concern for the app in this tutorial, binding form data to entity data models can be susceptible to overposting attacks. Additional information on this subject appears later in this article.
529564
@@ -558,21 +593,9 @@ private async Task DeleteMovie()
558593
}
559594
```
560595

561-
<!-- UPDATE 10.0 - Update for scaffolder changes when they
562-
appear: https://github.com/dotnet/Scaffolding/issues/3177 -->
563-
564596
:::moniker range=">= aspnetcore-10.0"
565597

566-
The .NET scaffolder currently doesn't implement .NET 10's Not Found feature, but it can be implemented manually.
567-
568-
Update the line that navigates to a non-existent `notfound` endpoint. Change the line to call <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A?displayProperty=nameWithType>:
569-
570-
```diff
571-
- NavigationManager.NavigateTo("notfound");
572-
+ NavigationManager.NotFound();
573-
```
574-
575-
When a movie isn't found, this line change results in rendering the `NotFound` component, which produces a Not Found page in the browser with a 404 (Not Found) status code.
598+
When a movie isn't found, calling <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A?displayProperty=nameWithType> renders the `NotFound` component, which produces a Not Found page in the browser with a 404 (Not Found) status code.
576599

577600
:::moniker-end
578601

@@ -597,6 +620,8 @@ The movie entity's identifier `Id` is stored in a hidden field of the form:
597620

598621
Examine the C# code of the `@code` block:
599622

623+
:::moniker range=">= aspnetcore-10.0"
624+
600625
```csharp
601626
private async Task UpdateMovie()
602627
{
@@ -611,7 +636,7 @@ private async Task UpdateMovie()
611636
{
612637
if (!MovieExists(Movie!.Id))
613638
{
614-
NavigationManager.NavigateTo("notfound");
639+
NavigationManager.NotFound();
615640
}
616641
else
617642
{
@@ -629,23 +654,49 @@ private bool MovieExists(int id)
629654
}
630655
```
631656

632-
The movie entity's <xref:Microsoft.EntityFrameworkCore.EntityState> is set to <xref:Microsoft.EntityFrameworkCore.EntityState.Modified>, which signifies that the entity is tracked by the context, exists in the database, and that some or all of its property values are modified.
657+
:::moniker-end
633658

634-
<!-- UPDATE 10.0 - Update for scaffolder changes when they
635-
appear: https://github.com/dotnet/Scaffolding/issues/3177 -->
659+
:::moniker range="< aspnetcore-10.0"
636660

637-
:::moniker range=">= aspnetcore-10.0"
661+
```csharp
662+
private async Task UpdateMovie()
663+
{
664+
using var context = DbFactory.CreateDbContext();
665+
context.Attach(Movie!).State = EntityState.Modified;
638666

639-
The .NET scaffolder currently doesn't implement .NET 10's Not Found feature, but it can be implemented manually.
667+
try
668+
{
669+
await context.SaveChangesAsync();
670+
}
671+
catch (DbUpdateConcurrencyException)
672+
{
673+
if (!MovieExists(Movie!.Id))
674+
{
675+
NavigationManager.NavigateTo("notfound");
676+
}
677+
else
678+
{
679+
throw;
680+
}
681+
}
640682

641-
Update the line that navigates to a non-existent `notfound` endpoint. Change the line to call <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A?displayProperty=nameWithType>:
683+
NavigationManager.NavigateTo("/movies");
684+
}
642685

643-
```diff
644-
- NavigationManager.NavigateTo("notfound");
645-
+ NavigationManager.NotFound();
686+
private bool MovieExists(int id)
687+
{
688+
using var context = DbFactory.CreateDbContext();
689+
return context.Movie.Any(e => e.Id == id);
690+
}
646691
```
647692

648-
When a movie isn't found, this line change results in rendering the `NotFound` component, which produces a Not Found page in the browser with a 404 (Not Found) status code.
693+
:::moniker-end
694+
695+
The movie entity's <xref:Microsoft.EntityFrameworkCore.EntityState> is set to <xref:Microsoft.EntityFrameworkCore.EntityState.Modified>, which signifies that the entity is tracked by the context, exists in the database, and that some or all of its property values are modified.
696+
697+
:::moniker range=">= aspnetcore-10.0"
698+
699+
When a movie isn't found, calling <xref:Microsoft.AspNetCore.Components.NavigationManager.NotFound%2A?displayProperty=nameWithType> renders the `NotFound` component, which produces a Not Found page in the browser with a 404 (Not Found) status code.
649700

650701
If there's a concurrency exception and the movie entity no longer exists at the time that changes are saved, the component redirects to the Not Found page, which results in returning a 404 (Not Found) status code. If the movie exists and a concurrency exception is thrown, for example when another user has already modified the entity, the exception is rethrown by the component with the [`throw` statement (C# Language Reference)](/dotnet/csharp/language-reference/statements/exception-handling-statements#the-throw-statement). Additional guidance on handling concurrency with EF Core in Blazor apps is provided by the Blazor documentation.
651702

aspnetcore/blazor/tutorials/movie-database-app/part-5.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Add the following data annotations to the `Movie` class properties. To update al
4040
public string? Title { get; set; }
4141

4242
+ [Required]
43-
* [StringLength(30)]
43+
+ [StringLength(30)]
4444
+ [RegularExpression(@"^[A-Z]+[a-zA-Z()\s-]*$")]
4545
public string? Genre { get; set; }
4646

@@ -50,7 +50,7 @@ Add the following data annotations to the `Movie` class properties. To update al
5050
public decimal Price { get; set; }
5151
```
5252

53-
<!-- HOLD for a later version of the tutorial
53+
<!-- UPDATE 11.0 - HOLD for a later version of the tutorial
5454
when QuickGrid has display name support per
5555
https://github.com/dotnet/aspnetcore/issues/49147.
5656

0 commit comments

Comments
 (0)