You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在xUnit中替换EF Core的默认Schema?

在EF Core(Database-First)集成测试中覆盖Oracle默认Schema

问题背景

我需要对基于EF Core(Database-First)连接Oracle数据库的API做集成测试。一开始用Testcontainers.Oracle,但本地运行速度太慢,所以打算用独立Schema做测试,且不想用内存数据库。现有两个Schema:

  • Schema1:生成DbContext时用的原始Schema
  • Schema2:打算在此创建Schema1的表并运行测试

现在想在Web应用工厂中覆盖默认Schema,之前试过在源项目里判断是否运行xUnit测试,但不想这么做——因为重新生成DbContext时会被覆盖,希望所有修改都在测试项目中完成。

现有代码如下:

public class MyApiFactory : WebApplicationFactory<IApiMarker>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestServices(services =>
        {
            services.RemoveAll(typeof(ApplicationDbContext));
            services.AddDbContext<ApplicationDbContext>(optionsBuilder =>
                optionsBuilder.UseOracle(""));

            // 是否可以在这里修改model builder的默认Schema?
        });
    }
}

public partial class ApplicationDbContext : DbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
    {
    }

    public virtual DbSet<MyTable> MyTable{ get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.HasDefaultSchema("Schema1");

#if DEBUG
        if (UnitTestDetector.IsRunningFromXUnit)
        {
            modelBuilder.HasDefaultSchema("Schema2");
        }
#endif

        modelBuilder.Entity<MyTable1>(entity =>
        {
            // 表配置逻辑
        });
    }
}

解决方案

方案一:派生测试专用DbContext(推荐)

在测试项目中创建ApplicationDbContext的派生类,重写OnModelCreating方法覆盖默认Schema,所有测试相关逻辑都放在这里,完全隔离源项目代码,不会被DbContext重生成操作覆盖。

步骤1:创建测试用DbContext

public class TestApplicationDbContext : ApplicationDbContext
{
    public TestApplicationDbContext(DbContextOptions<TestApplicationDbContext> options)
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // 先执行基类的模型配置,确保原始表结构、约束等被完整加载
        base.OnModelCreating(modelBuilder);
        
        // 覆盖默认Schema为Schema2
        modelBuilder.HasDefaultSchema("Schema2");

        // 如果基类中实体配置硬编码了Schema(比如ToTable("MyTable1", "Schema1")),需要修改实体的Schema
        // 批量修改所有实体Schema的方式:
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            entityType.SetSchema("Schema2");
        }
    }
}

步骤2:在Web应用工厂中注册测试用DbContext

public class MyApiFactory : WebApplicationFactory<IApiMarker>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestServices(services =>
        {
            // 移除源项目中注册的DbContext选项
            var dbContextOptionsDescriptor = services.SingleOrDefault(
                d => d.ServiceType == typeof(DbContextOptions<ApplicationDbContext>));
            if (dbContextOptionsDescriptor != null)
            {
                services.Remove(dbContextOptionsDescriptor);
            }

            // 注册测试用的DbContext,替换原始的ApplicationDbContext
            services.AddDbContext<ApplicationDbContext, TestApplicationDbContext>(optionsBuilder =>
            {
                optionsBuilder.UseOracle("你的测试Oracle连接字符串");
                // 可选:添加测试相关配置,比如禁用查询跟踪提升性能
                optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
            });
        });
    }
}

方案二:直接在Web应用工厂中修改模型配置

如果不想派生DbContext,可以在注册DbContext时,手动修改模型的Schema配置:

public class MyApiFactory : WebApplicationFactory<IApiMarker>
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureTestServices(services =>
        {
            services.RemoveAll(typeof(DbContextOptions<ApplicationDbContext>));
            services.AddDbContext<ApplicationDbContext>(optionsBuilder =>
            {
                optionsBuilder.UseOracle("你的测试Oracle连接字符串");
                
                // 自定义模型配置,覆盖默认Schema
                optionsBuilder.UseModel(modelBuilder =>
                {
                    // 先应用原始DbContext的模型配置
                    var originalContext = new ApplicationDbContext(new DbContextOptions<ApplicationDbContext>());
                    originalContext.OnModelCreating(modelBuilder);
                    
                    // 修改默认Schema
                    modelBuilder.HasDefaultSchema("Schema2");
                    
                    // 批量修改所有实体的Schema
                    foreach (var entityType in modelBuilder.Model.GetEntityTypes())
                    {
                        entityType.SetSchema("Schema2");
                    }
                });
            });
        });
    }
}

注意事项

  • 确保测试环境中的Oracle数据库已创建Schema2,且包含与Schema1一致的表结构(可在测试初始化时执行DDL脚本或EF迁移)
  • 若源项目的DbContext是通过Scaffold命令重新生成的,方案一的派生类不会被影响,完全隔离测试逻辑
  • 若实体配置中硬编码了Schema名称,必须修改实体的Schema配置,仅修改默认Schema无法生效

内容的提问来源于stack exchange,提问作者darrenleeyx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 20:22:09