是否可以使用单个 DbContext,并将迁移文件拆分为不同的程序集?

Is that possible to have a single DbContext, with migration files being split in different assemblies?

我已经构建了一个系统,其中有一个核心解决方案和一个针对我们每个客户的自定义解决方案。 Core 有自己的 dbcontext,每个 Customs 都有自己的 dbcontext,用于特定于客户端的 tables。 自定义解决方案引用核心解决方案 dll。 插件架构中的核心解决方案正在动态加载自定义 dll。 由于Core和Custom有自己的dbcontext,每个都有自己的迁移文件。

除一件事外,一切都运行良好,我无法进行连接核心 table 和自定义 table 的 linq 查询。 (例如:在单个查询中加入 ProjectCustom 和 Project)。 我收到错误:

The specified LINQ expression contains references to queries that are associated with different contexts.

因此,为了允许从 Core 和 Custom 中加入 tables 的查询,我正在考虑采用另一条路线,其中 Core 定义一个基础抽象 DbContext,而 Custom 继承自该 dbcontext 并添加其自定义 table秒。所以我在 运行 时间有一个 dbcontext。

现在回答我的问题,有什么方法可以让我在 Core 中拥有迁移文件,并在自定义中定义另一组迁移文件?

我制定了一个工作解决方案,其中所有迁移都在自定义解决方案中维护。但是我不喜欢这样的事实,即如果我进行影响核心 tables 的手动迁移,我必须手动将其传播到所有自定义解决方案。

我想将影响核心 table 的迁移保留到该核心解决方案中,并为海关设置另一组迁移。所有应用到相同的 dbcontext,最终实现驻留在自定义解决方案中。

这可能吗?有没有办法为迁移文件配置某种提供程序?所以我可以首先应用核心解决方案中定义的迁移,然后使用相同的 dbcontext 从自定义应用迁移。

Source Code

让我们从为 Context 的配置创建一个接口开始

public interface IDbContextRegistry
{
    void Configure(DbModelBuilder builder);
}

在我的例子中,实现存在于每个单独的上下文中,但在差异 class 中实现它会很好。

public class ContextA : DbContext, IDbContextRegistry
{
    static ContextA()
    {
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<ContextA, ConfigurationA>());
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        Configure(modelBuilder);
    }

    public void Configure(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ModelA>();
    }
}
internal sealed class ConfigurationA : DbMigrationsConfiguration<ContextA>
{
    public ConfigurationA()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(ContextA context)
    {
    }
}

public class ModelA
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class ContextB : DbContext, IDbContextRegistry
{
    static ContextB()
    {
        Database.SetInitializer(new MigrateDatabaseToLatestVersion<ContextB, ConfigurationB>());
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        Configure(modelBuilder);
    }

    public void Configure(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ModelB>();
    }
}
internal sealed class ConfigurationB : DbMigrationsConfiguration<ContextB>
{
    public ConfigurationB()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(ContextB context)
    {
    }
}

public class ModelB
{
    public int Id { get; set; }
    public DateTimeOffset Date { get; set; }
}

稍后在您的主应用程序中创建一个将注册所有单独类型的上下文。

public class ContextJoin : DbContext
{
    private readonly List<IDbContextRegistry> _registry = new List<IDbContextRegistry>();

    // Constructor to allow automatic DI injection for most modern containers
    public ContextJoin(IEnumerable<IDbContextRegistry> registry)
    {
        _registry.AddRange(registry);
    }

    public ContextJoin(params IDbContextRegistry[] registry) : this(_registry.AsEnumerable())
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        foreach (var entry in _registry)
        {
            entry.Configure(modelBuilder);
        }
    }
}

瞧,每个实体都存在于 ContextJoin 中并准备好使用。 示例:

class Program
{
    static void Main(string[] args)
    {
        ContextA ctxA = new ContextA();
        ctxA.Set<ModelA>().Add(new ModelA());
        ctxA.SaveChanges();

        ContextB ctxB = new ContextB();
        ctxB.Set<ModelB>().Add(new ModelB());
        ctxB.SaveChanges();

        ContextJoin ctxJoin = new ContextJoin(ctxA, ctxB);
        ctxJoin.Set<ModelB>().Add(new ModelB());
        ctxJoin.Set<ModelA>().Add(new ModelA());
        ctxJoin.SaveChanges();

        var crossQuery = ctxJoin.Set<ModelA>().Join(
            ctxJoin.Set<ModelB>(), t => t.Id, t => t.Id, (a, b) => new
            {
                a.Name,
                b.Date
            }).ToList();
        crossQuery.ForEach(t => Console.WriteLine($"Name: {t.Name}, Date: {t.Date}"));
    }
}

希望对您有所帮助!