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

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Redis中Hash从使用过程到原理说明

《Redis中Hash从使用过程到原理说明》RedisHash结构用于存储字段-值对,适合对象数据,支持HSET、HGET等命令,采用ziplist或hashtable编码,通过渐进式rehash优化... 目录一、开篇:Hash就像超市的货架二、Hash的基本使用1. 常用命令示例2. Java操作示例三

Redis中Set结构使用过程与原理说明

《Redis中Set结构使用过程与原理说明》本文解析了RedisSet数据结构,涵盖其基本操作(如添加、查找)、集合运算(交并差)、底层实现(intset与hashtable自动切换机制)、典型应用场... 目录开篇:从购物车到Redis Set一、Redis Set的基本操作1.1 编程常用命令1.2 集

Redis中的有序集合zset从使用到原理分析

《Redis中的有序集合zset从使用到原理分析》Redis有序集合(zset)是字符串与分值的有序映射,通过跳跃表和哈希表结合实现高效有序性管理,适用于排行榜、延迟队列等场景,其时间复杂度低,内存占... 目录开篇:排行榜背后的秘密一、zset的基本使用1.1 常用命令1.2 Java客户端示例二、zse

Redis中的AOF原理及分析

《Redis中的AOF原理及分析》Redis的AOF通过记录所有写操作命令实现持久化,支持always/everysec/no三种同步策略,重写机制优化文件体积,与RDB结合可平衡数据安全与恢复效率... 目录开篇:从日记本到AOF一、AOF的基本执行流程1. 命令执行与记录2. AOF重写机制二、AOF的

java程序远程debug原理与配置全过程

《java程序远程debug原理与配置全过程》文章介绍了Java远程调试的JPDA体系,包含JVMTI监控JVM、JDWP传输调试命令、JDI提供调试接口,通过-Xdebug、-Xrunjdwp参数配... 目录背景组成模块间联系IBM对三个模块的详细介绍编程使用总结背景日常工作中,每个程序员都会遇到bu

Python中isinstance()函数原理解释及详细用法示例

《Python中isinstance()函数原理解释及详细用法示例》isinstance()是Python内置的一个非常有用的函数,用于检查一个对象是否属于指定的类型或类型元组中的某一个类型,它是Py... 目录python中isinstance()函数原理解释及详细用法指南一、isinstance()函数

java 恺撒加密/解密实现原理(附带源码)

《java恺撒加密/解密实现原理(附带源码)》本文介绍Java实现恺撒加密与解密,通过固定位移量对字母进行循环替换,保留大小写及非字母字符,由于其实现简单、易于理解,恺撒加密常被用作学习加密算法的入... 目录Java 恺撒加密/解密实现1. 项目背景与介绍2. 相关知识2.1 恺撒加密算法原理2.2 Ja