C# 使用单个实体框架Core DbContext管理具有同名表的多个数据库模式
在.NET Core 2.1库中我需要访问在多个模式中组织的C# 使用单个实体框架Core DbContext管理具有同名表的多个数据库模式,c#,.net-core,entity-framework-core,database-schema,dbcontext,C#,.net Core,Entity Framework Core,Database Schema,Dbcontext,在.NET Core 2.1库中我需要访问在多个模式中组织的MySQL数据库,这些模式中的表可以具有相同的名称。我无法对DB进行任何更改,因为它来自另一家公司。 对于大多数表,我需要只读访问,并且希望使用单EF CoreDbContext 实际上,我在初始化过程中收到以下错误消息: InvalidOperationException:无法将表“tbl_panel”用于 实体类型“Db2Panels”,因为它正用于实体 键入“Db1Panels”,它们之间没有关系 主键 我认为问题的关键在于配置方
MySQL
数据库,这些模式中的表可以具有相同的名称。我无法对DB进行任何更改,因为它来自另一家公司。
对于大多数表,我需要只读访问,并且希望使用单EF CoreDbContext
实际上,我在初始化过程中收到以下错误消息:
InvalidOperationException:无法将表“tbl_panel”用于
实体类型“Db2Panels”,因为它正用于实体
键入“Db1Panels”,它们之间没有关系
主键
我认为问题的关键在于配置方法,它不应该只调用一次,而应该调用N次,对于具有不同模式的实体的每个实例都调用一次(db\u machine\u 1.tbl\u panel
,db\u machine\u 2.tbl\u panel
,等等)。
我怎样才能达到我的目标
这是我的实际实现
数据库模式
// 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 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();
}
}
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();
}
}
配置
// 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 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();
}
}
这有可能吗?我认为,对于不同的表,您无法避免使用两个不同的EF对象,而且您可能不应该这样做,因为它们在将来的某个时候可能会出现分歧 您至少需要两个类
Db1Panel
和Db2Panel
。我假设“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; }
}
或根据应用程序的大小,您可以考虑拥有域对象的另一个命名空间,并将数据库对象映射到它中:
您应该能够使用<代码>表< /C>属性。有一个参数Schema
,允许您设置架构名称。有关文档,请参阅。在你的情况下,你会得到
[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。您是指具有多个模式的单个数据库吗?也许这会有所帮助:@KiranJoshi具有多个模式的单个MySQL数据库实例。
db\u machine\u X
前缀是架构名称。我有一个用户可以访问所有这些内容。@Isma问题是一样的。他对不同的模式使用了一个DbContext
,到目前为止,对我来说一切都还可以。但我也希望避免实体对象重复,而是希望在不同的DbSet
对象中多次重复使用它们,其中每个对象都应该引用相同的实体,这些实体应该配置为映射不同的模式(以及不同的表)。我相信这是唯一的方法,由于单个实体类不能与多个DbSet
(表)关联。@ste fu Yes,db
前缀标识MySQL架构。我想避免实体的重复,因为我的模式很多,其中类似的表也很多…@ste fu我做了一些实验,但正如@IvanStoev所说,没有机会将单个实体与多个DbSet
属性关联起来…:-(然而,现在我使用了基类抽象类和派生类解决方案,我仍然想知道是否至少可以创建基类的数组(public DbSet[]Panels{get;}
),其中包括我的所有派生类。请查看更新问题。