Skip to content

Commit

Permalink
Merge pull request #30403 from dotnet/main
Browse files Browse the repository at this point in the history
Merge to Live
  • Loading branch information
Rick-Anderson authored Sep 20, 2023
2 parents 00387ca + 7eb5a6b commit da8327c
Show file tree
Hide file tree
Showing 32 changed files with 236 additions and 133 deletions.
5 changes: 5 additions & 0 deletions .openpublishing.redirection.json
Original file line number Diff line number Diff line change
Expand Up @@ -1252,6 +1252,11 @@
"source_path": "aspnetcore/blazor/tutorials/signalr-blazor-preview.md",
"redirect_url": "/aspnet/core/blazor/tutorials/signalr-blazor",
"redirect_document_id": false
},
{
"source_path": "aspnetcore/blazor/blazor-server-ef-core.md",
"redirect_url": "/aspnet/core/blazor/blazor-ef-core",
"redirect_document_id": false
}
]
}
Original file line number Diff line number Diff line change
@@ -1,33 +1,39 @@
---
title: ASP.NET Core Blazor Server with Entity Framework Core (EF Core)
title: ASP.NET Core Blazor with Entity Framework Core (EF Core)
author: guardrex
description: Learn how to use Entity Framework Core (EF Core) in Blazor Server apps.
description: Learn how to use Entity Framework Core (EF Core) in Blazor apps.
monikerRange: '>= aspnetcore-3.1'
ms.author: jeliknes
ms.custom: mvc
ms.date: 03/27/2023
uid: blazor/blazor-server-ef-core
uid: blazor/blazor-ef-core
---
# ASP.NET Core Blazor Server with Entity Framework Core (EF Core)
# ASP.NET Core Blazor with Entity Framework Core (EF Core)

[!INCLUDE[](~/includes/not-latest-version.md)]

This article explains how to use [Entity Framework Core (EF Core)](/ef/core/) in Blazor Server apps.
This article explains how to use [Entity Framework Core (EF Core)](/ef/core/) in server-side Blazor apps.

Blazor Server is a stateful app framework. The app maintains an ongoing connection to the server, and the user's state is held in the server's memory in a *circuit*. One example of user state is data held in [dependency injection (DI)](xref:fundamentals/dependency-injection) service instances that are scoped to the circuit. The unique application model that Blazor Server provides requires a special approach to use Entity Framework Core.
Server-side Blazor is a stateful app framework. The app maintains an ongoing connection to the server, and the user's state is held in the server's memory in a *circuit*. One example of user state is data held in [dependency injection (DI)](xref:fundamentals/dependency-injection) service instances that are scoped to the circuit. The unique application model that Blazor provides requires a special approach to use Entity Framework Core.

> [!NOTE]
> This article addresses EF Core in Blazor Server apps. Blazor WebAssembly apps run in a WebAssembly sandbox that prevents most direct database connections. Running EF Core in Blazor WebAssembly is beyond the scope of this article.
> This article addresses EF Core in server-side Blazor apps. Blazor WebAssembly apps run in a WebAssembly sandbox that prevents most direct database connections. Running EF Core in Blazor WebAssembly is beyond the scope of this article.
## Sample app

The sample app was built as a reference for Blazor Server apps that use EF Core. The sample app includes a grid with sorting and filtering, delete, add, and update operations. The sample demonstrates use of EF Core to handle optimistic concurrency.
The sample app was built as a reference for server-side Blazor apps that use EF Core. The sample app includes a grid with sorting and filtering, delete, add, and update operations. The sample demonstrates use of EF Core to handle optimistic concurrency.

[View or download sample code](https://github.com/dotnet/blazor-samples) ([how to download](xref:index#how-to-download-a-sample))

The sample uses a local [SQLite](https://www.sqlite.org/index.html) database so that it can be used on any platform. The sample also configures database logging to show the SQL queries that are generated. This is configured in `appsettings.Development.json`:

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

:::code language="json" source="~/../blazor-samples/8.0/BlazorWebAppEFCore/appsettings.Development.json" highlight="8":::

:::moniker-end

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

:::code language="json" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/appsettings.Development.json" highlight="8":::

Expand Down Expand Up @@ -58,13 +64,13 @@ The grid, add, and view components use the "context-per-operation" pattern, wher
## 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 by default. In Blazor Server 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:
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 by default. 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:

* **Singleton** shares state across all users of the app and leads to inappropriate concurrent use.
* **Scoped** (the default) poses a similar issue between components for the same user.
* **Transient** results in a new instance per request; but as components can be long-lived, this results in a longer-lived context than may be intended.

The following recommendations are designed to provide a consistent approach to using EF Core in Blazor Server apps.
The following recommendations are designed to provide a consistent approach to using EF Core in server-side Blazor apps.

* By default, consider using one context per operation. The context is designed for fast, low overhead instantiation:

Expand Down Expand Up @@ -124,7 +130,13 @@ In the preceding factory:

The following example configures [SQLite](https://www.sqlite.org/index.html) and enables data logging. The code uses an extension method (`AddDbContextFactory`) to configure the database factory for DI and provide default options:

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

:::code language="csharp" source="~/../blazor-samples/8.0/BlazorWebAppEFCore/Program.cs" id="snippet1":::

:::moniker-end

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

:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Program.cs" id="snippet1":::

Expand All @@ -150,15 +162,21 @@ The following example configures [SQLite](https://www.sqlite.org/index.html) and

The factory is injected into components and used to create new `DbContext` instances.

In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samples/blob/main/7.0/BlazorServerEFCoreSample/Pages/Index.razor), `IDbContextFactory<ContactContext>` is injected into the component:
In the home page of the sample app, `IDbContextFactory<ContactContext>` is injected into the component:

```razor
@inject IDbContextFactory<ContactContext> DbFactory
```

A `DbContext` is created using the factory (`DbFactory`) to delete a contact in the `DeleteContactAsync` method:

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

:::code language="csharp" source="~/../blazor-samples/8.0/BlazorWebAppEFCore/Components/Pages/Home.razor" id="snippet1":::

:::moniker-end

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

:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Pages/Index.razor" id="snippet1":::

Expand All @@ -182,13 +200,35 @@ A `DbContext` is created using the factory (`DbFactory`) to delete a contact in

:::moniker-end

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

> [!NOTE]
> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Home` component (`Components/Pages/Home.razor`) in the sample app.
:::moniker-end

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

> [!NOTE]
> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the [sample app](https://github.com/dotnet/blazor-samples/blob/main/6.0/BlazorServerEFCoreSample/Pages/Index.razor).
> `Filters` is an injected `IContactFilters`, and `Wrapper` is a [component reference](xref:blazor/components/index#capture-references-to-components) to the `GridWrapper` component. See the `Index` component (`Pages/Index.razor`) in the sample app.
:::moniker-end

## Scope to the component lifetime

You may wish to create a <xref:Microsoft.EntityFrameworkCore.DbContext> that exists for the lifetime of a component. This allows you to use it as a [unit of work](https://martinfowler.com/eaaCatalog/unitOfWork.html) and take advantage of built-in features, such as change tracking and concurrency resolution.
You can use the factory to create a context and track it for the lifetime of the component. First, implement <xref:System.IDisposable> and inject the factory as shown in `Pages/EditContact.razor`:

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

You can use the factory to create a context and track it for the lifetime of the component. First, implement <xref:System.IDisposable> and inject the factory as shown in the `EditContact` component (`Components/Pages/EditContact.razor`):

:::moniker-end

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

You can use the factory to create a context and track it for the lifetime of the component. First, implement <xref:System.IDisposable> and inject the factory as shown in the `EditContact` component (`Pages/EditContact.razor`):

:::moniker-end

```razor
@implements IDisposable
Expand All @@ -197,7 +237,13 @@ You can use the factory to create a context and track it for the lifetime of the

The sample app ensures the context is disposed when the component is disposed:

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

:::code language="csharp" source="~/../blazor-samples/8.0/BlazorWebAppEFCore/Components/Pages/EditContact.razor" id="snippet1":::

:::moniker-end

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

:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Pages/EditContact.razor" id="snippet1":::

Expand All @@ -223,7 +269,13 @@ The sample app ensures the context is disposed when the component is disposed:

Finally, [`OnInitializedAsync`](xref:blazor/components/lifecycle) is overridden to create a new context. In the sample app, [`OnInitializedAsync`](xref:blazor/components/lifecycle) loads the contact in the same method:

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

:::code language="csharp" source="~/../blazor-samples/8.0/BlazorWebAppEFCore/Components/Pages/EditContact.razor" id="snippet2":::

:::moniker-end

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

:::code language="csharp" source="~/../blazor-samples/7.0/BlazorServerEFCoreSample/Pages/EditContact.razor" id="snippet2":::

Expand Down
4 changes: 1 addition & 3 deletions aspnetcore/blazor/fundamentals/dependency-injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,9 +405,7 @@ In spite of the scoped service registration in the `Program` file and the longev

## Use of an Entity Framework Core (EF Core) DbContext from DI

<!-- UPDATE 8.0 The UID will change -->

For more information, see <xref:blazor/blazor-server-ef-core>.
For more information, see <xref:blazor/blazor-ef-core>.

## Detect client-side transient disposables

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ The following code uses <xref:Microsoft.AspNetCore.Http.IFormFile> and <xref:Mic

Authenticated file upload requests are supported using an [Authorization header](https://developer.mozilla.org/docs/Web/HTTP/Headers/Authorization), a [client certificate](/aspnet/core/security/authentication/certauth), or a cookie header.

There is no built-in support for [antiforgery](/aspnet/core/security/anti-request-forgery?view=aspnetcore-7.0&preserve-view=true#anti7). However, it can be implemented using the [`IAntiforgery` service](/aspnet/core/security/anti-request-forgery?view=aspnetcore-7.0&preserve-view=true#antimin7).
There is no built-in support for [antiforgery](/aspnet/core/security/anti-request-forgery?view=aspnetcore-7.0&preserve-view=true#anti7) in ASP.NET Core 7.0. [Antiforgery is available in ASP.NET Core 8.0](/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-8.0&preserve-view=true#bind8) and later. However, it can be implemented using the [`IAntiforgery` service](/aspnet/core/security/anti-request-forgery?view=aspnetcore-7.0&preserve-view=true#antimin7).

<a name="bindar"></a>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ The following code uses <xref:Microsoft.AspNetCore.Http.IFormFile> and <xref:Mic

Authenticated file upload requests are supported using an [Authorization header](https://developer.mozilla.org/docs/Web/HTTP/Headers/Authorization), a [client certificate](/aspnet/core/security/authentication/certauth), or a cookie header.

There is no built-in support for [antiforgery](/aspnet/core/security/anti-request-forgery?view=aspnetcore-7.0&preserve-view=true#anti7). However, it can be implemented using the [`IAntiforgery` service](/aspnet/core/security/anti-request-forgery?view=aspnetcore-7.0&preserve-view=true#antimin7).
<a name="bind8"></a>

#### Binding to forms with IFormCollection, IFormFile, and IFormFileCollection

Expand Down
5 changes: 3 additions & 2 deletions aspnetcore/fundamentals/minimal-apis/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ title: Authentication and authorization in minimal APIs
author: captainsafia
description: Learn how to configure authentication and authorization in minimal API apps
ms.author: safia
content_well_notification: AI-contribution
monikerRange: '>= aspnetcore-7.0'
ms.date: 10/17/2022
ms.date: 9/17/2023
uid: fundamentals/minimal-apis/security
---

Expand Down Expand Up @@ -41,7 +42,7 @@ By default, the [`WebApplication`](/dotnet/api/microsoft.aspnetcore.builder.weba

In some cases, such as controlling middleware order, it's necessary to explicitly register authentication and authorization. In the following sample, the authentication middleware runs _after_ the CORS middleware has run. For more information on middlewares and this automatic behavior, see [Middleware in Minimal API apps](/aspnet/core/fundamentals/minimal-apis/middleware).

:::code language="csharp" source="~/fundamentals/minimal-apis/security/7.0-samples/MinApiAuth/MinApiAuth/Program.cs" id="snippet_after" highlight="8-10":::
:::code language="csharp" source="~/fundamentals/minimal-apis/security/7.0-samples/MinApiAuth/MinApiAuth/Program.cs" id="snippet_after" highlight="9-11":::

### Configuring authentication strategy

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#define GREET // FIRST JWT1 JWT2 AFTER LOCAL GREET
#define AFTER // FIRST JWT1 JWT2 AFTER LOCAL GREET
#if NEVER
#elif FIRST
// <snippet_1>
Expand Down Expand Up @@ -33,6 +33,7 @@
// <snippet_after>
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors();
builder.Services.AddAuthentication().AddJwtBearer();
builder.Services.AddAuthorization();

Expand Down
3 changes: 2 additions & 1 deletion aspnetcore/fundamentals/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ title: Routing in ASP.NET Core
author: rick-anderson
description: Discover how ASP.NET Core routing is responsible for matching HTTP requests and dispatching to executable endpoints.
monikerRange: '>= aspnetcore-3.1'
content_well_notification: AI-contribution
ms.author: riande
ms.custom: mvc
ms.date: 6/14/2023
Expand Down Expand Up @@ -485,7 +486,7 @@ Regular expressions provide much more control over their matching behavior.

<a name="greedy"></a>

Greedy matching, also known as [lazy matching](https://wikipedia.org/wiki/Regular_expression#Lazy_matching), matches the largest possible string. Non-greedy matches the smallest possible string.
Greedy matching, also known as *maximal matching* attempts to find the longest possible match in the input text that satisfies the [regex](/dotnet/standard/base-types/regular-expressions) pattern. Non-greedy matching, also known as *lazy matching*, seeks the shortest possible match in the input text that satisfies the regex pattern.

[!INCLUDE[](~/includes/routeSlash.md)]

Expand Down
7 changes: 7 additions & 0 deletions aspnetcore/includes/vscode-trust-authors-add-assets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Visual Studio Code might display a dialog box that asks: **Do you trust the authors of the files in this folder?**

* If you trust all files in the parent folder, select **Trust the authors of all files in the parent folder**.
* Select **Yes, I trust the authors** since the project folder has files generated by .NET.
* When Visual Studio Code requests that you add assets to build and debug the project, select **Yes**. If Visual Studio Code doesn't offer to add build and debug assets, select **View** > **Command Palette** and type "`.NET`" into the search box. From the list of commands, select the `.NET: Generate Assets for Build and Debug` command.

Visual Studio Code adds a `.vscode` folder with generated `launch.json` and `tasks.json` files.
5 changes: 3 additions & 2 deletions aspnetcore/security/app-secrets.md
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,8 @@ See [this GitHub issue](https://github.com/dotnet/AspNetCore.Docs/issues/27611).

## Additional resources

* See [this issue](https://github.com/dotnet/AspNetCore.Docs/issues/16328) for information on accessing user secrets from IIS.

* See [this issue](https://github.com/dotnet/AspNetCore.Docs/issues/30378) and [this issue](https://github.com/dotnet/AspNetCore.Docs/issues/16328) for information on accessing user secrets from IIS.
* <xref:fundamentals/configuration/index>
* <xref:security/key-vault-configuration>

Expand Down Expand Up @@ -558,7 +559,7 @@ See [this GitHub issue](https://github.com/dotnet/AspNetCore.Docs/issues/27611).

## Additional resources

* See [this issue](https://github.com/dotnet/AspNetCore.Docs/issues/16328) for information on accessing user secrets from IIS.
* See [this issue](https://github.com/dotnet/AspNetCore.Docs/issues/30378) and [this issue](https://github.com/dotnet/AspNetCore.Docs/issues/16328) for information on accessing user secrets from IIS.
* <xref:fundamentals/configuration/index>
* <xref:security/key-vault-configuration>

Expand Down
4 changes: 2 additions & 2 deletions aspnetcore/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -583,8 +583,8 @@ items:
uid: blazor/host-and-deploy/webassembly-deployment-layout
- name: Multiple hosted WebAssembly apps
uid: blazor/host-and-deploy/multiple-hosted-webassembly
- name: Blazor Server and EF Core
uid: blazor/blazor-server-ef-core
- name: Blazor with EF Core
uid: blazor/blazor-ef-core
- name: Advanced scenarios
uid: blazor/advanced-scenarios
- name: Client-side development
Expand Down
Loading

0 comments on commit da8327c

Please sign in to comment.