使用单个Entity Framework Core DbContext管理具有同名表的多个数据库架构

时间:2018-08-28 08:41:36

标签: c# .net-core entity-framework-core database-schema dbcontext

.NET Core 2.1库中,我需要访问具有多个架构的MySQL数据库,其中的表在这些架构中可以使用相同的名称 >。我不能对数据库进行任何更改,因为它来自另一家公司。 对于大多数表,我需要只读访问权限,并且我想使用单个EF核心 DbContext

实际上我在初始化期间收到此错误消息:

  

InvalidOperationException:无法将表'tbl_panel'用于   实体类型“ Db2Panels”,因为它已用于实体   键入“ Db1Panels”,它们之间没有任何关系   主键。

我认为问题的症结主要在于配置方法,它不仅应该被调用一次,而且应该被调用N次,对于具有不同模式(db_machine_1.tbl_panel,{{1} }等)。 我该如何达到目标?

这是我的实际实现方式。

数据库架构

db_machine_2.tbl_panel

DbContext配置

// db_machine_1 schema
db_machine_1.tbl_panel
db_machine_1.tbl_basket
db_machine_1.tbl_unit

// db_machine_2 schema
db_machine_2.tbl_panel
db_machine_2.tbl_basket
db_machine_2.tbl_discard

// Other db_machine_X schemas with similar structure...

对象

public class MyDbContext : DbContext
{
    // Schema: db_machine_1
    public DbSet<Panel> Db1Panels { get; set; }
    public DbSet<Basket> Db1Baskets { get; set; }
    public DbSet<Unit> Db1Units { get; set; }

    // Schema: db_machine_2
    public DbSet<Panel> Db2Panels { get; set; }
    public DbSet<Basket> Db2Baskets { get; set; }
    public DbSet<Discard> Db2Discards { get; set; }

    // Other schemas DbSet<X> objects...

    // Arrays to access the specific DbSet by using the schema number:
    // Panels[1] -> Db1Panels, Panels[2] -> Db2Panels, ...
    public DbSet<Panel>[] Panels { get; }
    public DbSet<Basket>[] Baskets { get; }
    // Other arrays for other DbSet<X> objects...

    public MyDbContext(DbContextOptions<MyDbContext> options)
        : base(options)
    {
        // Arrays initialization
        List<DbSet<Panel>> dbPanelList = new List<DbSet<Panel>>();
        dbPanelList.Add(Db1Panels);
        dbPanelList.Add(Db2Panels);
        Panels = dbPanelList.ToArray();

        List<DbSet<Basket>> dbBasketList = new List<DbSet<Basket>>();
        dbBasketList.Add(Db1Baskets);
        dbBasketList.Add(Db2Baskets);
        Baskets = dbBasketList.ToArray();

        // Initialization for other DbSet<X> objects...
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyAllConfigurations<MyDbContext>();
        modelBuilder.ApplyAllConversions();
    }
}

配置

public class Panel
{
    public long Id { get; set; }
    public string SN { get; set; }
    // Other properties...
}

public class Basket
{
    public long Id { get; set; }
    public string Description { get; set; }
    // Other properties...
}

有关基类数组的更新

在创建基本抽象类和派生类之后,我想将所有派生类对象合并到单个数组中,以便能够通过使用架构编号访问特定的public class PanelConfiguration : IEntityTypeConfiguration<Panel> { public void Configure(EntityTypeBuilder<Panel> builder) { builder.ToTable("tbl_panel"); builder.HasKey(e => e.Id); builder.Property(e => e.Id) .HasColumnName("ID_Record"); builder.Property(e => e.SN) .HasColumnName("Serial") .HasMaxLength(20); // Other properties configuration... } } public class BasketConfiguration : IEntityTypeConfiguration<Basket> { public void Configure(EntityTypeBuilder<Basket> builder) { builder.ToTable("tbl_basket"); builder.HasKey(e => e.Id); builder.Property(e => e.Id) .HasColumnName("ID_Record"); builder.Property(e => e.Description) .HasColumnName("Desc") .HasMaxLength(100); // Other properties configuration... } } // Other IEntityTypeConfiguration implementations for other tables... // This extension method is used to automatically load all Configurations // of the various entities public static class ModelBuilderExtensions { public static void ApplyAllConfigurations(this ModelBuilder modelBuilder) { var applyConfigurationMethodInfo = modelBuilder .GetType() .GetMethods(BindingFlags.Instance | BindingFlags.Public) .First(m => m.Name.Equals("ApplyConfiguration", StringComparison.OrdinalIgnoreCase)); var ret = typeof(T).Assembly .GetTypes() .Select(t => (t, i: t.GetInterfaces().FirstOrDefault(i => i.Name.Equals(typeof(IEntityTypeConfiguration<>).Name, StringComparison.Ordinal)))) .Where(it => it.i != null) .Select(it => (et: it.i.GetGenericArguments()[0], cfgObj: Activator.CreateInstance(it.t))) .Select(it => applyConfigurationMethodInfo.MakeGenericMethod(it.et).Invoke(modelBuilder, new[] { it.cfgObj })) .ToList(); } } 。另请参见上面的DbSet构造函数代码。 我在投放时遇到问题...

DbContext

这有可能吗?

2 个答案:

答案 0 :(得分:3)

我认为您无法避免为不同的表使用两个不同的EF对象,并且您可能不希望这样做,因为它们将来可能会有所不同。

至少需要两个类Db1PanelDb2Panel。我假设实际上“ Db”前缀是指不同的模式,而不是实际上的数据库。

但是,这应该不是一个大问题,因为C#中还有其他方法可以使它们以类似的方式工作。我想到的两个选择是让它们从相同的基类继承,或让它们实现接口:

public abstract class PanelBase
{
    public long Id { get; set; }
    // other properties
}

[Table("tbl_panel", Schema = "Db1")]
public class Db1Panel : PanelBase{}

[Table("tbl_panel", Schema = "Db2")]
public class Db2Panel : PanelBase{}

如果选择实现接口,则需要在每个类中重复这些属性,但是重构工具使此操作非常容易。

public interface IPanel
{
    public long Id { get; set; }
}

[Table("tbl_panel", Schema = "Db1")]
public class Db1Panel : IPanel
{
    public long Id { get; set; }
}

[Table("tbl_panel", Schema = "Db2")]
public class Db2Panel : IPanel
{
    public long Id { get; set; }
}

或者根据您应用程序的大小,您可以考虑使用另一个域对象命名空间,并将数据库对象映射到其中:

答案 1 :(得分:0)

您应该可以使用Table属性。有一个参数Schema,可用于设置架构名称。有关文档,请参见here。就您而言,您会得到类似

[Table("Table1", Schema="Schema1")]
public class Entity1Schema1
{
    public string Property1 {get;set;}
}

[Table("Table1", Schema="Schema2")]
public class Entity1Schema2
{
    public string Property1 {get;set;}
}

然后,您当然可以使用接口或基类来重构代码,就像已经提到的@ ste-fu。