如何在单个查询执行中使用多个 DbContext

作者:微信公众号:【架构师老卢】
9-8 18:1
8

概述:在单个应用程序中有多个 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 进行查询。在本文中

在单个应用程序中有多个 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

LMS 程序

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
}

从主机应用程序的应用程序服务中的多个 DbContext 进行查询

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)
    {
    }
}

AppDbContext

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 实现它,从而允许我们在单个查询执行中从多个存储库进行查询。

阅读排行