EntityFramework Core 自动绑定模型映射

时间:2022-12-31 11:12:39

笔者最近在和同事共同开发项目时,需要从他们提供的包含数据库实体类型的类库中读取实体信息绑定到自己的项目中(但是都在同一个解决方案里),所以很直接的一种方式就是把项目中所有的实体都以 public DbSet<Blog> Blogs { get; set; } 的形式加入到自己的 Context 中,但是这显然十分麻烦,而且如果又新增或减少了实体,每次又得在Context中做修改。

先放上示例的两个实体,假设它们都处于Synyi.EntityDemo这个项目类库中。其实IEntity是一个空接口,起指示作用。

namespace Synyi.EntityDemo
{
public class Blog : IEntity
{
public int BlogId { get; set; }
public string Url { get; set; } public List<Post> Posts { get; set; }
} public class Post : IEntity
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; } public int BlogId { get; set; }
public Blog Blog { get; set; }
}
}

所以有没有什么办法可以直接让 EntityFramework Core 来代劳这项工作呢?从这个想法出发,其实我们很自然地就可以想到 Context 中的 OnModelCreating 方法,在传统的 EF 6中,它也是作为实体模型属性映射的方法容器存在。如果大家看过笔者之前的那篇《EntityFramework Core 学习扫盲》,就会知道 Fluent Api 的使用都是在这个方法中的。它的方法签名如下:

protected internal virtual void OnModelCreating(ModelBuilder modelBuilder)
{
}

配置的方法容器找到了,读取实体信息也是水到渠成的一件事,我们可以直接利用对程序集的反射读取所有的内部实体信息。代码如下:

var entityTypes = Assembly.Load(new AssemblyName("存放实体类型的程序集名称")).GetTypes()
.Where(type => !string.IsNullOrWhiteSpace(type.Namespace))
.Where(type => type.GetTypeInfo().IsClass)
.Where(type => type.GetTypeInfo().BaseType != null)
.Where(type => typeof(IEntity).IsAssignableFrom(type)).ToList();

其中 typeof(IEntity).IsAssignableFrom(type) 只是为了能获取到确定的继承了 IEntity 接口的实体而已。在这一步以后,通过查看 modelBuilder 上的相应方法,我们找到了 FindEntityTypeAddEntityType 方法。所以最后的代码如下:


protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(
"Server=(localdb)\\MSSQLLocalDB;Database=ExampleDb;Trusted_Connection=True;MultipleActiveResultSets=true");
} protected override void OnModelCreating(ModelBuilder builder)
{
var entityTypes = Assembly.Load(new AssemblyName("存放实体类型的程序集名称")).GetTypes()
.Where(type => !string.IsNullOrWhiteSpace(type.Namespace))
.Where(type => type.GetTypeInfo().IsClass)
.Where(type => type.GetTypeInfo().BaseType != null)
.Where(type => typeof(IEntity).IsAssignableFrom(type)).ToList(); foreach (var entityType in entityTypes)
{
// 防止重复附加模型,否则会在生成指令中报错
if (builder.Model.FindEntityType(entityType) != null)
continue; builder.Model.AddEntityType(entityType);
} base.OnModelCreating(builder);
}

使用 Add-Migration XXUpdate-Database 指令后,我们的 ExampleDb 中就生成了相应的数据库表,一些隐藏的诸如“实体中命名为 Id 或者 ClassName+Id 的属性将自动设置为主键”的规则也会自动生效。假如目标数据库是类似于 PostgreSql 这种,数据库的表名和列名都得定义成小写字母,否则在 sql 时将不得不使用双引定义,十分的麻烦。所幸我们也可以直接在 OnModelCreating 方法中指定这一项规则。在上述方法末尾加上如下代码:

foreach (var entity in builder.Model.GetEntityTypes())
{
var currentTableName = builder.Entity(entity.Name).Metadata.Relational().TableName;
builder.Entity(entity.Name).ToTable(currentTableName.ToLower()); var properties = entity.GetProperties();
foreach (var property in properties)
builder.Entity(entity.Name).Property(property.Name).HasColumnName(property.Name.ToLower());
}

至于其他的配置,就要靠大家去挖掘了。

消失的 EntityTypeConfiguration

在传统的 EF 编程中,大家对 EntityTypeConfiguration 应该都十分的熟悉。比如如下的代码:

public class BlogConfiguration : EntityTypeConfiguration<Blog>
{
public BlogConfiguration()
{
ToTable("Blogs");
HasKey(x => x.Id);
Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("BlogId");
Property(x => x.Title).HasMaxLength(175);
HasRequired(x => x.Url).WithRequiredPrincipal();
}
} // 在 OnModelCreating 方法中加入以下代码
modelBuilder.Configurations.Add(new BlogConfiguration());

就是这样一个好用的东西,却没有随着 EF 的迁移而保留下来,在 EF Core 中,我们已经看不到它的身影了,残念ですね。不过这也不是多难解决的事情,Github上已经有人给出了相关的解决方案

做些简单的分析——一句比较完整的Fluent Api 设置方式形如 builder.Entity<Blog>().ToTable("Blogs"); 所以我们只要抓住 builder.Entity<XXX>()的返回类型 EntityTypeBuilder 做文章即可。笔者在下面也给出另一种接口+反射方式的实现(第二个参考链接中的代码并不能直接使用)。

public interface IEntityTypeConfiguration
{
} public class BlogConfiguration : IEntityTypeConfiguration
{
public BlogConfiguration(ModelBuilder builder)
{
builder.Entity<Blog>().ToTable("Blogs");
}
} public static class ModelBuilderExtensions
{
public static void ExecuteConfigurations(this ModelBuilder modelBuilder,string assemblyName)
{
var configurationTypes = Assembly.Load(new AssemblyName(assemblyName)).GetTypes()
.Where(type => !string.IsNullOrWhiteSpace(type.Namespace))
.Where(type => type.GetTypeInfo().IsClass)
.Where(type => type.GetTypeInfo().BaseType != null)
.Where(type => typeof(IEntityTypeConfiguration).IsAssignableFrom(type))
.ToList(); foreach (var type in configurationTypes)
Activator.CreateInstance(type, modelBuilder);
}
} // 在 OnModelCreating 方法中加入以下代码
builder.ExecuteConfigurations("存放实体配置的程序集名称");
base.OnModelCreating(builder);

至此,Entity Framework Core 中的自动绑定实体映射应该就告一段落了,其他的功能也很容易基于上文扩展。如果大家有更好的想法,也可以在评论中留言(这语气听起来就好像自己的文章真的会有很多读者一样)。

参考资料

  1. 《Model configuration: Entity type configuration can be factored into a class》
  2. 《Organizing Fluent Configurations into Separate Classes in EF Core 1.0》

EntityFramework Core 自动绑定模型映射的更多相关文章

  1. Entityframework core 动态添加模型实体

    重新DBContext中OnModelCreating protected override void OnModelCreating(ModelBuilder modelBuilder)  { // ...

  2. 你所不知道的库存超限做法 服务器一般达到多少qps比较好&lbrack;转&rsqb; JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2&period;1延迟加载(Lazy Loading) EntityFramework 6&period;x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制

    你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...

  3. EntityFramework 6&period;x和EntityFramework Core关系映射中导航属性必须是public?

    前言 不知我们是否思考过一个问题,在关系映射中对于导航属性的访问修饰符是否一定必须为public呢?如果从未想过这个问题,那么我们接下来来探讨这个问题. EF 6.x和EF Core 何种情况下必须配 ...

  4. EntityFramework Core如何映射动态模型?

    前言 本文我们来探讨下映射动态模型的几种方式,相信一部分童鞋项目有这样的需求,比如每天/每小时等生成一张表,此种动态模型映射非常常见,经我摸索,这里给出每一步详细思路,希望能帮助到没有任何头绪的童鞋, ...

  5. ASP&period;NET Core MVC&sol;WebAPi 模型绑定探索

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  6. EntityFramework Core 2&period;x (ef core) 在迁移中自动生成数据库表和列说明

    在项目开发中有没有用过拼音首字母做列名或者接手这样的项目? 看见xmspsqb(项目审批申请表)这种表名时是否有一种无法抑制的想肛了取名的老兄的冲动? 更坑爹的是这种数据库没有文档(或者文档老旧不堪早 ...

  7. ASP&period;NET Core MVC&sol;WebAPi 模型绑定探索 转载https&colon;&sol;&sol;www&period;cnblogs&period;com&sol;CreateMyself&sol;p&sol;6246977&period;html

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

  8. EntityFramework Core一劳永逸动态加载模型,我们要知道些什么呢?

    前言 这篇文章源于一位问我的童鞋:在EntityFramework Core中如何动态加载模型呢?在学习EntityFramwork时关于这个问题已有对应园友给出答案,故没有过多研究,虽然最后解决了这 ...

  9. 【转】ASP&period;NET Core MVC&sol;WebAPi 模型绑定探索

    前言 相信一直关注我的园友都知道,我写的博文都没有特别枯燥理论性的东西,主要是当每开启一门新的技术之旅时,刚开始就直接去看底层实现原理,第一会感觉索然无味,第二也不明白到底为何要这样做,所以只有当你用 ...

随机推荐

  1. UIScrollView的delegate方法妙用之让UICollectionView滑动到某个你想要的位置

    一个UICollectionView有好多个cell,滑动一下,谁也不知道会停留在哪个cell,滑的快一点,就会多滑一段距离,反之则会滑的比较近,这正是UIScrollview用户体验好的地方. 如果 ...

  2. &lbrack;转载&rsqb;Python &amp&semi; Selenium -- 页面加载时间过长&amp&semi;启动指定FF

    原文链接:https://my.oschina.net/u/2344787/blog/400507?p={{page}} 1. selenium webdriver在get方法会一直等待页面加载完毕才 ...

  3. BOM初始状态配置

    一个很简单的东西:有些公司在建BOM的时候,可能不是一次性建好,或者是想需要审核或者什么的,先不让使用. 其实这是SPRO里面配置的...路径:生产->基本物料->物料清单->物料单 ...

  4. think straight系列读书笔记之《暗时间》

    一周一篇读书笔记,这是第零篇,为啥从零计数,你们懂的~   大二读了<暗时间>,这本书带我进入了心理学的大门,让我开始关注思维,专注,效率,认知,记忆等东西.两年之后重读这本书,依然收获很 ...

  5. javascript学习初衷

    很久没有过来写东西了,由于要做小网页,介于不懂javascript,一味的去爬其他站点的代码下来,却不能*组合,控制,达到自己想要的效果, 于是只能沉下心,javascript从头学起,还记得张老师 ...

  6. centos命令自动补全增强

    CentOS默认没有像Ubuntu系统一样命令参数补全功能,例如yum install无法补全.通过安装bash-completion安装命令参数补全增强. CentOS6 默认情况下,CentOS6 ...

  7. Learning Structured Representation for Text Classification via Reinforcement Learning 学习笔记

    Representation learning : 表征学习,端到端的学习 pre-specified  预先指定的 demonstrate  论证;证明,证实;显示,展示;演示,说明 attempt ...

  8. Orchard是如何工作的&quest;

    文章翻译自http://docs.orchardproject.net/Documentation/How-Orchard-works 对Orchard的理解还不深刻,翻译可能有不好的地方.     ...

  9. sql server&colon;查詢系統表

    ---查看所有存储过程或视图的位置 select a.name,a.[type],b.[definition] from sys.all_objects a,sys.sql_modules b whe ...

  10. Android全局可调试(ro&period;debuggable &equals; 1)的一种另类改法

    网上流传比较多的,是重打包boot.img.读aosp的init进程源码,发现通过patch init进程也可以实现相同目的. 首先看一下init进程对ro只读属性的检查: /* property_s ...