EF Core 原理从源码出发(二)
紧接着我的上一篇博客,上回分析到ef 一个重要的对象,changetracker这个对象,当我们向DbContext添加对象的时候我们会调用如下代码。
- 1 private EntityEntry<TEntity> SetEntityState<TEntity>(
- 2 TEntity entity,
- 3 EntityState entityState)
- 4 where TEntity : class
- 5 {
- 6 var entry = EntryWithoutDetectChanges(entity);
- 7
- 8 SetEntityState(entry.GetInfrastructure(), entityState);
- 9
- 10 return entry;
- 11 }
在第6行的时候我们会得到一个entity,如上一篇博客所说,这个entity 会被记录detach状态(因为是new的状态)并记录在detachReferenceMap中,在第七行的代码会将这个实体对象标记为add方法,但是需要考虑的情况有很多,比如是否被修改,是否添加完再修改,主键生成,外键联系,导航属性处理等等,这些都是一些棘手的操作,让我们看一下第七行的代码具体逻辑吧。
- 1 if (entry.EntityState == EntityState.Detached)
- 2 {
- 3 DbContextDependencies.EntityGraphAttacher.AttachGraph(
- 4 entry,
- 5 entityState,
- 6 entityState,
- 7 forceStateWhenUnknownKey: true);
- 8 }
- 9 else
- 10 {
- 11 entry.SetEntityState(
- 12 entityState,
- 13 acceptChanges: true,
- 14 forceStateWhenUnknownKey: entityState);
- 15 }
- 16 }
首先我们先看到如果这个对象原先是detach的状态,会由EntityGraph来进行处理,也就是一个图的数据结构,因为这不仅仅是点对点的数据结构,导航属性的存在会让其变成复杂的多对多的有可能的闭环图,我们在进去看一下这里面的操作。
- 1 public virtual void AttachGraph(
- 2 InternalEntityEntry rootEntry,
- 3 EntityState targetState,
- 4 EntityState storeGeneratedWithKeySetTargetState,
- 5 bool forceStateWhenUnknownKey)
- 6 => _graphIterator.TraverseGraph(
- 7 new EntityEntryGraphNode<(EntityState TargetState, EntityState StoreGenTargetState, bool Force)>(
- 8 rootEntry,
- 9 (targetState, storeGeneratedWithKeySetTargetState, forceStateWhenUnknownKey),
- 10 null,
- 11 null),
- 12 PaintAction);
正如其名所言我们需要遍历这个图,得到所有的导航属性并继续遍历,而遍历的时候的操作我们会给PaintAction 方法去操作,微软正规军写的代码取名非常有意思并且精确,PaintAction告诉我们在遍历的时候做的绘画操作。
那我们先看一下这个PaintAction 所做的操作,
- 1 private static bool PaintAction(
- 2 EntityEntryGraphNode<(EntityState TargetState, EntityState StoreGenTargetState, bool Force)> node)
- 3 {
- 4 SetReferenceLoaded(node);
- 5
- 6 var internalEntityEntry = node.GetInfrastructure();
- 7 if (internalEntityEntry.EntityState != EntityState.Detached)
- 8 {
- 9 return false;
- 10 }
- 11
- 12 var (targetState, storeGenTargetState, force) = node.NodeState;
- 13
- 14 var (isGenerated, isSet) = internalEntityEntry.IsKeySet;
- 15
- 16 internalEntityEntry.SetEntityState(
- 17 isSet
- 18 ? (isGenerated ? storeGenTargetState : targetState)
- 19 : EntityState.Added, // Key can only be not-set if it is store-generated
- 20 acceptChanges: true,
- 21 forceStateWhenUnknownKey: force ? (EntityState?)targetState : null);
- 22
- 23 return true;
- 24 }
非常精确的说明了到一个新的node会设置其中的状态,并且我们看到EF的一些tips,就是当你add一个root entity的时候,导航属性是否设置主键来判断这个导航属性是add 的还是modify的状态。现在我们要去看一下这个SetEntityState的这个方法,现在事情变得有趣起来,因为这个entity会有新旧状态之分,要从detach状态变成add的状态,并且主键的值应该如何设定,我们先看一下状态的变化会导致哪些东西产生变化。
- 1 private void SetEntityState(EntityState oldState, EntityState newState, bool acceptChanges, bool modifyProperties)
- 2 {
- 3 var entityType = EntityType;
- 4
- 5 StateManager.StateChanging(this, newState);
- 6
- 7 if (newState == EntityState.Unchanged
- 8 && oldState == EntityState.Modified)
- 9 {
- 10 if (acceptChanges)
- 11 {
- 12 _originalValues.AcceptChanges(this);
- 13 }
- 14 else
- 15 {
- 16 _originalValues.RejectChanges(this);
- 17 }
- 18 }
- 19
- 20 SetServiceProperties(oldState, newState);
- 21
- 22 _stateData.EntityState = newState;
- 23
- 24 if (oldState == EntityState.Detached)
- 25 {
- 26 StateManager.StartTracking(this);
- 27 }
- 28 else if (newState == EntityState.Detached)
- 29 {
- 30 StateManager.StopTracking(this, oldState);
- 31 }
- 32
- 33 FireStateChanged(oldState);
- 34
- 35 if (newState == EntityState.Unchanged)
- 36 {
- 37 SharedIdentityEntry?.SetEntityState(EntityState.Detached);
- 38 }
- 39
- 40 if ((newState == EntityState.Deleted
- 41 || newState == EntityState.Detached)
- 42 && StateManager.CascadeDeleteTiming == CascadeTiming.Immediate)
- 43 {
- 44 StateManager.CascadeDelete(this, force: false);
- 45 }
- 46 }
截取一些核心代码如上,在第五行的代码中,会将这个entity状态从detach状态变为add状态,也就是将StateManager的五个reference map进行处理,并且触发了这个entity的StateChanging方法,然后在26行代码中如果这个entity的old状态是detach,则需要StateManager去开始追踪他 StateManager.StartTracking(this);,追踪的概念是为这个实体生成唯一的Identity,并为这个实体生成一系列的快照,以后这个实体的所有的变化会与快照进行对比,这个快照会有origin values 和导航属性的快照,这些做完之后这个node就是已经是一个状态成add 的entity并且已经是追踪的状态。
- public virtual void TraverseGraph<TState>(
- EntityEntryGraphNode<TState> node,
- Func<EntityEntryGraphNode<TState>, bool> handleNode)
- {
- if (!handleNode(node))
- {
- return;
- }
- var internalEntityEntry = node.GetInfrastructure();
- var navigations = internalEntityEntry.EntityType.GetNavigations()
- .Concat<INavigationBase>(internalEntityEntry.EntityType.GetSkipNavigations());
- var stateManager = internalEntityEntry.StateManager;
- foreach (var navigation in navigations)
- {
- var navigationValue = internalEntityEntry[navigation];
- if (navigationValue != null)
- {
- var targetEntityType = navigation.TargetEntityType;
- if (navigation.IsCollection)
- {
- foreach (var relatedEntity in ((IEnumerable)navigationValue).Cast<object>().ToList())
- {
- var targetEntry = stateManager.GetOrCreateEntry(relatedEntity, targetEntityType);
- TraverseGraph(
- (EntityEntryGraphNode<TState>)node.CreateNode(node, targetEntry, navigation),
- handleNode);
- }
- }
- else
- {
- var targetEntry = stateManager.GetOrCreateEntry(navigationValue, targetEntityType);
- TraverseGraph(
- (EntityEntryGraphNode<TState>)node.CreateNode(node, targetEntry, navigation),
- handleNode);
- }
- }
- }
- }
接下来就是递归去遍历图的方法,对每一个节点都执行paintaction 的方法,改变状态并跟踪他,有兴趣的小伙伴可以去看一下图的遍历,这个时候所有entity的状态已经更新,现在已经到savechang这个操作了。
- 1 public virtual int SaveChanges(bool acceptAllChangesOnSuccess)
- 2 {
- 3 CheckDisposed();
- 4
- 5 SavingChanges?.Invoke(this, new SavingChangesEventArgs(acceptAllChangesOnSuccess));
- 6
- 7 var interceptionResult = DbContextDependencies.UpdateLogger.SaveChangesStarting(this);
- 8
- 9 TryDetectChanges();
- 10
- 11 try
- 12 {
- 13 var entitiesSaved = interceptionResult.HasResult
- 14 ? interceptionResult.Result
- 15 : DbContextDependencies.StateManager.SaveChanges(acceptAllChangesOnSuccess);
- 16
- 17 var result = DbContextDependencies.UpdateLogger.SaveChangesCompleted(this, entitiesSaved);
- 18
- 19 SavedChanges?.Invoke(this, new SavedChangesEventArgs(acceptAllChangesOnSuccess, result));
- 20
- 21 return result;
- 22 }
- 23 catch (DbUpdateConcurrencyException exception)
- 24 {
- 25 EntityFrameworkEventSource.Log.OptimisticConcurrencyFailure();
- 26
- 27 DbContextDependencies.UpdateLogger.OptimisticConcurrencyException(this, exception);
- 28
- 29 SaveChangesFailed?.Invoke(this, new SaveChangesFailedEventArgs(acceptAllChangesOnSuccess, exception));
- 30
- 31 throw;
- 32 }
- 33 catch (Exception exception)
- 34 {
- 35 DbContextDependencies.UpdateLogger.SaveChangesFailed(this, exception);
- 36
- 37 SaveChangesFailed?.Invoke(this, new SaveChangesFailedEventArgs(acceptAllChangesOnSuccess, exception));
- 38
- 39 throw;
- 40 }
- 41 }
在第9行的代码会显示出追踪的功能,一些实体会因为add完之后继续修改状态,这时我们会根据快照进行相应的修改,这个就是changetracker 这个对象去实现的,然后我们就会根据statemanager的五个referencemap去得到需要保存的对象,然后我们将这些entity与相应的状态给到dayabase组件。
- 1 protected virtual int SaveChanges([NotNull] IList<IUpdateEntry> entriesToSave)
- 2 {
- 3 _concurrencyDetector?.EnterCriticalSection();
- 4
- 5 try
- 6 {
- 7 EntityFrameworkEventSource.Log.SavingChanges();
- 8
- 9 return _database.SaveChanges(entriesToSave);
- 10 }
- 11 finally
- 12 {
- 13 _concurrencyDetector?.ExitCriticalSection();
- 14 }
- 15 }
而这个database不管是什么sql server 还是pgsql 等等,这些都是在之前配置文件配置的扩展组件中,因为本例子中使用memory database进行测试,而memory database 就是用内存的字典对象存储的。有兴趣的小伙伴可以自己去看一下实现的原理。
好了,现在EF core 的代码原理已经结束了,因为这几遍博客完全从代码出发写的比较生硬,希望建议小伙伴自己去clone代码对照这边博客去学习ef 的内部实现方式,图的数据结构确实适合解析导航属性的问题给人一亮的感觉,最后谢谢大家了。如果有任何不解的问题或者指正欢迎留言额。再次谢谢大家的阅读。
EF Core 原理从源码出发(二)的更多相关文章
- spring——AOP原理及源码(二)
回顾: 在上一篇中,我们提到@EnableAspectJAutoProxy注解给容器中加入了一个关键组件internalAutoProxyCreator的BeanDefinition,实际类型为 An ...
- 并发编程(十二)—— Java 线程池 实现原理与源码深度解析 之 submit 方法 (二)
在上一篇<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中提到了线程池ThreadPoolExecutor的原理以及它的execute方法.这篇文章是接着上一篇文章 ...
- 并发编程(十五)——定时器 ScheduledThreadPoolExecutor 实现原理与源码深度解析
在上一篇线程池的文章<并发编程(十一)—— Java 线程池 实现原理与源码深度解析(一)>中从ThreadPoolExecutor源码分析了其运行机制.限于篇幅,留下了Scheduled ...
- 十、Spring之BeanFactory源码分析(二)
Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...
- Vue源码分析(二) : Vue实例挂载
Vue源码分析(二) : Vue实例挂载 author: @TiffanysBear 实例挂载主要是 $mount 方法的实现,在 src/platforms/web/entry-runtime-wi ...
- 手牵手,从零学习Vue源码 系列二(变化侦测篇)
系列文章: 手牵手,从零学习Vue源码 系列一(前言-目录篇) 手牵手,从零学习Vue源码 系列二(变化侦测篇) 陆续更新中... 预计八月中旬更新完毕. 1 概述 Vue最大的特点之一就是数据驱动视 ...
- springmvc工作原理以及源码分析(基于spring3.1.0)
springmvc是一个基于spring的web框架.本篇文章对它的工作原理以及源码进行深入分析. 一.springmvc请求处理流程 二.springmvc的工作机制 三.springmvc核心源码 ...
- OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波
http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...
- CopyOnWriteArrayList实现原理及源码分析
CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可 ...
随机推荐
- ZOJ 2563 Long Dominoes(状压DP)题解
题意:n*m的格子,用1 * 3的矩形正好填满它,矩形不能重叠,问有几种填法 思路:poj2411进阶版.我们可以知道,当连续两行的摆法确定,那么接下来的一行也确定.当第一行还有空时,这时第三行必须要 ...
- Java开发工程师最新面试题库系列——Java基础部分
JAVA基础 面向对象有哪些特征? 答:继承.封装.多态 JDK与JRE的区别是什么? 答:JDK是java开发时所需环境,它包含了Java开发时需要用到的API,JRE是Java的运行时环境,JDK ...
- 痞子衡嵌入式:超级下载算法(RT-UFL)开发笔记(4) - 轮询Flash配置参数
大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是超级下载算法开发笔记(4)之轮询Flash配置参数. 文接上篇 <超级下载算法(RT-UFL)开发笔记(3) - 统一FlexSP ...
- matplotlib 图标显示中文
matplotlib 显示中文 Method_1: # 添上: plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签 plt.rcPara ...
- Learn-JavaScript-with-MDN 系列文章: 01. var & let & const 对比
Learn-JavaScript-with-MDN 系列文章: 01. var & let & const 对比 var & let & const 区别 https: ...
- ES2020 All in One
ES2020 All in One ES2020 new features / ES11 ES2020 中的10个新功能 1. BigInt BigInt是JavaScript中最令人期待的功能之一, ...
- 09_MySQL数据库的索引机制
CREATE TABLE t_message( id INT UNSIGNED PRIMARY KEY, content VARCHAR(200) NOT NULL, type ENUM(" ...
- 最佳搭档:利用 SSH 及其配置文件节省你的生命
本文转载自最佳搭档:利用 SSH 及其配置文件节省你的生命 导语 SSH 协议是事实上的互联网基石之一.在 SSH 协议出现之前(1995 年由 Tatu Ylonen 设计),通过互联网远程登录其他 ...
- smart-adminx项目导入依赖时,点击reinport时没反应且依赖全部报红的解决办法
依赖报红的解决办法 报红效果如下: 原因分析:下载jar包时,出现大量以.lastUpdated结尾的无效文件. 解决办法:使用bat批处理文件批量删除无效文件 set REPOSITORY_PATH ...
- Bitter.NotifyOpenPaltform : HTTP 异步消息接收调度中心--开源贡献 之 一:简介
现在互联网的系统越来越趋向于复杂,从单体系统到现在的微服务体系演变.公司与公司的分工也越来越明确. 大数据公司提供了大数据服务 人脸识别公司提供了人脸识别服务 OCR 公司提供了专业的OCR 服务 车 ...