应无所住,而生其心
排名
1
文章
860
粉丝
112
评论
163
net core webapi post传递参数
庸人 : 确实坑哈,我也是下班好了好几次,发现后台传递对象是可以的,但...
百度编辑器自定义模板
庸人 : 我建议换个编辑器,因为现在百度富文本已经停止维护了,用tinymec...
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术

新版EF自定义表名主键规则,统一并发列与级联删除等规则处理

6812人阅读 2021/12/22 16:20 总访问:5182460 评论:0 收藏:0 手机
分类: EF

EF根据模型生成表的时候会默认加上复数,以前要想解决这个写法很简单,把这个复数规则删除掉即可

  1. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  2. {    
  3.     //删除掉默认添加的复数形式规则
  4.     modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
  5. }

但是这种modelBuilder.Conventions这种写法已经不能使用了



  下面说一下在新版中如何实现,自定义表名规则



添加一些扩展方法备用:

  1. public static class EFExtands
  2. {
  3.     public static IEnumerable<IMutableEntityType> EntityTypes(this ModelBuilder builder)
  4.     {
  5.         return builder.Model.GetEntityTypes();
  6.     }
  7.     public static IEnumerable<IMutableProperty> Properties(this ModelBuilder builder)
  8.     {
  9.         return builder.EntityTypes().SelectMany(entityType => entityType.GetProperties());
  10.     }
  11.     public static IEnumerable<IMutableProperty> Properties<T>(this ModelBuilder builder)
  12.     {
  13.         return builder.EntityTypes().SelectMany(entityType => entityType.GetProperties().Where(x => x.ClrType == typeof(T)));
  14.     }
  15.     public static void Configure(this IEnumerable<IMutableEntityType> entityTypes, Action<IMutableEntityType> convention)
  16.     {
  17.         foreach (var entityType in entityTypes)
  18.         {
  19.             convention(entityType);
  20.         }
  21.     }
  22.     public static void Configure(this IEnumerable<IMutableProperty> propertyTypes, Action<IMutableProperty> convention)
  23.     {
  24.         foreach (var propertyType in propertyTypes)
  25.         {
  26.             convention(propertyType);
  27.         }
  28.     }
  29. }

现在要实现删除表名的默认复数形式就很简单了,代码如下

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3.     //删除默认的表名复数规则,表名按照类名为标准
  4.     modelBuilder.Model.GetEntityTypes().Configure(et => et.SetTableName(et.DisplayName()));
  5. }

然后执行一次数据库迁移,就可以看到数据的表,已经没有自动加上复数了,和实体名称保持一致了


这样就可以直接实现了,但是如果我们想有一个特殊的命名规则呢,比如最简单的Table特性来自定义表名

用上面的写法是不行的,它直接统一的把生成的表名设置成了实体名一样的名称



要想自定义表名规则来解决这个问题,我们需要知道两个内容,一个是如何拿到Table特性对应的表名与实体对应的表名,就是能够拿到两种不同情况的表名,还有一个是如何区分实体上面有没有Table这个特性。当然我们分析这个不仅仅是为了解决这一个问题,其他类似的规则或者更特殊的自定义规则都这种通过这种思路去解决,灵活运用。

先思考第一点:如何拿到Table特性对应的表名与实体对应的表名,就是能够拿到两种不同情况的表名
其实上面那个设置表名规则我们已经看到了DisplayName就是实体的名字嘛

那另外一个特性对应的表名怎么拿呢,根据上面的方法我们可以顺势推出来其他可能的方法,实在不知道我们可以挨着分析一下可能的方法在调试一下即可,具体的方法过程就不细说了,我们直接看代码:

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3.     //删除默认的表名复数规则,表名按照类名为标准
  4.     //modelBuilder.Model.GetEntityTypes().Configure(et => et.SetTableName(et.DisplayName()));
  5.     //分析表名规则
  6.     modelBuilder.Model.GetEntityTypes().ToList().ForEach(a =>
  7.     {
  8.         string tableName = a.GetTableName();//默认的规则按照这个名称来实现的
  9.         string defaultTableName = a.GetDefaultTableName();
  10.         string displayName = a.DisplayName();//实体名称
  11.         var typeName = a.GetType();
  12.     });
  13. }

这里我们猜测了一下三个可能的表名,GetTableName(),GetDefaultTableName(),DisplayName(),我们运气打断点调试一下OnModelCreating方法直接的过程就知道了

根据分析得知,GetTableName()就是默认的规则,比如默认给你加复数。

或者有Table特性的时候GetTableName就是加特性的名字。(注意在分析表名规则的时候先把方法设置规则的去掉,不然已经设置表名规则按照DisplayName标准来,那么下面在执行的时候三种方法获取的内容都完全一样,就没有分析的意义了)

到这一步为止我们第一步两个表名都已经能拿到了,第一步的问题算解决了。然后我们可以根据是否有Table特性来自定义决定使用什么命名规则,甚至是其他千奇百怪的规则都可以自定义。


然后是第二点如何确定实体类有没有table这个特性 
我们可以首先想到的是反射取特性,但是我们看到了FindAnnotation和GetAnnotations方法我们可以试一下,看微软注释是FindAnnotation找不到就返回空GetAnnotations找不到就报错,所以我们可以试一下FindAnnotation方法来验证一下我们的猜想。
微软官方对这个方法的解释:https://docs.microsoft.com/zh-tw/dotnet/api/microsoft.entityframeworkcore.infrastructure.iannotatable.findannotation?view=efcore-5.0

代码如下我们获取一下这个特性为空的试试,如果是三个就对了,因为我们是4个实体,只有一个student实体是有Table特性的

但是实际情况并不是我们猜想的那样,而是4个实体获取的都为空

后面才发现这个方法不是那样用的,不是我们想象的取实体上面有没有那个特性的,而是可以通过Relational:TableName,Relational:ColumnName,Relational:ColumnType来取表名取列明这些,比如这样:

在做其他的时候我们也可以尝试用一下这个方法但是这里用这个并不行。

所以我们还是考虑反射,反射来获取特性就比较简单的,我们随便来写两个类测试一下。

  1. //反射取特性
  2. object[] objArray = typeof(Student).GetCustomAttributes(typeof(TableAttribute), false);//结果是1个
  3. object[] objcourseArray = typeof(Course).GetCustomAttributes(typeof(TableAttribute), false);//结果是0个

结果完全是没有猜想的那样,然后我们就可以实际用起来了

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3.     //没有TableAttribute特性的就使用和model一样的类名
  4.     modelBuilder.Model.GetEntityTypes().Where(a => a.GetType().GetCustomAttributes(typeof(TableAttribute), false).Count() == 0)
  5.                     .Configure(et => et.SetTableName(et.DisplayName()));
  6.     //有TableAttribute特性的就使用和Table特性规定的类名
  7.     modelBuilder.Model.GetEntityTypes().Where(a => a.GetType().GetCustomAttributes(typeof(TableAttribute), false).Count() > 0)
  8.                     .Configure(et => et.SetTableName(et.GetTableName()));
  9. }

但是我们会发现并没有成功,它并没有获取到TableAttribute特性,其实这里也是一个坑,因为这里的a并不是和实体一样的,是ef在实体上封装过的,要拿到实体的具体类型使用a.ClrType就可以了,这点我们可以调试得知,所以要实现这个功能最终代码如下:

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3.     //没有TableAttribute特性的就使用和model一样的类名
  4.     modelBuilder.Model.GetEntityTypes().Where(a => a.ClrType.GetCustomAttributes(typeof(TableAttribute), false).Count() == 0)
  5.                     .Configure(et => et.SetTableName(et.DisplayName()));
  6.     //有TableAttribute特性的就使用和Table特性规定的类名
  7.     modelBuilder.Model.GetEntityTypes().Where(a => a.ClrType.GetCustomAttributes(typeof(TableAttribute), false).Count() > 0)
  8.                     .Configure(et => et.SetTableName(et.GetTableName()));
  9. }

还是比较简洁的,然后我们迁移一下数据库就能看到规则已经成功了。除了student表其他表名都和实体名一样



下面贴个第二种方法:勉强算第二种吧,其实本质都一样

  1. string typestr = "System.ComponentModel.DataAnnotations.Schema.TableAttribute".Trim();
  2. modelBuilder.Model.GetEntityTypes().ToList().ForEach(a =>
  3. {
  4.     IEnumerable<CustomAttributeData> m = a.ClrType.CustomAttributes;//拿到自定义属性
  5.     foreach (var item in m)
  6.     {
  7.         string type = item.AttributeType.FullName.Trim();
  8.         if (type == typestr)
  9.         {
  10.             a.SetTableName(a.GetTableName());
  11.             return;
  12.         }
  13.     }
  14.     a.SetTableName(a.GetDefaultTableName());
  15. });



  当然其他的,比如处理并发冲突列的统一标注,类型判断等也比较方便



统一把Timestamp标注为ConcurrencyToken:

  1. modelBuilder.Properties().Where(x => x.Name == "Timestamp").Configure(p => p.IsConcurrencyToken = true);

类型判断:

  1. modelBuilder.Model.GetEntityTypes()            
  2.     .Where(x => !x.ClrType.IsSubclassOf(typeof(ValueObject)))
  3.     .Configure(et => et.SetTableName(et.DisplayName()));

或者:

  1. modelBuilder.Model.GetEntityTypes()
  2.     .Where(x => x.ClrType.BaseType == typeof(BaseEntity))
  3.     .Configure(et => et.SetTableName(et.DisplayName()));


统一设置主键规则

  1. modelBuilder.Properties()
  2.     .Where(x => x.Name.ToLower() == "id")
  3.     .Configure(p => p.SetColumnName(p.DeclaringEntityType.ShortName() + "Id"));

方法2:

  1. modelBuilder.Model.GetEntityTypes().ToList().ForEach(a =>
  2. {
  3.     var t = a.GetProperties();
  4.     foreach (var item in t)
  5.     {
  6.         string name = item.Name;
  7.         string TableName = a.GetDefaultTableName();
  8.         if (name.ToLower() == "id")
  9.         {
  10.             item.SetColumnName(TableName + "Id");
  11.         }
  12.     }
  13. });



 新版ef级联删除



和以前的写法有点不同现在没有modelBuilder.Conventions这种用法了,单独开启级联删除的方法也和以前不太一样

以前的写法:

  1. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  2. {    
  3.  //删除掉级联删除的规则
  4.  modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
  5. }

以前单独的控制写法:

  1. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  2. {
  3.     //关闭级联删除
  4.     modelBuilder.Entity<Pet>().HasRequired(a => a.userinfo).WithMany(a => a.pets).HasForeignKey(a => a.UserId).WillCascadeOnDelete(false);
  5. }

以前使用的是WillCascadeOnDelete方法,而且参数只有true和false,就是是否开启级联查询

.NET CORE中EF级联删除的写法
使用的是OnDelete,而且参数是枚举不止,ture和false

  1. protected override void OnModelCreating(ModelBuilder modelBuilder)
  2. {
  3.     modelBuilder.Entity<Student>().Property(p => p.RowVersion).IsConcurrencyToken();
  4.     modelBuilder.Entity<UserParent>().HasMany(a => a.Students).WithOne(a => a.UserParent).HasForeignKey(a => a.MyUserParentId).OnDelete(DeleteBehavior.NoAction);
  5. }

DeleteBehavior.NoAction:就是不开启级联删除,会出发外键约束。
DeleteBehavior.Cascade:就是开启级联删除,是默认值
其他还有:DeleteBehavior.SetNull,DeleteBehavior.ClientSetNull,Restrict等

欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)

评价

如何升级到 Ansible 的最新版

如何升级 Ansible 的版本[TOC] 访问 https://pypi.org/project/ansible/#files 找到 ansible-{版本}.tar.gz #下载到本...

Docker 系统性入门+进阶实践(2021最新版

ocker 系统性入门+进阶实践(2021最新版)- docker安装部署安装依赖包sudoyuminstall-yyum-utilsdevice-mapper-persistent-...

uni-app实现app更新,下载更新新版

在index.vue中的onload方法里面或者app.vue中的onLaunch中使用如下部分即可: update() { var _this = this; un...

NET Core 使用 EF Code First

下面这些内容很老了看这篇:https://www.tnblog.net/aojiancc2/article/details/5365 项目使用多层,把数据库访问...

C out、rEF关键字的用法和区别

说说自己对out、ref的认识,面试问到的几率很高哟。out:classProgram { /* *out、ref都是引用传递,传递后使用都会改变...

Net Core使用EF之DB First

一.新建一个.net core的MVC项目新建好项目后,不能像以前一样直接在新建项中添加ef了,需要用命令在添加ef的依赖二.使用Nug...

EF6动态添加条件

例如我们要匹配一个集合中的所有关键字,我们首先想到的做法是这样的publicList&lt;Article&gt;GetArtByKeys(List&lt;strin...

Net Core使用依赖注入来装载EF的上下文对象

妹子情人节快乐~.net core中用了不少的依赖注入,官方文档中也推荐使用。这样使用依赖注入来管理ef对象,还是比较科学,比如...

NET CORE配置EF连接字符串。windows验证的连接字符串配置

在appsettings.json中配置好连接字符串{&quot;ConnectionStrings&quot;:{ &quot;BloggingDatabase&quot;:&quot;Server=(lo...

使用REFit框架访问REST接口

改装是一个类型安全的REST开源库,是一套基于RESTful架构的.NET客户端实现,内部使用HttpClient类封装,可通过改装更加简单...

docker启动报错 No dEFault Boot2Docker ISO found locally downloading the latest

这是因为,启动时如果检测到没有 Boot2Docker,就会去下载,这个下载过程出现网络连接上的错误了,导致启动失败。可以去下...

扩展EF自动映射需要查询的字段(表达式树Expression),动态构建返回值

Entity Framework 动态构造select表达式比如我们需要返回某些字段会采用如下的写法但是发现每次都去写select如果字段很多不...

layer弹窗+EF引入独立页面进行操作

使用layer弹窗进行操作的时候我们可以使用type=2,来把一个独立的页面指向content内容例如我们有一个表格,点击添加后,我...

Entity Framework常用查询,EF joinEF多表联查,原生sql。EF 多表查询。AsNoTracking

直接执行sql语句//全表查询 List&lt;Users&gt;ulist=se.Database.SqlQuery&lt;Users&gt;(&quot;select*fromusers&quot;).T...

EF Code First常用命令

Enable-Migrations启用数据库迁移Enable-Migrations –EnableAutomaticMigrations 启动自动迁移Add-Migration Name(名字可...