Skip to content

Fixed support for using multiple DbContexts #836

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

Merged
merged 2 commits into from
Sep 29, 2020
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: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -363,4 +363,7 @@ MigrationBackup/
.ionide/

# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd

# Sqlite example databases
*.db
30 changes: 30 additions & 0 deletions JsonApiDotnetCore.sln
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GettingStarted", "src\Examp
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "test\IntegrationTests\IntegrationTests.csproj", "{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiDbContextExample", "src\Examples\MultiDbContextExample\MultiDbContextExample.csproj", "{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiDbContextTests", "test\MultiDbContextTests\MultiDbContextTests.csproj", "{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -185,6 +189,30 @@ Global
{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}.Release|x64.Build.0 = Release|Any CPU
{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}.Release|x86.ActiveCfg = Release|Any CPU
{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9}.Release|x86.Build.0 = Release|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|x64.ActiveCfg = Debug|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|x64.Build.0 = Debug|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|x86.ActiveCfg = Debug|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Debug|x86.Build.0 = Debug|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|Any CPU.Build.0 = Release|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|x64.ActiveCfg = Release|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|x64.Build.0 = Release|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|x86.ActiveCfg = Release|Any CPU
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26}.Release|x86.Build.0 = Release|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|x64.ActiveCfg = Debug|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|x64.Build.0 = Debug|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|x86.ActiveCfg = Debug|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Debug|x86.Build.0 = Debug|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|Any CPU.Build.0 = Release|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x64.ActiveCfg = Release|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x64.Build.0 = Release|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x86.ActiveCfg = Release|Any CPU
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -201,6 +229,8 @@ Global
{8BCFF95F-4850-427C-AEDB-B5B4F62B2C7B} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
{21D27239-138D-4604-8E49-DCBE41BCE4C8} = {7A2B7ADD-ECB5-4D00-AA6A-D45BD11C97CF}
{CEB08B86-6BF1-4227-B20F-45AE9C1CC6D9} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
{6CAFDDBE-00AB-4784-801B-AB419C3C3A26} = {026FBC6C-AF76-4568-9B87-EC73457899FD}
{EC3202C6-1D4C-4B14-A599-B9D3F27FE3BA} = {24B15015-62E5-42E1-9BA0-ECE6BE7AA15F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A2421882-8F0A-4905-928F-B550B192F9A4}
Expand Down
67 changes: 20 additions & 47 deletions docs/usage/extensibility/repositories.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,62 +45,35 @@ public class ArticleRepository : EntityFrameworkCoreRepository<Article>

## Multiple DbContexts

If you need to use multiple Entity Framework Core DbContexts, first add each DbContext to the ResourceGraphBuilder.

Then, create an implementation of IDbContextResolver for each context.

Register each of the new IDbContextResolver implementations in Startup.cs.

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.

Then inject the repository for the correct entity, in this case Foo is a member of DbContextA.
If you need to use multiple Entity Framework Core DbContexts, first create a repository for each context and inject its typed resolver.
This example shows a single `DbContextARepository` for all entities that are members of `DbContextA`.

```c#
// Startup.cs
services.AddJsonApi(resources: builder =>
{
// Add both contexts using the builder
builder.AddDbContext<DbContextA>();
builder.AddDbContext<DbContextB>();
});

public class DbContextAResolver : IDbContextResolver
public class DbContextARepository<TResource> : EntityFrameworkCoreRepository<TResource>
where TResource : class, IIdentifiable<int>
{
private readonly DbContextA _context;

public DbContextAResolver(DbContextA context)
{
_context = context;
}

public DbContext GetContext()
public DbContextARepository(ITargetedFields targetedFields, DbContextResolver<DbContextA> contextResolver,
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
IResourceGraph resourceGraph, IGenericServiceFactory genericServiceFactory,
IResourceFactory resourceFactory, IEnumerable<IQueryConstraintProvider> constraintProviders,
ILoggerFactory loggerFactory)
: base(targetedFields, contextResolver, resourceGraph, genericServiceFactory, resourceFactory,
constraintProviders, loggerFactory)
{
return _context;
}
}
```

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

// Startup.cs
services.AddScoped<DbContextAResolver>();
services.AddScoped<DbContextBResolver>();

```c#
// In Startup.ConfigureServices

public class DbContextARepository<TResource> : EntityFrameworkCoreRepository<TResource>
where TResource : class, IIdentifiable<int>
{
public DbContextARepository(
ITargetedFields targetedFields,
DbContextAResolver contextResolver,
IResourceGraph resourceGraph,
IGenericServiceFactory genericServiceFactory,
IResourceFactory resourceFactory,
IEnumerable<IQueryConstraintProvider> constraintProviders,
ILoggerFactory loggerFactory)
: base(targetedFields, contextResolver, resourceGraph, genericServiceFactory, resourceFactory, constraintProviders, loggerFactory)
{ }
}
services.AddDbContext<DbContextA>(options => options.UseSqlite("Data Source=A.db"));
services.AddDbContext<DbContextB>(options => options.UseSqlite("Data Source=B.db"));

services.AddScoped<IResourceRepository<ResourceA>, DbContextARepository<ResourceA>>();
services.AddScoped<IResourceRepository<ResourceB>, DbContextBRepository<ResourceB>>();

// Startup.cs
services.AddScoped<IResourceRepository<Foo>, DbContextARepository<Foo>>();
services.AddJsonApi(dbContextTypes: new[] {typeof(DbContextA), typeof(DbContextB)});
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
using Microsoft.Extensions.Logging;
using MultiDbContextExample.Models;

namespace MultiDbContextExample.Controllers
{
public sealed class ResourceAsController : JsonApiController<ResourceA>
{
public ResourceAsController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IResourceService<ResourceA> resourceService)
: base(options, loggerFactory, resourceService)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Controllers;
using JsonApiDotNetCore.Services;
using Microsoft.Extensions.Logging;
using MultiDbContextExample.Models;

namespace MultiDbContextExample.Controllers
{
public sealed class ResourceBsController : JsonApiController<ResourceB>
{
public ResourceBsController(IJsonApiOptions options, ILoggerFactory loggerFactory,
IResourceService<ResourceB> resourceService)
: base(options, loggerFactory, resourceService)
{
}
}
}
15 changes: 15 additions & 0 deletions src/Examples/MultiDbContextExample/Data/DbContextA.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;
using MultiDbContextExample.Models;

namespace MultiDbContextExample.Data
{
public sealed class DbContextA : DbContext
{
public DbSet<ResourceA> ResourceAs { get; set; }

public DbContextA(DbContextOptions<DbContextA> options)
: base(options)
{
}
}
}
15 changes: 15 additions & 0 deletions src/Examples/MultiDbContextExample/Data/DbContextB.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;
using MultiDbContextExample.Models;

namespace MultiDbContextExample.Data
{
public sealed class DbContextB : DbContext
{
public DbSet<ResourceB> ResourceBs { get; set; }

public DbContextB(DbContextOptions<DbContextB> options)
: base(options)
{
}
}
}
11 changes: 11 additions & 0 deletions src/Examples/MultiDbContextExample/Models/ResourceA.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;

namespace MultiDbContextExample.Models
{
public sealed class ResourceA : Identifiable
{
[Attr]
public string NameA { get; set; }
}
}
11 changes: 11 additions & 0 deletions src/Examples/MultiDbContextExample/Models/ResourceB.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using JsonApiDotNetCore.Resources;
using JsonApiDotNetCore.Resources.Annotations;

namespace MultiDbContextExample.Models
{
public sealed class ResourceB : Identifiable
{
[Attr]
public string NameB { get; set; }
}
}
13 changes: 13 additions & 0 deletions src/Examples/MultiDbContextExample/MultiDbContextExample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\JsonApiDotNetCore\JsonApiDotNetCore.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="$(EFCoreVersion)" />
</ItemGroup>
</Project>
19 changes: 19 additions & 0 deletions src/Examples/MultiDbContextExample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace MultiDbContextExample
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
}
}
30 changes: 30 additions & 0 deletions src/Examples/MultiDbContextExample/Properties/launchSettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:14150",
"sslPort": 44350
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"launchUrl": "/resourceBs",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Kestrel": {
"commandName": "Project",
"launchBrowser": false,
"launchUrl": "/resourceBs",
"applicationUrl": "https://localhost:44350;http://localhost:14150",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Generic;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Queries;
using JsonApiDotNetCore.Repositories;
using JsonApiDotNetCore.Resources;
using Microsoft.Extensions.Logging;
using MultiDbContextExample.Data;

namespace MultiDbContextExample.Repositories
{
public class DbContextARepository<TResource> : EntityFrameworkCoreRepository<TResource>
where TResource : class, IIdentifiable<int>
{
public DbContextARepository(ITargetedFields targetedFields, DbContextResolver<DbContextA> contextResolver,
IResourceGraph resourceGraph, IGenericServiceFactory genericServiceFactory,
IResourceFactory resourceFactory, IEnumerable<IQueryConstraintProvider> constraintProviders,
ILoggerFactory loggerFactory)
: base(targetedFields, contextResolver, resourceGraph, genericServiceFactory, resourceFactory,
constraintProviders, loggerFactory)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Collections.Generic;
using JsonApiDotNetCore.Configuration;
using JsonApiDotNetCore.Queries;
using JsonApiDotNetCore.Repositories;
using JsonApiDotNetCore.Resources;
using Microsoft.Extensions.Logging;
using MultiDbContextExample.Data;

namespace MultiDbContextExample.Repositories
{
public class DbContextBRepository<TResource> : EntityFrameworkCoreRepository<TResource>
where TResource : class, IIdentifiable<int>
{
public DbContextBRepository(ITargetedFields targetedFields, DbContextResolver<DbContextB> contextResolver,
IResourceGraph resourceGraph, IGenericServiceFactory genericServiceFactory,
IResourceFactory resourceFactory, IEnumerable<IQueryConstraintProvider> constraintProviders,
ILoggerFactory loggerFactory)
: base(targetedFields, contextResolver, resourceGraph, genericServiceFactory, resourceFactory,
constraintProviders, loggerFactory)
{
}
}
}
Loading