EF Core 原理从源码出发(二)

2024-04-10 12:32
文章标签 源码 原理 出发 core ef

本文主要是介绍EF Core 原理从源码出发(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

紧接着我的上一篇博客,可以点击这里回到上一篇博客,上回分析到ef 两个重要的对象,StateManager和ChangeTracker这个对象,当我们向DbContext添加对象的时候我们会调用如下代码。

复制代码
1 private EntityEntry SetEntityState(
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(
EntityEntryGraphNode node,
Func<EntityEntryGraphNode, 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 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 }
USB Microphone https://www.soft-voice.com/
Wooden Speakers https://www.zeshuiplatform.com/
亚马逊测评 www.yisuping.cn
深圳网站建设www.sz886.com

这篇关于EF Core 原理从源码出发(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/891067

相关文章

Python中使用uv创建环境及原理举例详解

《Python中使用uv创建环境及原理举例详解》uv是Astral团队开发的高性能Python工具,整合包管理、虚拟环境、Python版本控制等功能,:本文主要介绍Python中使用uv创建环境及... 目录一、uv工具简介核心特点:二、安装uv1. 通过pip安装2. 通过脚本安装验证安装:配置镜像源(可

Mysql的主从同步/复制的原理分析

《Mysql的主从同步/复制的原理分析》:本文主要介绍Mysql的主从同步/复制的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录为什么要主从同步?mysql主从同步架构有哪些?Mysql主从复制的原理/整体流程级联复制架构为什么好?Mysql主从复制注意

Nacos注册中心和配置中心的底层原理全面解读

《Nacos注册中心和配置中心的底层原理全面解读》:本文主要介绍Nacos注册中心和配置中心的底层原理的全面解读,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录临时实例和永久实例为什么 Nacos 要将服务实例分为临时实例和永久实例?1.x 版本和2.x版本的区别

apache的commons-pool2原理与使用实践记录

《apache的commons-pool2原理与使用实践记录》ApacheCommonsPool2是一个高效的对象池化框架,通过复用昂贵资源(如数据库连接、线程、网络连接)优化系统性能,这篇文章主... 目录一、核心原理与组件二、使用步骤详解(以数据库连接池为例)三、高级配置与优化四、典型应用场景五、注意事

8种快速易用的Python Matplotlib数据可视化方法汇总(附源码)

《8种快速易用的PythonMatplotlib数据可视化方法汇总(附源码)》你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python的Matplotlib库是你数据可视化的... 目录引言1. 折线图(Line Plot)——趋势分析2. 柱状图(Bar Chart)——对比分析3

电脑系统Hosts文件原理和应用分享

《电脑系统Hosts文件原理和应用分享》Hosts是一个没有扩展名的系统文件,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从Hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应... Hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应

无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案

《无法启动此程序因为计算机丢失api-ms-win-core-path-l1-1-0.dll修复方案》:本文主要介绍了无法启动此程序,详细内容请阅读本文,希望能对你有所帮助... 在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是"api-ms-win-core-path-l1-1-0.dll丢失

Dubbo之SPI机制的实现原理和优势分析

《Dubbo之SPI机制的实现原理和优势分析》:本文主要介绍Dubbo之SPI机制的实现原理和优势,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Dubbo中SPI机制的实现原理和优势JDK 中的 SPI 机制解析Dubbo 中的 SPI 机制解析总结Dubbo中

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

Spring框架中@Lazy延迟加载原理和使用详解

《Spring框架中@Lazy延迟加载原理和使用详解》:本文主要介绍Spring框架中@Lazy延迟加载原理和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、@Lazy延迟加载原理1.延迟加载原理1.1 @Lazy三种配置方法1.2 @Component