Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions aspnetcore/blazor/blazor-ef-core.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ The grid, add, and view components use the "context-per-operation" pattern, wher
> [!NOTE]
> Some of the code examples in this topic require namespaces and services that aren't shown. To inspect the fully working code, including the required [`@using`](xref:mvc/views/razor#using) and [`@inject`](xref:mvc/views/razor#inject) directives for Razor examples, see the [sample app](#sample-app).

:::moniker range=">= aspnetcore-8.0"

## Build a Blazor movie database app tutorial

For a tutorial experience building an app that uses EF Core to work with a database, see <xref:blazor/tutorials/movie-database-app/index>. The tutorial shows you how to create a Blazor Web App that can display and manage movies in a movie database.

:::moniker-end

## Database access

EF Core relies on a <xref:Microsoft.EntityFrameworkCore.DbContext> as the means to [configure database access](/ef/core/miscellaneous/configuring-dbcontext) and act as a [*unit of work*](https://martinfowler.com/eaaCatalog/unitOfWork.html). EF Core provides the <xref:Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.AddDbContext%2A> extension for ASP.NET Core apps that registers the context as a *scoped* service. In server-side Blazor apps, scoped service registrations can be problematic because the instance is shared across components within the user's circuit. <xref:Microsoft.EntityFrameworkCore.DbContext> isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons:
Expand Down
70 changes: 9 additions & 61 deletions aspnetcore/blazor/debug.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,13 @@ Blazor Server: [`Microsoft.AspNetCore.Components.WebAssembly.Server`](https://ww

:::moniker-end

Standalone Blazor WebAssembly: [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer): Development server for use when building Blazor apps. Calls <xref:Microsoft.AspNetCore.Builder.WebAssemblyNetDebugProxyAppBuilderExtensions.UseWebAssemblyDebugging%2A?displayProperty=nameWithType> internally to add middleware for debugging Blazor WebAssembly apps inside Chromium developer tools.
Standalone Blazor WebAssembly: [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer): Development server for use when building Blazor apps. Calls <xref:Microsoft.AspNetCore.Builder.WebAssemblyNetDebugProxyAppBuilderExtensions.UseWebAssemblyDebugging%2A> internally to add middleware for debugging Blazor WebAssembly apps inside Chromium developer tools.

:::moniker range="< aspnetcore-8.0"

Hosted Blazor WebAssembly:

* **:::no-loc text="Client":::** project: [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer): Development server for use when building Blazor apps. Calls <xref:Microsoft.AspNetCore.Builder.WebAssemblyNetDebugProxyAppBuilderExtensions.UseWebAssemblyDebugging%2A?displayProperty=nameWithType> internally to add middleware for debugging Blazor WebAssembly apps inside Chromium developer tools.
* **:::no-loc text="Client":::** project: [`Microsoft.AspNetCore.Components.WebAssembly.DevServer`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.DevServer): Development server for use when building Blazor apps. Calls <xref:Microsoft.AspNetCore.Builder.WebAssemblyNetDebugProxyAppBuilderExtensions.UseWebAssemblyDebugging%2A> internally to add middleware for debugging Blazor WebAssembly apps inside Chromium developer tools.
* **:::no-loc text="Server":::** project: [`Microsoft.AspNetCore.Components.WebAssembly.Server`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.WebAssembly.Server): References an internal package ([`Microsoft.NETCore.BrowserDebugHost.Transport`](https://github.com/dotnet/runtime/blob/main/src/mono/nuget/Microsoft.NETCore.BrowserDebugHost.Transport/Microsoft.NETCore.BrowserDebugHost.Transport.pkgproj)) for assemblies that share the browser debug host.

:::moniker-end
Expand All @@ -187,43 +187,17 @@ The example in this section assumes that you've created a Blazor Web App with an

1. Open the app.
1. Set a breakpoint on the `currentCount++;` line in the `Counter` component (`Pages/Counter.razor`) of the client project (`.Client`).
1. Press <kbd>F5</kbd> to run the app in the debugger.
1. With the server project selected in **Solution Explorer**, press <kbd>F5</kbd> to run the app in the debugger.
1. In the browser, navigate to `Counter` page at `/counter`. Wait a few seconds for the debug proxy to load and run. Select the **Click me** button to hit the breakpoint.
1. In Visual Studio, inspect the value of the `currentCount` field in the **Locals** window.
1. Press <kbd>F5</kbd> to continue execution.

Breakpoints can also be hit in the server project in statically-rendered and interactively-rendered server-side components.

1. Stop the debugger.
1. Add the following component to the server app. The component applies the Interactive Server render mode (`InteractiveServer`).

`Components/Pages/Counter2.razor`:

```razor
@page "/counter-2"
@rendermode InteractiveServer

<PageTitle>Counter 2</PageTitle>

<h1>Counter 2</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
private int currentCount = 0;

private void IncrementCount()
{
currentCount++;
}
}
```

1. Set a breakpoint on the `currentCount++;` line in the `Counter2` component.
1. In the server app, open the statically-rendered `Weather` component (`Components/Pages/Weather.razor`) and set a breakpoint anywhere in the `OnInitializedAsync` method.
1. Press <kbd>F5</kbd> to run the app in the debugger.
1. In the browser, navigate to `Counter2` page at `/counter-2`. Wait a few seconds for the debug proxy to load and run. Select the **Click me** button to hit the breakpoint.
1. In the browser, navigate to the `Weather` page at `/weather`. Wait a few seconds for the debug proxy to load and run. Application execution stops at the breakpoint.
1. Press <kbd>F5</kbd> to continue execution.

Breakpoints are **not** hit during app startup before the debug proxy is running. This includes breakpoints in the `Program` file and breakpoints in the [`OnInitialized{Async}` lifecycle methods](xref:blazor/components/lifecycle#component-initialization-oninitializedasync) of components that are loaded by the first page requested from the app.
Expand All @@ -241,35 +215,9 @@ The example in this section assumes that you've created a Blazor Web App with an
Breakpoints can also be hit in the server project.

1. Stop debugging by selecting the **Stop** button or press <kbd>Shift</kbd>+<kbd>F5</kbd> on the keyboard.
1. Add the following component to the server app. The component adopts the Interactive Server render mode (`InteractiveServer`).

`Components/Pages/Counter2.razor`:

```razor
@page "/counter-2"
@rendermode InteractiveServer

<PageTitle>Counter 2</PageTitle>

<h1>Counter 2</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
private int currentCount = 0;

private void IncrementCount()
{
currentCount++;
}
}
```

1. Set a breakpoint on the `currentCount++;` line in the `Counter2` component.
1. In the server app, open the statically-rendered `Weather` component (`Components/Pages/Weather.razor`) and set a breakpoint anywhere in the `OnInitializedAsync` method.
1. Select the **Start Debugging** button in the UI or press <kbd>F5</kbd> (**Start Debugging**) to run the app in the debugger.
1. In the browser, navigate to the `Counter2` page at `/counter-2`. Wait a few seconds for the debug proxy to load and run. Select the **Click me** button to hit the breakpoint.
1. In the browser, navigate to the `Weather` page at `/weather`. Wait a few seconds for the debug proxy to load and run. Application execution stops at the breakpoint.
1. Select the **Continue** button in the UI or press <kbd>F5</kbd> (**Continue**) to continue execution.

Breakpoints are **not** hit during app startup before the debug proxy is running. This includes breakpoints in the `Program` file and breakpoints in the [`OnInitialized{Async}` lifecycle methods](xref:blazor/components/lifecycle#component-initialization-oninitializedasync) of components that are loaded by the first page requested from the app.
Expand Down Expand Up @@ -437,8 +385,8 @@ The additional options in the following table only apply to **hosted Blazor WebA

*The guidance in this section applies debugging Blazor WebAssembly apps in:*

* *Google Chrome running on Windows or macOS.*
* *Microsoft Edge running on Windows.*
* ***Google Chrome*** *running on Windows or macOS.*
* ***Microsoft Edge** *running on Windows.*

1. Run the app in a command shell with `dotnet watch` (or `dotnet run`).
1. Launch a browser and navigate to the app's URL.
Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/fundamentals/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ Constraint | Example | Example Matches | Invariant<br>culture<br>matching
`decimal` | `{price:decimal}` | `49.99`, `-1,000.01` | Yes
`double` | `{weight:double}` | `1.234`, `-1,001.01e8` | Yes
`float` | `{weight:float}` | `1.234`, `-1,001.01e8` | Yes
`guid` | `{id:guid}` | `CD2C1638-1638-72D5-1638-DEADBEEF1638`, `{CD2C1638-1638-72D5-1638-DEADBEEF1638}` | No
`guid` | `{id:guid}` | `00001111-aaaa-2222-bbbb-3333cccc4444`, `{00001111-aaaa-2222-bbbb-3333cccc4444}` | No
`int` | `{id:int}` | `123456789`, `-123456789` | Yes
`long` | `{ticks:long}` | `123456789`, `-123456789` | Yes
`nonfile` | `{parameter:nonfile}` | Not `BlazorSample.styles.css`, not `favicon.ico` | Yes
Expand Down
10 changes: 5 additions & 5 deletions aspnetcore/blazor/security/blazor-web-app-with-oidc.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ The following <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConn
* Client Id (`{CLIENT ID}`): `00001111-aaaa-2222-bbbb-3333cccc4444`

```csharp
oidcOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/";
oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
```

Expand Down Expand Up @@ -363,7 +363,7 @@ The following <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConn
* Scope configured for weather data from `MinimalApiJwt` (`{API NAME}`): `Weather.Get`

```csharp
oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/4ba4de56-9cef-45d9-83fa-a4c18f9f5f0f/Weather.Get");
oidcOptions.Scope.Add("https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444/Weather.Get");
```

The preceding example pertains to an app registered in a tenant with an AAD B2C tenant type. If the app is registered in an ME-ID tenant, the App ID URI is different, thus the scope is different.
Expand All @@ -390,7 +390,7 @@ The following <xref:Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConn
* Client Id (`{CLIENT ID}`): `00001111-aaaa-2222-bbbb-3333cccc4444`

```csharp
oidcOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/";
oidcOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
oidcOptions.ClientId = "00001111-aaaa-2222-bbbb-3333cccc4444";
```

Expand Down Expand Up @@ -560,13 +560,13 @@ Configure the project in the <xref:Microsoft.AspNetCore.Authentication.JwtBearer
Authority (`{AUTHORITY}`): `https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/` (uses Tenant ID `aaaabbbb-0000-cccc-1111-dddd2222eeee`)

```csharp
jwtOptions.Authority = "https://login.microsoftonline.com/a3942615-d115-4eb7-bc84-9974abcf5064/v2.0/";
jwtOptions.Authority = "https://login.microsoftonline.com/aaaabbbb-0000-cccc-1111-dddd2222eeee/v2.0/";
```

The preceding example pertains to an app registered in a tenant with an AAD B2C tenant type. If the app is registered in an ME-ID tenant, the authority should match the issurer (`iss`) of the JWT returned by the identity provider:

```csharp
jwtOptions.Authority = "https://sts.windows.net/a3942615-d115-4eb7-bc84-9974abcf5064/";
jwtOptions.Authority = "https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/";
```

### Minimal API for weather data
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
> If you don't have the authority to grant admin consent to the tenant in the last step of **API permissions** configuration because consent to use the app is delegated to users, then you must take the following additional steps:
>
> * The app must use a [trusted publisher domain](/entra/identity-platform/howto-configure-publisher-domain).
> * In the **`Server`** app's configuration in the Azure portal, select **Expose an API**. Under **Authorized client applications**, select the button to **Add a client application**. Add the **`Client`** app's Application (client) ID (for example, `4369008b-21fa-427c-abaa-9b53bf58e538`).
> * In the **`Server`** app's configuration in the Azure portal, select **Expose an API**. Under **Authorized client applications**, select the button to **Add a client application**. Add the **`Client`** app's Application (client) ID (for example, `11112222-bbbb-3333-cccc-4444dddd5555`).
4 changes: 2 additions & 2 deletions aspnetcore/blazor/security/includes/troubleshoot-wasm.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,10 +210,10 @@ Example JWT decoded by the tool for an app that authenticates against Azure AAD
"exp": 1610059429,
"nbf": 1610055829,
"ver": "1.0",
"iss": "https://mysiteb2c.b2clogin.com/5cc15ea8-a296-4aa3-97e4-226dcc9ad298/v2.0/",
"iss": "https://mysiteb2c.b2clogin.com/11112222-bbbb-3333-cccc-4444dddd5555/v2.0/",
"sub": "aaaaaaaa-0000-1111-2222-bbbbbbbbbbbb",
"aud": "00001111-aaaa-2222-bbbb-3333cccc4444",
"nonce": "b2641f54-8dc4-42ca-97ea-7f12ff4af871",
"nonce": "bbbb0000-cccc-1111-dddd-2222eeee3333",
"iat": 1610055829,
"auth_time": 1610055822,
"idp": "idp.com",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ The output location specified with the `-o|--output` option creates a project fo

*The guidance in this section covers optionally populating `User.Identity.Name` with the value from the `name` claim.*

The **:::no-loc text="Server":::** app API populates `User.Identity.Name` with the value from the `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name` claim type (for example, `2d64b3da-d9d5-42c6-9352-53d8df33d770@contoso.onmicrosoft.com`).
The **:::no-loc text="Server":::** app API populates `User.Identity.Name` with the value from the `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name` claim type (for example, `aaaabbbb-0000-cccc-1111-dddd2222eeee@contoso.onmicrosoft.com`).

To configure the app to receive the value from the `name` claim type:

Expand Down Expand Up @@ -358,7 +358,7 @@ Example default access token scope:

```csharp
options.ProviderOptions.DefaultAccessTokenScopes.Add(
"https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access");
"https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444/API.Access");
```

For more information, see the following sections of the *Additional scenarios* article:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ The output location specified with the `-o|--output` option creates a project fo

*The guidance in this section covers optionally populating `User.Identity.Name` with the value from the `name` claim.*

The **:::no-loc text="Server":::** app API populates `User.Identity.Name` with the value from the `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name` claim type (for example, `2d64b3da-d9d5-42c6-9352-53d8df33d770@contoso.onmicrosoft.com`).
The **:::no-loc text="Server":::** app API populates `User.Identity.Name` with the value from the `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name` claim type (for example, `bbbb0000-cccc-1111-dddd-2222eeee3333@contoso.onmicrosoft.com`).

To configure the app to receive the value from the `name` claim type:

Expand Down Expand Up @@ -464,7 +464,7 @@ Instead of the App ID URI matching the format `api://{SERVER API APP CLIENT ID O

```csharp
options.ProviderOptions.DefaultAccessTokenScopes
.Add("https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access");
.Add("https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444/API.Access");
```

In the preceding scope, the App ID URI/audience is the `https://contoso.onmicrosoft.com/00001111-aaaa-2222-bbbb-3333cccc4444` portion of the value, which doesn't include a trailing slash (`/`) and doesn't include the scope name (`API.Access`).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ When working with the default directory, follow the guidance in [Add app roles t
],
"description": "Administrators manage developers.",
"displayName": "Admin",
"id": "584e483a-7101-404b-9bb1-83bf9463e335",
"id": "{ADMIN GUID}",
"isEnabled": true,
"lang": null,
"origin": "Application",
Expand All @@ -554,7 +554,7 @@ When working with the default directory, follow the guidance in [Add app roles t
],
"description": "Developers write code.",
"displayName": "Developer",
"id": "82770d35-2a93-4182-b3f5-3d7bfe9dfe46",
"id": "{DEVELOPER GUID}",
"isEnabled": true,
"lang": null,
"origin": "Application",
Expand All @@ -563,8 +563,7 @@ When working with the default directory, follow the guidance in [Add app roles t
],
```

> [!NOTE]
> You can generate GUIDs with an [online GUID generator program (Google search result for "guid generator")](https://www.google.com/search?q=guid+generator).
For the `{ADMIN GUID}` and `{DEVELOPER GUID}` placeholders in the preceding example, you can generate GUIDs with an [online GUID generator (Google search result for "guid generator")](https://www.google.com/search?q=guid+generator).

To assign a role to a user (or group if you have a Premium tier Azure account):

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ Take either of the following approaches add app roles in ME-ID:
],
"description": "Administrators manage developers.",
"displayName": "Admin",
"id": "584e483a-7101-404b-9bb1-83bf9463e335",
"id": "{ADMIN GUID}",
"isEnabled": true,
"lang": null,
"origin": "Application",
Expand All @@ -294,7 +294,7 @@ Take either of the following approaches add app roles in ME-ID:
],
"description": "Developers write code.",
"displayName": "Developer",
"id": "82770d35-2a93-4182-b3f5-3d7bfe9dfe46",
"id": "{DEVELOPER GUID}",
"isEnabled": true,
"lang": null,
"origin": "Application",
Expand All @@ -303,8 +303,7 @@ Take either of the following approaches add app roles in ME-ID:
],
```

> [!NOTE]
> You can generate GUIDs with an [online GUID generator program (Google search result for "guid generator")](https://www.google.com/search?q=guid+generator).
For the `{ADMIN GUID}` and `{DEVELOPER GUID}` placeholders in the preceding example, you can generate GUIDs with an [online GUID generator (Google search result for "guid generator")](https://www.google.com/search?q=guid+generator).

To assign a role to a user (or group if you have a Premium tier Azure account):

Expand Down
2 changes: 1 addition & 1 deletion aspnetcore/blazor/tutorials/movie-database-app/part-4.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ For local development, configuration obtains the database connection string from

The following is an example connection string:

> :::no-loc text="Server=(localdb)\\mssqllocaldb;Database=BlazorWebAppMoviesContext-c347f669-bddf-56a3-a32e-7fe010306593;Trusted_Connection=True;MultipleActiveResultSets=true":::
> :::no-loc text="Server=(localdb)\\mssqllocaldb;Database=BlazorWebAppMoviesContext-00001111-aaaa-2222-bbbb-3333cccc4444;Trusted_Connection=True;MultipleActiveResultSets=true":::

When the app is deployed to a test/staging or production server, securely store the connection string outside of the project's configuration files.

Expand Down
Loading
Loading