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调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

Java编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

Python中随机休眠技术原理与应用详解

《Python中随机休眠技术原理与应用详解》在编程中,让程序暂停执行特定时间是常见需求,当需要引入不确定性时,随机休眠就成为关键技巧,下面我们就来看看Python中随机休眠技术的具体实现与应用吧... 目录引言一、实现原理与基础方法1.1 核心函数解析1.2 基础实现模板1.3 整数版实现二、典型应用场景2

Python实现无痛修改第三方库源码的方法详解

《Python实现无痛修改第三方库源码的方法详解》很多时候,我们下载的第三方库是不会有需求不满足的情况,但也有极少的情况,第三方库没有兼顾到需求,本文将介绍几个修改源码的操作,大家可以根据需求进行选择... 目录需求不符合模拟示例 1. 修改源文件2. 继承修改3. 猴子补丁4. 追踪局部变量需求不符合很

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory

JAVA封装多线程实现的方式及原理

《JAVA封装多线程实现的方式及原理》:本文主要介绍Java中封装多线程的原理和常见方式,通过封装可以简化多线程的使用,提高安全性,并增强代码的可维护性和可扩展性,需要的朋友可以参考下... 目录前言一、封装的目标二、常见的封装方式及原理总结前言在 Java 中,封装多线程的原理主要围绕着将多线程相关的操

kotlin中的模块化结构组件及工作原理

《kotlin中的模块化结构组件及工作原理》本文介绍了Kotlin中模块化结构组件,包括ViewModel、LiveData、Room和Navigation的工作原理和基础使用,本文通过实例代码给大家... 目录ViewModel 工作原理LiveData 工作原理Room 工作原理Navigation 工

Java的volatile和sychronized底层实现原理解析

《Java的volatile和sychronized底层实现原理解析》文章详细介绍了Java中的synchronized和volatile关键字的底层实现原理,包括字节码层面、JVM层面的实现细节,以... 目录1. 概览2. Synchronized2.1 字节码层面2.2 JVM层面2.2.1 ente

MySQL的隐式锁(Implicit Lock)原理实现

《MySQL的隐式锁(ImplicitLock)原理实现》MySQL的InnoDB存储引擎中隐式锁是一种自动管理的锁,用于保证事务在行级别操作时的数据一致性和安全性,本文主要介绍了MySQL的隐式锁... 目录1. 背景:什么是隐式锁?2. 隐式锁的工作原理3. 隐式锁的类型4. 隐式锁的实现与源代码分析4