在单个应用程序中有多个 DbContext 是很常见的,尤其是当您的应用程序是模块化的时。但是,从不同的存储库(每个存储库都与不同的 DbContext 相关联)进行查询可能会导致此错误:
System.InvalidOperationException:'Cannot use multiple context instances within a single query execution. Ensure the query uses a single context instance.'
出现此错误的原因是 EF Core 不支持在单个查询执行中从多个 DbContext 进行查询。
在本文中,我们将探讨一种解决方案,即使用 EF Core 在 ABP 框架中的单个执行中从多个 DbContext 实例进行查询。具体来说,我们将重点介绍有两个 DbContext 实例的场景:一个用于 ExamsModule,另一个用于主应用程序 (LMSApp)。值得注意的是,我们示例中的两个 DbContext 实例都连接到同一个数据库。
此外,请务必注意,此解决方法并不是唯一的解决方案。另一种方法涉及替换 DbContext 并将其直接注入主机应用程序中需要的位置。ABP 框架文档中详细介绍了此方法。
假设我们有两个 DbContext:一个用于 ,另一个用于主机应用程序 。我们希望在单个查询执行中从 LMSApp DbContext 获取 和 ExamsModule DbContext 的详细信息。ExamsModuleLMSAppCourseExam
public class Course<Guid>
{
public string Title { get; set; }
public string Description { get; set; }
}
public interface ILMSDbContext : IEfCoreDbContext
{
DbSet<Course> Courses { get; set; }
}
public class LMSDbContext : AbpDbContext<LMSDbContext>, ILMSDbContext
{
public DbSet<Course> Courses { get; set; }
}
public interface ICourseRepository : IRepository<Course, Guid>
{
// Add custom methods here
}
public class CourseRepository : EfCoreRepository<LMSDbContext, Course, Guid>, ICourseRepository
{
public CourseRepository(IDbContextProvider<LMSDbContext> dbContextProvider) : base(dbContextProvider)
{
}
// Implement custom methods here
}
public class Exam<Guid>
{
public string Title { get; set; }
public DateTime Date { get; set; }
}
public interface IExamDbContext : IEfCoreDbContext
{
DbSet<Exam> Exams { get; set; }
}
public class ExamDbContext : AbpDbContext<ExamDbContext>, IExamDbContext
{
public DbSet<Exam> Exams { get; set; }
}
public interface IExamRepository : IRepository<Exam, Guid>
{
// Add custom methods here
}
public class ExamRepository : EfCoreRepository<ExamDbContext, Exam, Guid>, IExamRepository
{
public ExamRepository(IDbContextProvider<ExamDbContext> dbContextProvider) : base(dbContextProvider)
{
}
// Implement custom methods here
}
public class CourseExamAppService : ApplicationService
{
private readonly ICourseRepository _courseRepository;
private readonly IExamRepository _examRepository;
public CourseExamAppService(ICourseRepository courseRepository, IExamRepository examRepository)
{
_courseRepository = courseRepository;
_examRepository = examRepository;
}
public async Task<CourseExamDto> GetCourseExamAsync(Guid courseId, Guid examId)
{
var course = await _courseRepository.GetAsync(courseId);
var exam = await _examRepository.GetAsync(examId);
return new CourseExamDto
{
CourseTitle = course.Title,
CourseDescription = course.Description,
ExamTitle = exam.Title,
ExamDate = exam.Date
};
}
}
在上面的代码中,我们从两个不同的存储库进行查询,每个存储库位于不同的 DbContext 中。这将引发前面提到的错误。
要解决此问题,我们需要抽象化 exams 存储库并使用 LMSApp DbContext 实现它。这可确保我们使用的两个存储库位于同一 DbContext (LMSApp DbContext) 中。
// Abstract Exam Repository to use in the host application
public class AbstractExamRepository<TDbContext> : EfCoreRepository<TDbContext, Exam, Guid>, IExamRepository
where TDbContext : IExamDbContext
{
public AbstractExamRepository(IDbContextProvider<TDbContext> dbContextProvider) : base(dbContextProvider)
{
}
// Implement custom methods here
}
// Concrete Exam Repository to use in the ExamsModule
public class ExamRepository : AbstractExamRepository<ExamDbContext>
{
public ExamRepository(IDbContextProvider<ExamDbContext> dbContextProvider) : base(dbContextProvider)
{
}
}
public class LMSDbContext : AbpDbContext<LMSDbContext>, ILMSDbContext, IExamDbContext
{
public DbSet<Course> Courses { get; set; }
public DbSet<Exam> Exams { get; set; }
}
public interface ILMSExamRepository : IExamRepository
{
}
public class LMSExamRepository : AbstractExamRepository<LMSDbContext>, ILMSExamRepository
{
public LMSExamRepository(IDbContextProvider<LMSDbContext> dbContextProvider) : base(dbContextProvider)
{
}
}
然后,您需要将新存储库添加到主机应用程序中的依赖项注入容器中。
public override void ConfigureServices(ServiceConfigurationContext context)
{
// context.Services.AddScoped<ILMSExamRepository, LMSExamRepository>();
context.Services.AddAbpDbContext<LMSDbContext>(options =>
{
options.AddDefaultRepositories(includeAllEntities: true);
options.AddRepository<Exam, LMSExamRepository>();
});
}
现在,您可以在单个查询执行中使用来自两个存储库的 in to 查询。ILMSExamRepositoryCourseExamAppService
public class CourseExamAppService : ApplicationService
{
private readonly ICourseRepository _courseRepository;
private readonly ILMSExamRepository _examRepository;
public CourseExamAppService(ICourseRepository courseRepository, ILMSExamRepository examRepository)
{
_courseRepository = courseRepository;
_examRepository = examRepository;
}
public async Task<CourseExamDto> GetCourseExamAsync(Guid courseId, Guid examId)
{
var course = await _courseRepository.GetAsync(courseId);
var exam = await _examRepository.GetAsync(examId);
return new CourseExamDto
{
CourseTitle = course.Title,
CourseDescription = course.Description,
ExamTitle = exam.Title,
ExamDate = exam.Date
};
}
}
在本文中,我们了解了如何在 ABP 框架中的单个查询执行中从多个 DbContext 进行查询。我们抽象了存储库并使用主机应用程序 DbContext 实现它,从而允许我们在单个查询执行中从多个存储库进行查询。