===============================================
2020/8/12_第3次修改 ccb_warlock
更新說明:
2020/8/12:
1.增加了參考資料的內容
2020/6/8:
1.增加了在mysql上試驗的結果說明;
===============================================
最近忙於公司的業務系統,終於有時間對框架結構進行完善。在開發子系統時需要連接的是oracle中帶用戶名前綴的數據表(因為客戶提供的是其他的用戶),然而之前的框架實現中沒有對這方面的內容做考慮(因為框架中EF Core實際只使用了MSSQL,而且業務只操作了當前庫里的表(即dbo)),於是針對EF Core的實體映射還是單獨寫篇文章做個記錄。
2020/6/8,我在MySQL上也進行了試驗,這種實體映射方式也是支持的。
我在開發中采用的是Code First,所以這里不去深究記錄的內容是否在DB First中可以應用。
一、當前庫/當前用戶下,實體與表的映射
在實現的過程中,有2種映射的處理(這里以用戶實體舉例):
1)通過特性標記實體映射的表,再到DbContext中根據程序集的添加實體到模型中
// 實體定義並定義表映射
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Common.Entities; namespace Entity.Po { [Table("USER_T")] public class User : BaseEntity { [Column("NAME")] [DataType("varchar")] [MaxLength(30)] public string Name { get; set; } [Column("PASSWORD")] [DataType("varchar")] [MaxLength(50)] public string Password { get; set; } } }
// 在模型中添加實體
using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Reflection; namespace Core.Dao { public class CommonDbContext : DbContext { private static Assembly _asmEntity; private static DbConfig _dbConfig;//這個是自定義的數據庫配置,通過依賴注入獲取 private CommonDbContext(DbContextOptions options) : base(options) { } // todo:創建上下文等等 protected override void OnModelCreating(ModelBuilder modelBuilder) { LoadModelBuilder(modelBuilder); base.OnModelCreating(modelBuilder); } private static void LoadModelBuilder(ModelBuilder modelBuilder) { if (null == _dbConfig) throw new Exception("DB Configuration Not Found."); _asmEntity = Assembly.Load("Entity"); if (null == _asmEntity) throw new Exception("Entity Assembly Not Found."); var method = modelBuilder.GetType().GetMethod( "Entity", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(Type) }, null ); if (null == method) return; foreach (var type in _asmEntity.ExportedTypes) { //設計在數據庫配置信息中,增加了PO的基類標記,當該實體繼承該基類,表示該實體是個PO if (!type.IsSubclassOf(_dbConfig.BaseEntityClass)) continue; modelBuilder.Entity(type); } } } }
2)在DbContext中定義每個實體的DbSet,再在模型中添加實體並定義表映射
// 實體定義
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Common.Entities; namespace Entity.Po { public class User : BaseEntity { public string Name { get; set; } public string Password { get; set; } } }
// 在模型中添加實體並定義表映射
using Microsoft.EntityFrameworkCore; namespace Core.Dao { public class CommonDbContext : DbContext { public CommonDbContext(DbContextOptions<CommonDbContext> options) : base(options) { } // todo:創建上下文等等 public DbSet<User> User { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>(entity => { entity.ToTable("USER_T"); entity.Property(e => e.Id) .HasColumnName("ID") .HasColumnType("varchar") .HasMaxLength(36); entity.Property(e => e.Dname) .HasColumnName("NAME") .HasColumnType("varchar") .HasMaxLength(30); entity.Property(e => e.Password) .HasColumnName("PASSWORD") .HasColumnType("varchar") .HasMaxLength(50); }); } } }
二、非當前庫/非當前用戶下,實體與表的映射(帶前綴)
在MSSQL中,當連接數據庫ABC需要訪問數據庫DEF時,需要增加數據庫前綴,如下:
SELECT * FROM DEF.USER_T;
在Oracle中,當用戶A需要訪問用戶B的表時,需要增加用戶前綴,如下:
SELECT * FROM B.USER_T;
直接在“一、當前庫/當前用戶下,實體與表的映射”的表名前增加前綴是無效的,運行后會提示表或視圖不存在。
查看官方文檔(https://docs.microsoft.com/en-us/ef/core/modeling/entity-types?tabs=data-annotations)后,發現原來已經提供了一種統一的屬性“表模式”來實現該前綴的標記。
在實現的過程中,有2種映射的處理(這里以用戶實體舉例):
1)通過特性標記實體映射的表,再到DbContext中根據程序集的添加實體到模型中
// 實體定義並定義表映射(例如在MSSQL中,當連接數據庫ABC需要訪問數據庫DEF時)
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Common.Entities; namespace Entity.Po { [Table("USER_T", Schema = "DEF")] public class User : BaseEntity { [Column("NAME")] [DataType("varchar")] [MaxLength(30)] public string Name { get; set; } [Column("PASSWORD")] [DataType("varchar")] [MaxLength(50)] public string Password { get; set; } } }
// 在模型中添加實體
using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Reflection; namespace Core.Dao { public class CommonDbContext : DbContext { private static Assembly _asmEntity; private static DbConfig _dbConfig;//這個是自定義的數據庫配置,通過依賴注入獲取 private CommonDbContext(DbContextOptions options) : base(options) { } // todo:創建上下文等等 protected override void OnModelCreating(ModelBuilder modelBuilder) { LoadModelBuilder(modelBuilder); base.OnModelCreating(modelBuilder); } private static void LoadModelBuilder(ModelBuilder modelBuilder) { if (null == _dbConfig) throw new Exception("DB Configuration Not Found."); _asmEntity = Assembly.Load("Entity"); if (null == _asmEntity) throw new Exception("Entity Assembly Not Found."); var method = modelBuilder.GetType().GetMethod( "Entity", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(Type) }, null ); if (null == method) return; foreach (var type in _asmEntity.ExportedTypes) { //設計在數據庫配置信息中,增加了PO的基類標記,當該實體繼承該基類,表示該實體是個PO if (!type.IsSubclassOf(_dbConfig.BaseEntityClass)) continue; modelBuilder.Entity(type); } } } }
2)在DbContext中定義每個實體的DbSet,再在模型中添加實體並定義表映射
// 實體定義
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Common.Entities; namespace Entity.Po { public class User : BaseEntity { public string Name { get; set; } public string Password { get; set; } } }
// 在模型中添加實體並定義表映射
using Microsoft.EntityFrameworkCore; namespace Core.Dao { public class CommonDbContext : DbContext { public CommonDbContext(DbContextOptions<CommonDbContext> options) : base(options) { } // todo:創建上下文等等 public DbSet<User> User { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("DEF"); modelBuilder.Entity<User>(entity => { entity.ToTable("USER_T"); entity.Property(e => e.Id) .HasColumnName("ID") .HasColumnType("varchar") .HasMaxLength(36); entity.Property(e => e.Dname) .HasColumnName("NAME") .HasColumnType("varchar") .HasMaxLength(30); entity.Property(e => e.Password) .HasColumnName("PASSWORD") .HasColumnType("varchar") .HasMaxLength(50); }); } } }
或
using Microsoft.EntityFrameworkCore; namespace Core.Dao { public class CommonDbContext : DbContext { public CommonDbContext(DbContextOptions<CommonDbContext> options) : base(options) { } // todo:創建上下文等等 public DbSet<User> User { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>(entity => { entity.ToTable("USER_T", "DEF"); entity.Property(e => e.Id) .HasColumnName("ID") .HasColumnType("varchar") .HasMaxLength(36); entity.Property(e => e.Dname) .HasColumnName("NAME") .HasColumnType("varchar") .HasMaxLength(30); entity.Property(e => e.Password) .HasColumnName("PASSWORD") .HasColumnType("varchar") .HasMaxLength(50); }); } } }
總結:
從框架設計解耦與代碼的維護角度,我更推薦“使用特性標記實體映射的表”的方式。
因為映射的特性將會在實體定義時一起維護,這樣每次增刪改實體時就不需要修改DbContext的源碼,獨立了該實體添加的功能。
參考資料:
1.https://docs.microsoft.com/en-us/ef/core/modeling/entity-types?tabs=data-annotations