如何在控制器中使用多个 DBContext
How to use more than one DBContext in a controller
如何在控制器中使用多个 DBContext,我尝试以不同的方式重载构造函数?
一些控制器:
public C1(DBContext1 a, DBContext2 b, DBContext3 c)
{
}
//public C1(DBContext1 a)
//{
//}
//public C1(DBContext2 b)
//{
//}
//public C1(DBContext3 c)
//{
//}
StartUp.cs:
services.AddDbContext<DBContext1>(options =>
options.UseSqlServer(new string(K.ConnectionString))
);
services.AddDbContext<DBContext2>(options =>
options.UseSqlServer(new string(K.ConnectionString))
);
services.AddDbContext<DBContext3>(options =>
options.UseSqlServer(new string(K.ConnectionString))
);
我找到了this,但好像已经过时了
Error(, 前端调用构造函数时):
An unhandled exception has occurred while executing the request.
Exception: System.InvalidOperationException: The DbContextOptions
passed to the DBContext1 constructor must be a
DbContextOptions. When registering multiple DbContext
types make sure that the constructor for each context type has a
DbContextOptions parameter rather than a non-generic
DbContextOptions parameter. at
Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions
options) at _4_DWH.DBContext1..ctor(DbContextOptions options) in
D:...\DBContext1.cs:line 43 at
System.RuntimeMethodHandle.InvokeMethod(Object target, Object[]
arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags
invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite
constructorCallSite, RuntimeResolverContext context) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
2.VisitCallSite(ServiceCallSite
callSite, TArgument argument) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite
callSite, ServiceProviderEngineScope scope) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.b__0(ServiceProviderEngineScope
scope) at
Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type
serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at
Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type
serviceType) at
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider
sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] ) at
Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.b__0(ControllerContext
controllerContext) at
Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(ControllerContext
controllerContext) at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State&
next, Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location where exception was thrown --- at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker
invoker, Task lastTask, State next, Scope scope, Object state, Boolean
isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed
context) at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State&
next, Scope& scope, Object& state, Boolean& isCompleted) at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location where exception was thrown --- at
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker
invoker, Task task, IDisposable scope) at
Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint
endpoint, Task requestTask, ILogger logger) at
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.Invoke(HttpContext
context) at
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext
httpContext) at
Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext
httpContext) at
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext
context)
数据库上下文:
public class DBContext1 : DbContext
{
// ...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(new string(sameConnectionString));
}
public DBContext1() : base()
{
}
public DBContext1(DbContextOptions options) : base(options)
{
}
}
- 创建数据库上下文
public class DBContext_A : DbContext
{
public DBContext_A(DbContextOptions<DBContext_A> options) : base(options)
{
}
}
public class DBContext_B : DbContext
{
public DBContext_B(DbContextOptions<DBContext_B> options) : base(options)
{
}
}
public class DBContext_C : DbContext
{
public DBContext_C(DbContextOptions<DBContext_C> options) : base(options)
{
}
}
- 为每个 DBContext 定义一个连接字符串:
{
"ConnectionStrings": {
"Connection_A": "Server=(localdb)\mssqllocaldb;Database=DB_A;Trusted_Connection=True;...",
"Connection_B": "Server=(localdb)\mssqllocaldb;Database=DB_B;Trusted_Connection=True;...",
"Connection_C": "Server=(localdb)\mssqllocaldb;Database=DB_C;Trusted_Connection=True;...",
}
}
- 启动时注册:
services.AddDbContext<DBContext_A>(ops =>
{
ops.UseSqlServer(Configuration.GetConnectionString($"Connection_A"));
});
services.AddDbContext<DBContext_B>(ops =>
{
ops.UseSqlServer(Configuration.GetConnectionString($"Connection_B"));
});
services.AddDbContext<DBContext_C>(ops =>
{
ops.UseSqlServer(Configuration.GetConnectionString($"Connection_C"));
});
- 注入控制器:
public FooController : Controller
{
private readonly DBContext_A _context_A;
private readonly DBContext_B _context_B;
private readonly DBContext_C _context_C;
public FooController(
DBContext_A context_A,
DBContext_B context_B,
DBContext_C context_C)
{
_context_A = context_A;
_context_B = context_B;
_context_C = context_C;
}
}
Code First 方法的其他最佳实践:
为每个上下文创建一个 class 库,这样当您应用迁移时,每个上下文都会在其自己的项目中有自己的迁移文件夹。
- 解决方案
- 主项目
- startup.cs
- ClassLibrary_A
- DbContext_A.cs
- 迁移 // 文件夹
- ClassLibrary_B
- DbContext_B.cs
- 迁移 // 文件夹
- ClassLibrary_C
- DbContext_C.cs
- 迁移 // 文件夹
在多上下文解决方案中应用迁移时;
- 在解决方案资源管理器中,将主项目(startup.cs)设置为“启动项目”
- 将包管理器控制台中的相关 ClassLibrary_A 或 B 或 C 设置为“默认项目”
- 将目标上下文添加到每个 cmd,如下所示:
PM > add-migration Init -Context DBContext_A
PM > update-database -Context DBContext_A
PM > add-migration Init -Context DBContext_B
PM > update-database -Context DBContext_B
PM > add-migration Init -Context DBContext_C
PM > update-database -Context DBContext_C
或者,您可以使用如下完整的 PM 命令:
PM > add-migration Init -Context DBContext_A -Project ClassLibrary_A -StartupProject MainProject
PM > update-database -Context DBContext_A -Project ClassLibrary_A -StartupProject MainProject
PM > add-migration Init -Context DBContext_B -Project ClassLibrary_B -StartupProject MainProject
PM > update-database -Context DBContext_B -Project ClassLibrary_B -StartupProject MainProject
PM > add-migration Init -Context DBContext_C -Project ClassLibrary_C -StartupProject MainProject
PM > update-database -Context DBContext_C -Project ClassLibrary_C -StartupProject MainProject
如何在控制器中使用多个 DBContext,我尝试以不同的方式重载构造函数?
一些控制器:
public C1(DBContext1 a, DBContext2 b, DBContext3 c)
{
}
//public C1(DBContext1 a)
//{
//}
//public C1(DBContext2 b)
//{
//}
//public C1(DBContext3 c)
//{
//}
StartUp.cs:
services.AddDbContext<DBContext1>(options =>
options.UseSqlServer(new string(K.ConnectionString))
);
services.AddDbContext<DBContext2>(options =>
options.UseSqlServer(new string(K.ConnectionString))
);
services.AddDbContext<DBContext3>(options =>
options.UseSqlServer(new string(K.ConnectionString))
);
我找到了this,但好像已经过时了
Error(, 前端调用构造函数时):
An unhandled exception has occurred while executing the request.
Exception: System.InvalidOperationException: The DbContextOptions passed to the DBContext1 constructor must be a DbContextOptions. When registering multiple DbContext types make sure that the constructor for each context type has a DbContextOptions parameter rather than a non-generic DbContextOptions parameter. at Microsoft.EntityFrameworkCore.DbContext..ctor(DbContextOptions options) at _4_DWH.DBContext1..ctor(DbContextOptions options) in D:...\DBContext1.cs:line 43 at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor
2.VisitCallSite(ServiceCallSite callSite, TArgument argument) at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.b__0(ServiceProviderEngineScope scope) at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType) at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, Boolean isDefaultParameterRequired)
at lambda_method(Closure , IServiceProvider , Object[] ) at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.b__0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.g__CreateController|0(ControllerContext controllerContext) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync() --- End of stack trace from previous location where exception was thrown --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync() --- End of stack trace from previous location where exception was thrown --- at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) at Microsoft.AspNetCore.Routing.EndpointMiddleware.g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.MigrationsEndPointMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.DatabaseErrorPageMiddleware.Invoke(HttpContext httpContext) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
数据库上下文:
public class DBContext1 : DbContext
{
// ...
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(new string(sameConnectionString));
}
public DBContext1() : base()
{
}
public DBContext1(DbContextOptions options) : base(options)
{
}
}
- 创建数据库上下文
public class DBContext_A : DbContext
{
public DBContext_A(DbContextOptions<DBContext_A> options) : base(options)
{
}
}
public class DBContext_B : DbContext
{
public DBContext_B(DbContextOptions<DBContext_B> options) : base(options)
{
}
}
public class DBContext_C : DbContext
{
public DBContext_C(DbContextOptions<DBContext_C> options) : base(options)
{
}
}
- 为每个 DBContext 定义一个连接字符串:
{
"ConnectionStrings": {
"Connection_A": "Server=(localdb)\mssqllocaldb;Database=DB_A;Trusted_Connection=True;...",
"Connection_B": "Server=(localdb)\mssqllocaldb;Database=DB_B;Trusted_Connection=True;...",
"Connection_C": "Server=(localdb)\mssqllocaldb;Database=DB_C;Trusted_Connection=True;...",
}
}
- 启动时注册:
services.AddDbContext<DBContext_A>(ops =>
{
ops.UseSqlServer(Configuration.GetConnectionString($"Connection_A"));
});
services.AddDbContext<DBContext_B>(ops =>
{
ops.UseSqlServer(Configuration.GetConnectionString($"Connection_B"));
});
services.AddDbContext<DBContext_C>(ops =>
{
ops.UseSqlServer(Configuration.GetConnectionString($"Connection_C"));
});
- 注入控制器:
public FooController : Controller
{
private readonly DBContext_A _context_A;
private readonly DBContext_B _context_B;
private readonly DBContext_C _context_C;
public FooController(
DBContext_A context_A,
DBContext_B context_B,
DBContext_C context_C)
{
_context_A = context_A;
_context_B = context_B;
_context_C = context_C;
}
}
Code First 方法的其他最佳实践:
为每个上下文创建一个 class 库,这样当您应用迁移时,每个上下文都会在其自己的项目中有自己的迁移文件夹。
- 解决方案
- 主项目
- startup.cs
- ClassLibrary_A
- DbContext_A.cs
- 迁移 // 文件夹
- ClassLibrary_B
- DbContext_B.cs
- 迁移 // 文件夹
- ClassLibrary_C
- DbContext_C.cs
- 迁移 // 文件夹
- 主项目
在多上下文解决方案中应用迁移时;
- 在解决方案资源管理器中,将主项目(startup.cs)设置为“启动项目”
- 将包管理器控制台中的相关 ClassLibrary_A 或 B 或 C 设置为“默认项目”
- 将目标上下文添加到每个 cmd,如下所示:
PM > add-migration Init -Context DBContext_A
PM > update-database -Context DBContext_A
PM > add-migration Init -Context DBContext_B
PM > update-database -Context DBContext_B
PM > add-migration Init -Context DBContext_C
PM > update-database -Context DBContext_C
或者,您可以使用如下完整的 PM 命令:
PM > add-migration Init -Context DBContext_A -Project ClassLibrary_A -StartupProject MainProject
PM > update-database -Context DBContext_A -Project ClassLibrary_A -StartupProject MainProject
PM > add-migration Init -Context DBContext_B -Project ClassLibrary_B -StartupProject MainProject
PM > update-database -Context DBContext_B -Project ClassLibrary_B -StartupProject MainProject
PM > add-migration Init -Context DBContext_C -Project ClassLibrary_C -StartupProject MainProject
PM > update-database -Context DBContext_C -Project ClassLibrary_C -StartupProject MainProject