EntityFramework Core动态加载模型,我们要知道些什么呢?

这篇文章源于一位问我的童鞋:在EntityFramework Core中如何动态加载模型呢?在学习EntityFramwork时关于这个问题已有对应童鞋给出答案,故没有过多研究,虽然最后解决了这位童鞋提出的问题,但是当我再次深入研究时,发现原来问题远没有这么简单,由此而引申出来的问题值得我花了一点时间去思考,个人感觉很有价值和必要,所以在此做下记录或许能够帮助到有需要的童鞋,研究EntityFramework Core动态加载模型的历程由此而开始,接下来跟随我的脚步一起去瞧瞧。

我们依然从零开始,创建EF Core 2.x控制台程序,然后给出本节内容我们需要用到的模型,如往常一样我们已经用烂了的Blog和Post,如下:

接下来是我们需要用到的上下文,如下:

我们看到上述表名是模型的复数形式,接下来我们查询博客列表,如下:

以上演示的则是我们一贯的做法,这个时候就有人问了,随着业务变更,我们都得在上下文中添加多个模型的DbSet属性,能否避免此重复操作的情况,将我们后续添加的模型动态加载到上下文中去从而提高工作效率让我们着重关注业务呢?

当然是阔以的,这里我们借助实际场景来说明,我们将模型通常都会放在一个类库中,比如我们将上述Blog和Post放在如下图Model类库中。

接下来我们要做的则是在初始化模型时,获取模型所在的程序集,然后将该程序集中的模型通过ModelBuilder生成,正常情况下我们是调用如下Entity方法配置模型,如下:

有了如上分析,我们就通过反射获取上述Entity方法,然后调用通过ModelBuilder调用反射得到的Entity方法,如下:

当然上述加载模型程序集的方式根据我们实际项目情况而定,同时在我们过滤程序集中类型时也同样如此,比如若是DDD架构,对于仓储都会封装一层进行基本操作的仓储,此时其他模型仓储必派生于基仓储,通过基本仓储模型进行过滤等等。

接下来我们将上下文中添加的DbSet<Blog>和DbSet<Post>给去掉,如下:

然后我们直接通过上下文中的Set方法来查询数据,如下:

上述我们多添加了一行确保数据库模型已提前被创建,这是必要的,其背后本质就是通过命令进行迁移,要不然在加载模型时应该会报错,当然若在Web应用程序中,我们在Configure方法中也同样添加如下一行:

此时将会抛出上述异常,这是为何呢?这是因为数据库表名是和如上上下文中我们已经注释掉的DbSet包含的模型属性名称一致

若我们将上述DbSet包含的模型属性的注释给去掉,当加载DbSet属性时将获取该属性名称和我们配置的Schema作为架构名称(不配置,默认为空),我们通过如下源码可得知(当然我们通过SQL Server Profiler生成的SQL语句也可得知)

上述我们只是得到最终表的架构和名称而已,那么默认表名称是怎样的呢?当我们查询时,会从上述DatasetTable类中去获取表名,如下:

它具体是什么时候调用的呢,我们看如下代码:

到了这里我们并未看到任何有效的信息,只是将该类中得到的表名和架构设置到ToTable方法中,让我们从头开始梳理思路

因为从一开始我们并未通过注解或者Fluent APi去显式配置表名,所以此时必将走EntityFramework Core的默认约定,思路已经很清晰,最终我们找到获取表名的方法,如下:

到这里我们看到了获取表名的方法,我们继续往下走,看看具体是如何获取表名的呢?

因为对应类型并未有其派生类,接下来去获取注解的表名,此时我们也并未通过注解设置表名,到这里我们也能明白若是我们通过注解在对应模型上添加与数据库表名一致的复数即可解决问题。我们继续往下走,最后调用获取默认表名的方法:

首先我们并未设置模型的OwnType,接下来调用方法根据注释意为:获取模型是否有定义的导航类型,看到这里时,我认为Post不就是Blog的导航吗,此方法被暴露出来可供我们调用,当我去验证时发现结果却返回false,不禁让我心生疑窦

var hasDefiningNavigation = context.Model
.FindEntityType(typeof(Blog))
.HasDefiningNavigation();

既然返回false,接下来继续往下看ShortName方法,如下:

到这里我们总算明白了,模型类型不为空获取模型的名称,经验证其ShortDisplayName方法返回值就是模型名称即Blog,所以才抛出最开始异常对象名无效,我们也可通过如下代码验证表名是不是Blog

var mapping = context.Model.FindEntityType(typeof(Blog))
.Relational();
var schema = mapping.Schema;
var tableName = mapping.TableName;

注意:若您是EntityFramework Core 3.x版本上述获取架构和表名等方式已经修改成直接针对模型的扩展方法。如下:

var mapping = context.Model.FindEntityType(typeof(Blog));
var schema = mapping.GetSchema();
var tableName = mapping.GetTableName();pping.GetTableName();

所以对于EF Core而言,默认的表名就是模型名称,若我们以DbSet属性暴露模型则以DbSet属性名称作为表名,同样我们也验证下,我们将最开始注释掉的DbSet<Blog> Blogs,修改成如下:

 public DbSet<Blog> BlogAlias { get; set; }

所以若采用动态加载模型,如果数据库表名就是模型名称,那么没毛病,否则我们应该根据项目约定而需要进行相应的修改才行,如最开始给出的数据库表名为复数为例,此时我们还需修改数据库表名的约定,在OnModelCreating方法添加如下代码:

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{var tableName = entityType.Relational().TableName;modelBuilder.Entity(entityType.Name).ToTable($"{tableName}s");
}

同理针对EntityFramework Core 3.x版本修改成如上注意说明,接下来我们再次注释掉上述验证时暴露出的DbSet,最后查询结果如下:

事情还未结束,配置动态加载模型后,由上只是证明关系映射等没问题,接下来我们如下配置owned Type,我们将看到会抛出异常。

很显然,虽然我们只是加载了模型,但是对于映射关系通过约定可以得到,而owned Type必须显式配置,所以在遍历生成模型时,我们恐怕还需要额外处理owned Type,遗留的这个问题等待空闲时再弄下,暂时就到这里吧。

本节我们详细讲解了在EntityFramework Core如何动态加载模型,同时针对动态加载模型所带来的问题也只是进行了一丢丢的论述,来,我们下一个结论:在EntityFramework Core中根据约定表名为DbSet属性名称,若在上下文中未暴露DbSet属性,则表名为模型名称,如果采用动态加载模型,那么表名必须与模型名称一致,否则将抛出异常,当然我们也可以根据实际项目约定更改表名。通过本节动态加载模型将引入下一节内容:EntityFramework Core表名原理解析,感谢您的阅读,下一节内容相信很快就会到来。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312286.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++多态的基本语法与原理剖析

多态分为两类 1.静态多态&#xff1a;函数重载和运算符重载属于静态多态&#xff0c;复用函数名 2.动态多态&#xff1a;派生类和虚函数实现运行时多态 静态多态和动态多态区别&#xff1a; 1.静态多态的函数地址早绑定&#xff0c;编译阶段确定函数地址 2.多态多态的函数地址…

从零开始开发 VS Code 插件之 Translator Helper

本文目录Translator Helper 介绍开发概述创建第一个VS Code Extension需求分析操作文本调用Google Translation API实现核心功能配置命令插件配置测试插件打包插件发布插件CI/CDIcon及README小结Translator Helper 介绍 微软 Docs 网站上线之后&#xff0c;我发现很多中文内容是…

【实战 Ids4】║ 又一个项目迁移完成(MVC)

迎周一&#xff0c;腊月十九&#xff0c;小年倒计时新年还有两周时间就要到了&#xff0c;学习可不能停&#xff0c;这几天一直在加班调休&#xff0c;周末也如此&#xff0c;不过也是趁着半夜凌晨的时间&#xff0c;继续迁移我的项目到IdentityServer4统一认证授权中心Blog.Id…

C++string容器-赋值操作

功能描述&#xff1a; 给string字符串进行赋值 代码如下&#xff1a; #include <iostream> using namespace std; #include <cstring>void test01() {string str1;str1 "hello world";cout << "str1 " << str1 << endl;…

为什么说云原生会成为未来企业技术变迁的趋势

云原生是当下的热点话题&#xff0c;但是很多人对云原生有很多误解&#xff0c;特别是传统产业物联网或工控、物联网行业对云原生显得"后知后觉"。与其在这里说是预测&#xff0c;不如说是现在进行时&#xff0c;只是由于传统产业本身的技术包袱和组织个人认识程度差…

C++vector容器-构造函数

vector数据结构和数组非常相似&#xff0c;也称为单端数组&#xff0c;在数组的尾段可以做插入&#xff0c;删除操作 vector不同于普通数组&#xff0c;vector可以动态扩展 动态扩展&#xff1a;并不是在原空间之后续接新空间&#xff0c;而是找更大的内存空间&#xff0c;然…

BeetleX网关之请求聚合

在网关服务中请求聚合是允许把多个请求打包成一个响应给请求方&#xff0c;这样不仅可以节省请求方的请求数量&#xff0c;还可以根据需求的情况整合不同业务数据响应请求。BeetleX.Bumblebee虽然并没有内置这一功能&#xff0c;但可以通过扩展的形式来支持请求聚合&#xff0c…

C++vector容器-赋值操作

vector赋值操作 功能描述&#xff1a; 给vector容器进行赋值 函数原型&#xff1a; 代码如下&#xff1a; #include <iostream> using namespace std; #include <vector> //vector赋值操作void printVector(vector<int> &v) {for (vector<int >…

EntityFramework Core表名原理解析,让我来,揭开你神秘的面纱

上一节我们针对最开始抛出的异常只是进行了浅尝辄止的解析&#xff0c;是不是有点意犹未尽的感觉&#xff0c;是的&#xff0c;我也有这种感觉&#xff0c;看到这里相信您和我会有一些疑惑&#xff0c;要是我们接下来通过注解、Fluent APi、DbSet分别对表名进行如下设置&#x…

C++vector容器-容量和大小

vector容量和大小 功能描述&#xff1a; 对vector容器的容量和大小操作 函数原型&#xff1a; 代码如下&#xff1a; #include <iostream> using namespace std; #include <vector> //vector容器的容量和大小操作void printVector(vector<int > &v) {…

openresty+mysql+乱码_openresty记录响应body乱码问题

问题背景最近新上了一个功能&#xff0c;openresty通过syslog记录请求日志&#xff0c;然后由logstash推送至ES。测试上线时未发现这个问题&#xff0c;在日常查看日志的过程中&#xff0c;发现logstash推送有错误日志&#xff0c;错误内容为&#xff1a;Error parsing json&am…

【Azure学习.01】先从账号注册开始

本文部分内容配套视频&#xff1a;https://www.bilibili.com/video/av82898957马上要放假了&#xff0c;决定在家里简单了解一下Azure云服务&#xff0c;虽然公司其他部分用到了这个Azure&#xff0c;但是我还是没有接触到&#xff0c;只是听说很贵&#xff0c;好几千每天&…

C++vector容器-插入和删除

vector插入和删除 功能描述&#xff1a; 对vector容器进行插入&#xff0c;删除操作 函数原型&#xff1a; 代码如下&#xff1a; #include <iostream> using namespace std; #include <vector> //vector插入和删除void printVector(vector<int > &v…

C++vector容器-数据存取

vector数据存取 功能描述&#xff1a; 对vector中的数据的存取操作 函数原型&#xff1a; 代码如下&#xff1a; #include <iostream> using namespace std; #include <vector>//vector容器 数据存取 void test01() {vector<int >v1;for (int i 0; i &l…

如何快速融入团队(四)

作者&#xff1a;邹溪源&#xff0c;长沙资深互联网从业者&#xff0c;架构师社区特邀嘉宾&#xff01;01不知不觉这个系列已经开始第四篇的&#xff0c;其实我的原始意图只是思考一下如果有幸加入一个新团队&#xff0c;我们在思想和行动上该做哪些准备呢。不过随着内容的逐渐…

C++vector容器-互换容器

vector容器互换 功能描述&#xff1a; 实现两个容器内元素进行互换 函数原型&#xff1a; swap(vec);//将vec与本身的元素互换 1.基本使用 代码如下: #include <iostream> using namespace std; //vector容器互换 #include <vector> //1.基本使用void printVect…

.NET 状态机Automatonymous快速入门

介绍 Automatonymous是.NET开发人员的状态机库。它提供了一种流畅的语法来声明状态机&#xff0c;包括状态&#xff0c;事件&#xff08;支持触发器和数据事件&#xff09;以及状态/事件活动。尽管Automatonymous在简单的状态机上非常容易使用&#xff0c;但它具有许多高级功能…

BeetleX实现HTTP协议详解

在传统网络服务中扩展中需要处理Bytes来进行协议的读写&#xff0c;这种原始的处理方式让工作变得相当繁琐复杂&#xff0c;出错和调试的工作量都非常大&#xff1b;组件为了解决这一问题引用Stream读写方式&#xff0c;这种方式可以极大的简化网络协议读写的工作量&#xff0c…

euclidea4攻略_Euclidea几何构建11.4通关攻略

Euclidea几何构建10.2通关攻略Euclidea游戏10.2怎么过&#xff1f;下面小编为大家带来Euclidea几何构建10.2通关攻略&#xff1a;更多攻略不断更新中——Euclidea游戏全关卡通关攻略大全分两次做图第一次&#xff0c;画圆就成了&#xff0c;具体看图应该能懂。第二次&#xff0…

C++set容器-构造和赋值

set基本概念 简介&#xff1a; 所有元素都会自动在插入时自动被排序&#xff0c;set容器也叫集合容器 本质&#xff1a; set/multiset属于关联式容器&#xff0c;底层结构是用二叉树排序 set和multiset区别&#xff1a; 1.set不允许容器中有重复的元素 2.multiset允许容器中有…