diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index 53245c6fe6e4..9206b88cee83 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -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 } ] } diff --git a/aspnetcore/blazor/blazor-server-ef-core.md b/aspnetcore/blazor/blazor-ef-core.md similarity index 75% rename from aspnetcore/blazor/blazor-server-ef-core.md rename to aspnetcore/blazor/blazor-ef-core.md index 96e9b70799b8..e001cb34f194 100644 --- a/aspnetcore/blazor/blazor-server-ef-core.md +++ b/aspnetcore/blazor/blazor-ef-core.md @@ -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"::: @@ -58,13 +64,13 @@ The grid, add, and view components use the "context-per-operation" pattern, wher ## Database access -EF Core relies on a 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 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. isn't thread safe and isn't designed for concurrent use. The existing lifetimes are inappropriate for these reasons: +EF Core relies on a 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 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. 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: @@ -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"::: @@ -150,7 +162,7 @@ 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` is injected into the component: +In the home page of the sample app, `IDbContextFactory` is injected into the component: ```razor @inject IDbContextFactory DbFactory @@ -158,7 +170,13 @@ In `Pages/Index.razor` of the [sample app](https://github.com/dotnet/blazor-samp 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"::: @@ -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 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 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 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 and inject the factory as shown in the `EditContact` component (`Pages/EditContact.razor`): + +:::moniker-end ```razor @implements IDisposable @@ -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"::: @@ -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"::: diff --git a/aspnetcore/blazor/fundamentals/dependency-injection.md b/aspnetcore/blazor/fundamentals/dependency-injection.md index 597dba6034c7..de0a881e6233 100644 --- a/aspnetcore/blazor/fundamentals/dependency-injection.md +++ b/aspnetcore/blazor/fundamentals/dependency-injection.md @@ -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 - - -For more information, see . +For more information, see . ## Detect client-side transient disposables diff --git a/aspnetcore/toc.yml b/aspnetcore/toc.yml index 1436e84a4b39..888368ca047a 100644 --- a/aspnetcore/toc.yml +++ b/aspnetcore/toc.yml @@ -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 diff --git a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md index da43c609f777..d3c787e389eb 100644 --- a/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md +++ b/aspnetcore/whats-new/dotnet-AspNetCore.Docs-mod5.md @@ -29,7 +29,7 @@ Welcome to what's new in the ASP.NET Core docs for May 2023. This article lists - Components overview versioning - - Expand lead-in coverage on app base path - - Blazor SignalR fundamentals article versioning -- - Blazor Server EF Core article versioning +- - Blazor Server EF Core article versioning - - Blazor article versioning - - Blazor article versioning - - Subpath scenario for multiple hosted WASM apps