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

相关文章

ShardingProxy读写分离之原理、配置与实践过程

《ShardingProxy读写分离之原理、配置与实践过程》ShardingProxy是ApacheShardingSphere的数据库中间件,通过三层架构实现读写分离,解决高并发场景下数据库性能瓶... 目录一、ShardingProxy技术定位与读写分离核心价值1.1 技术定位1.2 读写分离核心价值二

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深入浅出Spring中的@Autowired自动注入的工作原理及实践应用

《深入浅出Spring中的@Autowired自动注入的工作原理及实践应用》在Spring框架的学习旅程中,@Autowired无疑是一个高频出现却又让初学者头疼的注解,它看似简单,却蕴含着Sprin... 目录深入浅出Spring中的@Autowired:自动注入的奥秘什么是依赖注入?@Autowired

从原理到实战解析Java Stream 的并行流性能优化

《从原理到实战解析JavaStream的并行流性能优化》本文给大家介绍JavaStream的并行流性能优化:从原理到实战的全攻略,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的... 目录一、并行流的核心原理与适用场景二、性能优化的核心策略1. 合理设置并行度:打破默认阈值2. 避免装箱

Python中的filter() 函数的工作原理及应用技巧

《Python中的filter()函数的工作原理及应用技巧》Python的filter()函数用于筛选序列元素,返回迭代器,适合函数式编程,相比列表推导式,内存更优,尤其适用于大数据集,结合lamb... 目录前言一、基本概念基本语法二、使用方式1. 使用 lambda 函数2. 使用普通函数3. 使用 N

MyBatis-Plus 与 Spring Boot 集成原理实战示例

《MyBatis-Plus与SpringBoot集成原理实战示例》MyBatis-Plus通过自动配置与核心组件集成SpringBoot实现零配置,提供分页、逻辑删除等插件化功能,增强MyBa... 目录 一、MyBATis-Plus 简介 二、集成方式(Spring Boot)1. 引入依赖 三、核心机制

redis和redission分布式锁原理及区别说明

《redis和redission分布式锁原理及区别说明》文章对比了synchronized、乐观锁、Redis分布式锁及Redission锁的原理与区别,指出在集群环境下synchronized失效,... 目录Redis和redission分布式锁原理及区别1、有的同伴想到了synchronized关键字

Linux中的HTTPS协议原理分析

《Linux中的HTTPS协议原理分析》文章解释了HTTPS的必要性:HTTP明文传输易被篡改和劫持,HTTPS通过非对称加密协商对称密钥、CA证书认证和混合加密机制,有效防范中间人攻击,保障通信安全... 目录一、什么是加密和解密?二、为什么需要加密?三、常见的加密方式3.1 对称加密3.2非对称加密四、

setsid 命令工作原理和使用案例介绍

《setsid命令工作原理和使用案例介绍》setsid命令在Linux中创建独立会话,使进程脱离终端运行,适用于守护进程和后台任务,通过重定向输出和确保权限,可有效管理长时间运行的进程,本文给大家介... 目录setsid 命令介绍和使用案例基本介绍基本语法主要特点命令参数使用案例1. 在后台运行命令2.

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、