简体   繁体   English

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

[英]Using a single Entity Framework Core DbContext to manage multiple database schemas with homonymous tables

In a .NET Core 2.1 library I need to access to a MySQL database organized in multiple schemas with tables that can have the same name across those schemas . .NET Core 2.1库中,我需要访问以多个模式组织的MySQL数据库,其中的表可以在这些模式中具有相同的名称 I can't make any changes to the DB since it comes from another company. 我无法对数据库进行任何更改,因为它来自另一家公司。 For most of the tables I need a read-only access and I'd like to use a single EF Core DbContext . 对于大多数表,我需要只读访问权限,并且我想使用单个EF Core DbContext

Actually I get this error message during initialization: 实际上我在初始化期间收到此错误消息:

InvalidOperationException: Cannot use table 'tbl_panel' for entity type 'Db2Panels' since it is being used for entity type 'Db1Panels' and there is no relationship between their primary keys. InvalidOperationException:不能将表'tbl_panel'用于实体类型'Db2Panels',因为它用于实体类型'Db1Panels',并且它们的主键之间没有关系。

I think that the crux of the matter mainly resides in the configuration methods, which should be called not just once but N times, one for each instance of the entity with different schema ( db_machine_1.tbl_panel , db_machine_2.tbl_panel , etc.). 我认为问题的症结主要在于配置方法,配置方法不仅应该被调用一次,而且应该被调用N次,一个用于具有不同模式的实体的每个实例( db_machine_1.tbl_paneldb_machine_2.tbl_panel等)。 How can I reach my goal? 我怎样才能实现目标?

This is my actual implementation. 这是我的实际实施。

Database schemas 数据库模式

// 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...

DbContext configuration DbContext配置

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

Objects 对象

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...
}

Configurations 配置

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

UPDATE about base class arrays 关于基类数组的更新

After creating base abstract classes and derived ones, I'd like to merge all the derived class objects into a single array to be able to access the specific DbSet by using the schema number. 在创建基本抽象类和派生类之后,我想将所有派生类对象合并到一个数组中,以便能够使用模式号访问特定的DbSet See also above code of DbContext constructor. 另请参见上面的DbContext构造函数代码。 I'm having problems with casting... 我在施法方面遇到问题......

List<DbSet<Panel>> dbPanelList = new List<DbSet<Panel>>();
dbPanelList.Add((DbSet<Panel>)Db1Panels.Select(g => g as Panel)); // NOT WORKING! Cast Exception
dbPanelList.Add((DbSet<Panel>)Db2Panels.Cast<DbSet<Panel>>()); // NOT WORKING! Cast Exception
Panels = dbPanelList.ToArray();

Is this possible somehow? 这有可能吗?

I think you can't get away from having two different EF objects for the different tables, and you probably shouldn't as they may diverge at some point in the future. 我认为你不能摆脱为不同的表格拥有两个不同的EF对象,你可能不应该因为它们在将来某些时候可能会分歧。

At a minimum you need two classes Db1Panel and Db2Panel . 至少你需要两个类Db1PanelDb2Panel I assume that actually the "Db" prefix is meant to meant a different schema, not actually a different database. 我假设实际上“Db”前缀意味着不同的模式,而不是实际上不同的数据库。

However that shouldn't be a big problem as there are other ways within C# of making them behave in similar fashions. 然而,这不应该是一个大问题,因为在C#中有其他方式可以使它们以类似的方式运行。 Two options that spring to mind are having them inherit from the same base class, or have them implement an interface: 我们想到的两个选项是让它们从同一个基类继承,或让它们实现一个接口:

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

If you chose to implement the interface you would need to repeat the properties in each class, but refactoring tools make this quite easy. 如果您选择实现界面,则需要在每个类中重复属性,但重构工具使这很容易。

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

Or depending on the size of your application you could consider having another namespace of domain objects and just map the database objects into it: 或者,根据应用程序的大小,您可以考虑使用域对象的另一个命名空间,并将数据库对象映射到其中:

You should be able to use the Table attribute. 您应该能够使用Table属性。 There's a parameter Schema that allows you to set the schema name. 有一个参数Schema ,允许您设置模式名称。 See here for documentation. 请参阅此处获取文档 In your case you'd get something like 在你的情况下,你会得到类似的东西

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

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

And then of course you can use interfaces or base classes to refactor your code as @ste-fu already mentioned. 当然,你可以使用接口或基类来重构代码,就像已经提到的@ ste-fu一样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 实体框架和在单个上下文中具有多个模式的迁移 - Entity Framework and Migration with multiple schemas in a single Context 使用Entity Framework Core获取多个表 - Get multiple tables using Entity Framework Core 在Entity Framework Core上使用using()封装DbContext - Encapsulating DbContext with using() on Entity Framework Core 实体框架和多个模式 - Entity Framework and multiple schemas 使用一个DBContext连接一次到数据库,在实体框架中执行多个Linq到实体查询 - Do Multiple Linq To Entity Queries In Entity Framework Using One DBContext Connect Only Once To The Database dotnet Core - 实体框架 - 多个 DbContext 选择错误的 DbContext - dotnet Core - Entity Framework - Multiple DbContexts choosing wrong DbContext Entity Framework 4 从具有多个模式的模型生成数据库 - Entity Framework 4 Generate Database From Model With Multiple Schemas 使用Entity Framework MVC5连接多个表和单个输出 - Join multiple tables and single output using Entity Framework mvc5 使用Entity Framework以单一方法在多个表上插入记录 - Inserting records on multiple tables in a single method using Entity Framework 使用Entity Framework Core时应该处置DbContext吗 - Should I dispose DbContext when using Entity Framework Core
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM