Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge to Live #30403

Merged
merged 8 commits into from
Sep 20, 2023
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
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
2 changes: 1 addition & 1 deletion aspnetcore/fundamentals/target-aspnetcore.md
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ For example, to add the web API client:
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
Expand Down
23 changes: 22 additions & 1 deletion aspnetcore/grpc/aspnetcore.md
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,28 @@ For more information on enabling HTTP/2 and TLS with Kestrel, see [Kestrel endpo

:::moniker-end

## Host gRPC in non-ASP.NET Core projects

An ASP.NET Core gRPC server is typically created from the gRPC template. The project file created by the template uses `Microsoft.NET.SDK.Web` as the SDK:

[!code-xml[](~/grpc/aspnetcore/Server-web.csproj?highlight=1)]

The `Microsoft.NET.SDK.Web` SDK value automatically adds a reference to the ASP.NET Core framework. The reference allows the app to use ASP.NET Core types required to host a server.

You can add a gRPC server to non-ASP.NET Core projects with the following project file settings:

[!code-xml[](~/grpc/aspnetcore/Server.csproj?highlight=1,7)]

The preceding project file:

* Adds a framework reference to `Microsoft.AspNetCore.App`.
* The framework reference allows non-ASP.NET Core apps, such as Windows Services, WPF apps, or WinForms apps to use ASP.NET Core APIs.
* The app can now use ASP.NET Core APIs to start an ASP.NET Core server.
* Adds gRPC requirements:
* NuGet package reference to [`Grpc.AspNetCore`](https://www.nuget.org/packages/Grpc.AspNetCore).
* `.proto` file.

For more information about using the `Microsoft.AspNetCore.App` framework reference, see [Use the ASP.NET Core shared framework](xref:fundamentals/target-aspnetcore#use-the-aspnet-core-shared-framework).
## Integration with ASP.NET Core APIs

gRPC services have full access to the ASP.NET Core features such as [Dependency Injection](xref:fundamentals/dependency-injection) (DI) and [Logging](xref:fundamentals/logging/index). For example, the service implementation can resolve a logger service from the DI container via the constructor:
Expand All @@ -271,7 +293,6 @@ The gRPC API provides access to some HTTP/2 message data, such as the method, ho

[!code-csharp[](~/grpc/aspnetcore/sample/GrcpService/GreeterService2.cs?highlight=6-7&name=snippet)]


## Additional resources

* <xref:tutorials/grpc/grpc-start>
Expand Down
8 changes: 8 additions & 0 deletions aspnetcore/grpc/aspnetcore/Server-web.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.47.0" />
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>

</Project>
10 changes: 10 additions & 0 deletions aspnetcore/grpc/aspnetcore/Server.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<ItemGroup>
<PackageReference Include="Grpc.AspNetCore" Version="2.47.0" />
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />

<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

</Project>
Loading