Skip to content

Commit 262d0f2

Browse files
author
Bart Koelman
authored
Fixed support for using multiple DbContexts (#836)
* Fixed support for using multiple DbContexts * Removed the need for adding custom resolvers.
1 parent 4ebc9ce commit 262d0f2

38 files changed

+654
-264
lines changed

.gitignore

+4-1
Original file line numberDiff line numberDiff line change
@@ -363,4 +363,7 @@ MigrationBackup/
363363
.ionide/
364364

365365
# Fody - auto-generated XML schema
366-
FodyWeavers.xsd
366+
FodyWeavers.xsd
367+
368+
# Sqlite example databases
369+
*.db

JsonApiDotnetCore.sln

+30
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GettingStarted", "src\Examp
4343
EndProject
4444
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "test\IntegrationTests\IntegrationTests.csproj", "{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}"
4545
EndProject
46+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiDbContextExample", "src\Examples\MultiDbContextExample\MultiDbContextExample.csproj", "{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}"
47+
EndProject
48+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiDbContextTests", "test\MultiDbContextTests\MultiDbContextTests.csproj", "{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}"
49+
EndProject
4650
Global
4751
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4852
Debug|Any CPU = Debug|Any CPU
@@ -185,6 +189,30 @@ Global
185189
{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}.Release|x64.Build.0 = Release|Any CPU
186190
{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}.Release|x86.ActiveCfg = Release|Any CPU
187191
{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}.Release|x86.Build.0 = Release|Any CPU
192+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
193+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|Any CPU.Build.0 = Debug|Any CPU
194+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|x64.ActiveCfg = Debug|Any CPU
195+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|x64.Build.0 = Debug|Any CPU
196+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|x86.ActiveCfg = Debug|Any CPU
197+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|x86.Build.0 = Debug|Any CPU
198+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|Any CPU.ActiveCfg = Release|Any CPU
199+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|Any CPU.Build.0 = Release|Any CPU
200+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|x64.ActiveCfg = Release|Any CPU
201+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|x64.Build.0 = Release|Any CPU
202+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|x86.ActiveCfg = Release|Any CPU
203+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|x86.Build.0 = Release|Any CPU
204+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
205+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
206+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|x64.ActiveCfg = Debug|Any CPU
207+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|x64.Build.0 = Debug|Any CPU
208+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|x86.ActiveCfg = Debug|Any CPU
209+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|x86.Build.0 = Debug|Any CPU
210+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
211+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|Any CPU.Build.0 = Release|Any CPU
212+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x64.ActiveCfg = Release|Any CPU
213+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x64.Build.0 = Release|Any CPU
214+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x86.ActiveCfg = Release|Any CPU
215+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x86.Build.0 = Release|Any CPU
188216
EndGlobalSection
189217
GlobalSection(SolutionProperties) = preSolution
190218
HideSolutionNode = FALSE
@@ -201,6 +229,8 @@ Global
201229
{8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
202230
{21D27239-138D-4604-8E49-DCBE41BCE4C8} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
203231
{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
232+
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
233+
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
204234
EndGlobalSection
205235
GlobalSection(ExtensibilityGlobals) = postSolution
206236
SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4}

docs/usage/extensibility/repositories.md

+20-47
Original file line numberDiff line numberDiff line change
@@ -45,62 +45,35 @@ public class ArticleRepository : EntityFrameworkCoreRepository<Article>
4545

4646
## Multiple DbContexts
4747

48-
If you need to use multiple Entity Framework Core DbContexts, first add each DbContext to the ResourceGraphBuilder.
49-
50-
Then, create an implementation of IDbContextResolver for each context.
51-
52-
Register each of the new IDbContextResolver implementations in Startup.cs.
53-
54-
You can then create a general repository for each context and inject it per resource type. This example shows a single DbContextARepository for all entities that are members of DbContextA.
55-
56-
Then inject the repository for the correct entity, in this case Foo is a member of DbContextA.
48+
If you need to use multiple Entity Framework Core DbContexts, first create a repository for each context and inject its typed resolver.
49+
This example shows a single `DbContextARepository` for all entities that are members of `DbContextA`.
5750

5851
```c#
59-
// Startup.cs
60-
services.AddJsonApi(resources: builder =>
61-
{
62-
// Add both contexts using the builder
63-
builder.AddDbContext<DbContextA>();
64-
builder.AddDbContext<DbContextB>();
65-
});
66-
67-
public class DbContextAResolver : IDbContextResolver
52+
public class DbContextARepository<TResource> : EntityFrameworkCoreRepository<TResource>
53+
where TResource : class, IIdentifiable<int>
6854
{
69-
private readonly DbContextA _context;
70-
71-
public DbContextAResolver(DbContextA context)
72-
{
73-
_context = context;
74-
}
75-
76-
public DbContext GetContext()
55+
public DbContextARepository(ITargetedFields targetedFields, DbContextResolver<DbContextA> contextResolver,
56+
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
57+
IResourceGraph resourceGraph, IGenericServiceFactory genericServiceFactory,
58+
IResourceFactory resourceFactory, IEnumerable<IQueryConstraintProvider> constraintProviders,
59+
ILoggerFactory loggerFactory)
60+
: base(targetedFields, contextResolver, resourceGraph, genericServiceFactory, resourceFactory,
61+
constraintProviders, loggerFactory)
7762
{
78-
return _context;
7963
}
8064
}
65+
```
8166

67+
Then register the added types and use the non-generic overload of `AddJsonApi` to add their resources to the graph.
8268

83-
// Startup.cs
84-
services.AddScoped<DbContextAResolver>();
85-
services.AddScoped<DbContextBResolver>();
86-
69+
```c#
70+
// In Startup.ConfigureServices
8771
88-
public class DbContextARepository<TResource> : EntityFrameworkCoreRepository<TResource>
89-
where TResource : class, IIdentifiable<int>
90-
{
91-
public DbContextARepository(
92-
ITargetedFields targetedFields,
93-
DbContextAResolver contextResolver,
94-
IResourceGraph resourceGraph,
95-
IGenericServiceFactory genericServiceFactory,
96-
IResourceFactory resourceFactory,
97-
IEnumerable<IQueryConstraintProvider> constraintProviders,
98-
ILoggerFactory loggerFactory)
99-
: base(targetedFields, contextResolver, resourceGraph, genericServiceFactory, resourceFactory, constraintProviders, loggerFactory)
100-
{ }
101-
}
72+
services.AddDbContext<DbContextA>(options => options.UseSqlite("Data Source=A.db"));
73+
services.AddDbContext<DbContextB>(options => options.UseSqlite("Data Source=B.db"));
10274

75+
services.AddScoped<IResourceRepository<ResourceA>, DbContextARepository<ResourceA>>();
76+
services.AddScoped<IResourceRepository<ResourceB>, DbContextBRepository<ResourceB>>();
10377

104-
// Startup.cs
105-
services.AddScoped<IResourceRepository<Foo>, DbContextARepository<Foo>>();
78+
services.AddJsonApi(dbContextTypes: new[] {typeof(DbContextA), typeof(DbContextB)});
10679
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.Services;
4+
using Microsoft.Extensions.Logging;
5+
using MultiDbContextExample.Models;
6+
7+
namespace MultiDbContextExample.Controllers
8+
{
9+
public sealed class ResourceAsController : JsonApiController<ResourceA>
10+
{
11+
public ResourceAsController(IJsonApiOptions options, ILoggerFactory loggerFactory,
12+
IResourceService<ResourceA> resourceService)
13+
: base(options, loggerFactory, resourceService)
14+
{
15+
}
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using JsonApiDotNetCore.Configuration;
2+
using JsonApiDotNetCore.Controllers;
3+
using JsonApiDotNetCore.Services;
4+
using Microsoft.Extensions.Logging;
5+
using MultiDbContextExample.Models;
6+
7+
namespace MultiDbContextExample.Controllers
8+
{
9+
public sealed class ResourceBsController : JsonApiController<ResourceB>
10+
{
11+
public ResourceBsController(IJsonApiOptions options, ILoggerFactory loggerFactory,
12+
IResourceService<ResourceB> resourceService)
13+
: base(options, loggerFactory, resourceService)
14+
{
15+
}
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.EntityFrameworkCore;
2+
using MultiDbContextExample.Models;
3+
4+
namespace MultiDbContextExample.Data
5+
{
6+
public sealed class DbContextA : DbContext
7+
{
8+
public DbSet<ResourceA> ResourceAs { get; set; }
9+
10+
public DbContextA(DbContextOptions<DbContextA> options)
11+
: base(options)
12+
{
13+
}
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Microsoft.EntityFrameworkCore;
2+
using MultiDbContextExample.Models;
3+
4+
namespace MultiDbContextExample.Data
5+
{
6+
public sealed class DbContextB : DbContext
7+
{
8+
public DbSet<ResourceB> ResourceBs { get; set; }
9+
10+
public DbContextB(DbContextOptions<DbContextB> options)
11+
: base(options)
12+
{
13+
}
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using JsonApiDotNetCore.Resources;
2+
using JsonApiDotNetCore.Resources.Annotations;
3+
4+
namespace MultiDbContextExample.Models
5+
{
6+
public sealed class ResourceA : Identifiable
7+
{
8+
[Attr]
9+
public string NameA { get; set; }
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using JsonApiDotNetCore.Resources;
2+
using JsonApiDotNetCore.Resources.Annotations;
3+
4+
namespace MultiDbContextExample.Models
5+
{
6+
public sealed class ResourceB : Identifiable
7+
{
8+
[Attr]
9+
public string NameB { get; set; }
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
<PropertyGroup>
3+
<TargetFramework>netcoreapp3.1</TargetFramework>
4+
</PropertyGroup>
5+
6+
<ItemGroup>
7+
<ProjectReference Include="..\..\JsonApiDotNetCore\JsonApiDotNetCore.csproj" />
8+
</ItemGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(EFCoreVersion)" />
12+
</ItemGroup>
13+
</Project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.AspNetCore.Hosting;
2+
using Microsoft.Extensions.Hosting;
3+
4+
namespace MultiDbContextExample
5+
{
6+
public class Program
7+
{
8+
public static void Main(string[] args)
9+
{
10+
CreateHostBuilder(args).Build().Run();
11+
}
12+
13+
public static IHostBuilder CreateHostBuilder(string[] args)
14+
{
15+
return Host.CreateDefaultBuilder(args)
16+
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
17+
}
18+
}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"$schema": "http://json.schemastore.org/launchsettings.json",
3+
"iisSettings": {
4+
"windowsAuthentication": false,
5+
"anonymousAuthentication": true,
6+
"iisExpress": {
7+
"applicationUrl": "http://localhost:14150",
8+
"sslPort": 44350
9+
}
10+
},
11+
"profiles": {
12+
"IIS Express": {
13+
"commandName": "IISExpress",
14+
"launchBrowser": false,
15+
"launchUrl": "/resourceBs",
16+
"environmentVariables": {
17+
"ASPNETCORE_ENVIRONMENT": "Development"
18+
}
19+
},
20+
"Kestrel": {
21+
"commandName": "Project",
22+
"launchBrowser": false,
23+
"launchUrl": "/resourceBs",
24+
"applicationUrl": "https://localhost:44350;http://localhost:14150",
25+
"environmentVariables": {
26+
"ASPNETCORE_ENVIRONMENT": "Development"
27+
}
28+
}
29+
}
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Collections.Generic;
2+
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.Queries;
4+
using JsonApiDotNetCore.Repositories;
5+
using JsonApiDotNetCore.Resources;
6+
using Microsoft.Extensions.Logging;
7+
using MultiDbContextExample.Data;
8+
9+
namespace MultiDbContextExample.Repositories
10+
{
11+
public class DbContextARepository<TResource> : EntityFrameworkCoreRepository<TResource>
12+
where TResource : class, IIdentifiable<int>
13+
{
14+
public DbContextARepository(ITargetedFields targetedFields, DbContextResolver<DbContextA> contextResolver,
15+
IResourceGraph resourceGraph, IGenericServiceFactory genericServiceFactory,
16+
IResourceFactory resourceFactory, IEnumerable<IQueryConstraintProvider> constraintProviders,
17+
ILoggerFactory loggerFactory)
18+
: base(targetedFields, contextResolver, resourceGraph, genericServiceFactory, resourceFactory,
19+
constraintProviders, loggerFactory)
20+
{
21+
}
22+
}
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Collections.Generic;
2+
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.Queries;
4+
using JsonApiDotNetCore.Repositories;
5+
using JsonApiDotNetCore.Resources;
6+
using Microsoft.Extensions.Logging;
7+
using MultiDbContextExample.Data;
8+
9+
namespace MultiDbContextExample.Repositories
10+
{
11+
public class DbContextBRepository<TResource> : EntityFrameworkCoreRepository<TResource>
12+
where TResource : class, IIdentifiable<int>
13+
{
14+
public DbContextBRepository(ITargetedFields targetedFields, DbContextResolver<DbContextB> contextResolver,
15+
IResourceGraph resourceGraph, IGenericServiceFactory genericServiceFactory,
16+
IResourceFactory resourceFactory, IEnumerable<IQueryConstraintProvider> constraintProviders,
17+
ILoggerFactory loggerFactory)
18+
: base(targetedFields, contextResolver, resourceGraph, genericServiceFactory, resourceFactory,
19+
constraintProviders, loggerFactory)
20+
{
21+
}
22+
}
23+
}

0 commit comments

Comments
 (0)