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

时间:2017-04-13 15:18:22

标签: entity-framework ef-code-first ef-migrations

我已经为每个客户构建了一个具有Core解决方案和Custom解决方案的系统。 Core拥有自己的dbcontext,每个海关都有自己的dbcontext用于客户特定的表。 自定义解决方案引用了Core解决方案dll。 Core解决方案在插件架构中动态加载自定义dll。 由于Core和Custom有自己的dbcontext,每个都有自己的迁移文件。

一切都很好,除了一件事,我不能做一个linq查询加入Core表与自定义表。 (例如:在单个查询中将ProjectCustom与Project一起加入)。 我收到错误:

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

因此,为了允许查询从Core和Custom连接表,我想到另一条路径,其中Core定义基本抽象DbContext,而Custom继承自此dbcontext并添加其自定义表。所以我在运行时有一个dbcontext。

现在我的问题是,有什么办法可以在Core中安装迁移文件,还有另一组在Custom中定义的迁移文件?

我已经制定了一个可行的解决方案,其中所有迁移都在Custom解决方案中进行了维护。但是我不喜欢这样一个事实:如果我进行影响Core表的手动迁移,我必须手动将其传播到所有Custom解决方案。

我希望将影响Core表的迁移保留到Core解决方案中,并为海关进行另一组迁移。全部应用于相同的dbcontext,最终实现位于自定义解决方案中。

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

1 个答案:

答案 0 :(得分:2)

Source Code

让我们首先为Context的配置创建一个接口

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

在我的情况下,实现存在于每个单独的Context中,但在diff类中实现它将是很好的。

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>();

    public ContextJoin(params IDbContextRegistry[] registry)
    {
        _registry.AddRange(registry);
    }

    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}"));
    }
}

希望这有帮助!