You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Entity Framework Core](https://docs.microsoft.com/ef/core/) is a powerful object-relational mapping framework that has become a staple when working with SQL-based Databases in .NET Core applications.
5
+
[Entity Framework Core](https://docs.microsoft.com/ef/core/) is a powerful object-relational mapping framework that has become a staple when working with SQL-based databases in .NET Core applications.
6
6
7
-
When working with Entity Framework Core's [DbContext](https://docs.microsoft.com/dotnet/api/system.data.entity.dbcontext), it is most commonly registered as a scoped service.
If you have read our [guidance on dependency injection](/docs/hotchocolate/v14/server/dependency-injection#resolver-injection) you might be inclined to simply inject your `DbContext` using the `HotChocolate.ServiceAttribute`.
9
+
When using the [default scope](/docs/hotchocolate/v14/server/dependency-injection#default-scope) for queries, each execution of a query that accepts a scoped DbContext will receive a **separate** instance, avoiding [threading issues](https://learn.microsoft.com/en-gb/ef/core/dbcontext-configuration/#avoiding-dbcontext-threading-issues).
While this is usually the correct way to inject services and it may appear to work initially, it has a fatal flaw: Entity Framework Core doesn't support [multiple parallel operations being run on the same context instance](https://docs.microsoft.com/ef/core/miscellaneous/async).
24
-
25
-
Lets take a look at an example to understand why this can lead to issues. Both the `foo` and `bar` field in the below query are backed by a resolver that injects a scoped `DbContext` instance and performs a database query using it.
26
-
27
-
```graphql
28
-
{
29
-
foo
30
-
bar
31
-
}
12
+
publicstaticasyncTask<Book?>GetBookByIdAsync(
13
+
ApplicationDbContextdbContext) =>// ...
32
14
```
33
15
34
-
Since Hot Chocolate parallelizes the execution of query fields, and both of the resolvers will receive the same scoped `DbContext` instance, two database queries are likely to be ran through this scoped `DbContext` instance in parallel. This will then lead to one of the following exceptions being thrown:
35
-
36
-
-`A second operation started on this context before a previous operation completed.`
37
-
-`Cannot access a disposed object.`
38
-
39
-
# Resolver injection of a DbContext
40
-
41
-
In order to ensure that resolvers do not access the same scoped `DbContext` instance in parallel, you can inject it using the `ServiceKind.Synchronized`.
16
+
When using the [default scope](/docs/hotchocolate/v14/server/dependency-injection#default-scope) for mutations, each execution of a mutation that accepts a scoped DbContext will receive the **same** request-scoped instance, as mutations are executed sequentially.
[Learn more about `ServiceKind.Synchronized`](/docs/hotchocolate/v14/server/dependency-injection#servicekindsynchronized)
24
+
See the [Dependency Injection](/docs/hotchocolate/v14/server/dependency-injection) documentation for more details.
50
25
51
-
Since this is a lot of code to write, just to inject a `DbContext`, you can use [`RegisterDbContext<T>`](#registerdbcontext) to simplify the injection.
26
+
> Warning: Changing the default scope for queries will likely result in the error "A second operation started on this context before a previous operation completed", since Entity Framework Core does not support multiple parallel operations being run on the same DbContext instance.
52
27
53
-
# RegisterDbContext
28
+
# Using a DbContext factory
54
29
55
-
In order to simplify the injection of a `DbContext` we have introduced a method called `RegisterDbContext<T>`, similar to the [`RegisterService<T>`](/docs/hotchocolate/v14/server/dependency-injection#registerservice) method for regular services. This method is part of the `HotChocolate.Data.EntityFramework`package, which you'll have to install.
30
+
In order to use a DbContext factory, you need to register your DbContext with Hot Chocolate. To do so, an additional package needs to be installed:
Once installed you can simply call the `RegisterDbContext<T>` method on the `IRequestExecutorBuilder`. The Hot Chocolate Resolver Compiler will then take care of correctly injecting your scoped `DbContext` instance into your resolvers and also ensuring that the resolvers using it are never run in parallel.
34
+
Once installed, you can simply call the `RegisterDbContextFactory<T>` method on the `IRequestExecutorBuilder`. The Hot Chocolate Resolver Compiler will then take care of correctly injecting your DbContext instance into your resolvers.
> Warning: You still have to register your `DbContext` in the actual dependency injectioncontainer, bycalling `services.AddDbContext<T>`. `RegisterDbContext<T>` onitsownisnotenough.
Thisinjectionmechanismwillresolvethescoped `DbContext` fromaresolver-scoped [`IServiceScope`](https://docs.microsoft.com/dotnet/api/microsoft.extensions.dependencyinjection.iservicescope). It behaves in the same fashion as [ServiceKind.Resolver](/docs/hotchocolate/v14/server/dependency-injection#servicekindresolver) does for regular services. Since a different `DbContext` instance is resolved for each resolver invocation, Hot Chocolate can parallelize the execution of resolvers using this `DbContext`.
When injecting a `DbContext` using the `DbContextKind.Pool`, Hot Chocolate will retrieve one `DbContext` instance from the pool for each invocation of a resolver. Once the resolver has finishedexecuting, theinstancewillbereturnedtothepool.
Take a look at the implementation-first or code-first example.
126
95
127
-
# Working with a pooled DbContext
96
+
</Schema>
97
+
</ExampleTabs>
128
98
129
-
Ifyouhaveregisteredyour `DbContext` using [DbContextKind.Pooled](#dbcontextkindpooled) youareonyourwaytosqueezethemostperformanceoutofyourGraphQLserver, butunfortunatelyitalsochangeshowyouhavetousethe `DbContext`.
99
+
> Warning: As shown above, you still need to add your `DbContextFactory` to the dependency injection container, by calling `AddDbContextFactory<T>` or `AddPooledDbContextFactory<T>`. `RegisterDbContextFactory<T>` on its own is not enough.
When you use a DbContext factory, you need to access your DbContext differently if it is not being directly injected into a resolver. In the following sections we will take a look at some of the changes that you need to make.
> Warning: It is important that you dispose the `DbContext` to return it to the pool. In the above example we are using `await using` to dispose the `DbContext` after it is no longer required.
141
+
> Warning: It is important that you dispose the DbContext. In the example above we use the `using`statement to dispose the DbContext after it is no longer required.
@@ -208,4 +180,49 @@ public class FooService : IAsyncDisposable
208
180
}
209
181
```
210
182
211
-
> Warning: It is important that you dispose the `DbContext` to return it to thepool, onceyourtransientserviceisbeingdisposed. Intheaboveexampleweareimplementing `IAsyncDisposable` anddisposingthecreated `DbContext` inthe `DisposeAsync` method. Thismethodwillbeinvokedbythedependencyinjectionsystem.
Take a look at the implementation-first or code-first example.
224
+
225
+
</Schema>
226
+
</ExampleTabs>
227
+
228
+
> Warning: It is important that you dispose the DbContext when your service is being disposed. In the example above we are implementing `IAsyncDisposable` and disposing the created DbContext in the `DisposeAsync` method. This method will be invoked by the dependency injection system.
0 commit comments