EF Core 实体映射表或视图
===============================================
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