EF实体对象变动跟踪

2024-05-30 22:32
文章标签 对象 跟踪 实体 ef 变动

本文主要是介绍EF实体对象变动跟踪,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 Entity Framework 通过DbContext.ChangeTracker对实体对象的变动进行跟踪,实现跟踪的方式有两种:变动跟踪快照和变动跟踪代理。

  变动跟踪快照:前面几篇随笔的示例都是通过实体对象变动快照跟踪来实现数据操作的,POCO模型不包含任何逻辑去通知Entity Framework实体类属性的变动。Entity Framework在第一次对象加载到内存中时进行一次快照,添加快照发生在返回一次查询或添加一个对象到DbSet中时。当Entity Framework需要知道对象的变动时,将先把当前实体与快照中的对象进行扫描对比。实现扫描对比的方法是调用DbContext.ChangeTracker的DetectChanges方法。

  变动跟踪代理:变动跟踪代理是一种会主动通知Entity Framework实体对象发生变动的机制。如:延迟加载的实现方式。要使用变动跟踪代理,需要在定义的类结构中,Entity Framework可以在运行时从POCO类中创建动态类型并重写POCO属性。动态代理就是一种动态类型,包含重写属性和通知Entity Framework实体对象变动的逻辑。

  Entity Framework Code First中能够自动调用DbContext.ChangeTracker.DetectChanges的方法:

  ◊ DbSet.Add

  ◊ DbSet.Find

  ◊ DbSet.Remove

  ◊ DbSet.Attach

  ◊ DbSet.Local

  ◊ DbContext.SaveChanges

  ◊ DbContext.GetValidationErrors

  ◊ DbContext.Entry

  ◊ DbChangeTracker.Entries

  ◊ 任何在DbSet上进行LINQ的查询

  1、控制什么时间调用DetectChanges

  大部分的实例对象的变动调整需要在Entity Framework进行SaveChanges时才会知道,但也可以根据需要调用变动跟踪获取当前对象的状态。

  Entity Framework Code First的DbContext.DetectChanges在检测实例对象的变动时,大部分情况不会有性能的问题。但当有大量的实例对象在内存中,或DbContext有大量的操作时,自动的DetectChanges行为可能会一定程度的影响性能。

Entity Framework提供关闭自动的DetectChanges的功能,在需要的时候进行手动调用。如果考虑内存中的跟踪数量太大问题,就可以手动跟踪。

using (var ctx = new PortalContext())
{ctx.Configuration.AutoDetectChangesEnabled = false;
}

  示例:

复制代码
using (var ctx = new PortalContext())
{ctx.Configuration.AutoDetectChangesEnabled = false;var province = ctx.Provinces.Find(1);province.ProvinceName = "测试";Console.WriteLine("Before DetectChanges:{0}", ctx.Entry(province).State);ctx.ChangeTracker.DetectChanges();Console.WriteLine("After DetectChanges:{0}", ctx.Entry(province).State);
}
复制代码

  代码运行结果:

Before DetectChanges:Unchanged
After DetectChanges:Modified

  2、获取不带变动跟踪的实体查询

  在一些情况下,我们只需要查询返回一个只读的数据记录,而不会对数据记录进行任何的修改。这种时候不希望Entity Framework进行不必要的状态变动跟踪,可以使用Entity Framework的AsNoTracking方法来查询返回不带变动跟踪的查询结果。

复制代码
using (var ctx = new PortalContext())
{foreach (var province in ctx.Provinces.AsNoTracking()){Console.WriteLine(province.ProvinceName);}
}
复制代码

  以上代码中使用AsNoTracking方法查询返回无变动跟踪的Province的DbSet,由于是无变动跟踪,所以对返回的Province集中数据的任何修改,在SaveChanges()时,都不会提交到数据库中。

  AsNoTracking是定义在IQueryable<T>中的扩展方法,所以也可以用于LINQ表达式查询。

复制代码
using (var ctx = new PortalContext())
{var query = from p in ctx.Provinces.AsNoTracking()where p.ProvinceID > 10select p;foreach (var province in query){Console.WriteLine(province.ProvinceName);}
}
复制代码

复制代码
using (var ctx = new PortalContext())
{var query = from p in ctx.Provinceswhere p.ProvinceID > 10select p;query = query.AsNoTracking();foreach (var province in query){Console.WriteLine(province.ProvinceName);}
}
复制代码

  注:使用AsNoTracking需要添加引用命名空间using System.Data.Entity。

  3、单个实体的变动跟踪信息及操作

  使用状态属性:

复制代码
using (var ctx = new PortalContext())
{var province = ctx.Provinces.Find(10);DbEntityEntry<Province> entry = ctx.Entry(province);Console.WriteLine("Before Edit: {0}", entry.State);province.ProvinceName = "Test";ctx.ChangeTracker.DetectChanges();Console.WriteLine("After Edit: {0}", entry.State);
}
复制代码

  注:DbEntityEntry需要引用命名空间using System.Data.Entity.Infrastructure;

  代码运行结果为:

Before Edit: Unchanged
After Edit: Modified

  4、查看对象的当前值、原始值及数据库中的值

  通过DbEntityEntry可以获取对象的当前、原始及在数据库中的值,DbPropertyValues则用于保存对象具体的属性。

复制代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;using Portal.Models;namespace Portal
{class Program{static void Main(string[] args){using (var ctx = new PortalContext()){var province = ctx.Provinces.Find(10);province.ProvinceName = "Test";ctx.Database.ExecuteSqlCommand("UPDATE Province SET ProvinceName = 'Testing' WHERE ProvinceID = 10");PrintChangeTrackingInfo(ctx, province);}}static void PrintChangeTrackingInfo(DbContext ctx, Province province){var entry = ctx.Entry(province);Console.WriteLine(entry.State);Console.WriteLine("\nCurrent Values:");PrintPropertyValues(entry.CurrentValues);Console.WriteLine("\nOriginal Values:");PrintPropertyValues(entry.OriginalValues);Console.WriteLine("\nDatabase Values:");PrintPropertyValues(entry.GetDatabaseValues());}static void PrintPropertyValues(DbPropertyValues values){foreach (var propertyName in values.PropertyNames){Console.WriteLine("- {0}-{1}", propertyName, values[propertyName]);}}}
}
复制代码

  代码运行的结果:

复制代码
ModifiedCurrent Values:
- ProvinceID-10
- ProvinceNo-320000
- ProvinceName-TestOriginal Values:
- ProvinceID-10
- ProvinceNo-320000
- ProvinceName-测试Database Values:
- ProvinceID-10
- ProvinceNo-320000
- ProvinceName-Testing
请按任意键继续. . .
复制代码

  代码运行所执行的SQL语句:

复制代码
exec sp_executesql N'SELECT 
[Limit1].[ProvinceID] AS [ProvinceID], 
[Limit1].[ProvinceNo] AS [ProvinceNo], 
[Limit1].[ProvinceName] AS [ProvinceName]
FROM ( SELECT TOP (2) [Extent1].[ProvinceID] AS [ProvinceID], [Extent1].[ProvinceNo] AS [ProvinceNo], [Extent1].[ProvinceName] AS [ProvinceName]FROM [dbo].[Province] AS [Extent1]WHERE [Extent1].[ProvinceID] = @p0
)  AS [Limit1]',N'@p0 int',@p0=10
复制代码
UPDATE Province SET ProvinceName = 'Testing' WHERE ProvinceID = 10
复制代码
exec sp_executesql N'SELECT 
[Limit1].[ProvinceID] AS [ProvinceID], 
[Limit1].[ProvinceNo] AS [ProvinceNo], 
[Limit1].[ProvinceName] AS [ProvinceName]
FROM ( SELECT TOP (2) [Extent1].[ProvinceID] AS [ProvinceID], [Extent1].[ProvinceNo] AS [ProvinceNo], [Extent1].[ProvinceName] AS [ProvinceName]FROM [dbo].[Province] AS [Extent1]WHERE [Extent1].[ProvinceID] = @p0
)  AS [Limit1]',N'@p0 int',@p0=10
复制代码

  从代码运行所执行的SQL语句可以看出,在最后获取对象在数据库中的值时,Entity Framework再一次到数据库中去查询对象的记录值。

  5、修改DbPropertyValues中的值

  DbPropertyValues中的值不是只读,故可以在第一次加载之后进行修改。

复制代码
using (var ctx = new PortalContext())
{ctx.Configuration.AutoDetectChangesEnabled = false;var province = ctx.Provinces.Find(10);ctx.Entry(province).CurrentValues["ProvinceName"] = "测试";Console.WriteLine("Property Value:{0}", province.ProvinceName);Console.WriteLine("State:{0}", ctx.Entry(province).State);
}
复制代码

  运行结果:

Property Value:测试
State:Modified

  在上面的代码中,尽管已经禁用了自动侦测变动,但在修改了属性值之后,对象的属性仍修改为Modified。实体属性的修改是通过Change Tracker API实现的,实体的状态在不需要调用DetectChanges即修改为Modified。

  若不希望实体的状态发生改变,则实现方式为:

复制代码
using (var ctx = new PortalContext())
{ctx.Configuration.AutoDetectChangesEnabled = false;var province = ctx.Provinces.Find(10);var _province = ctx.Entry(province).CurrentValues.Clone();_province["ProvinceName"] = "测试";Console.WriteLine("Property Value:{0}", province.ProvinceName);Console.WriteLine("State:{0}", ctx.Entry(province).State);
}
复制代码

  运行结果:

Property Value:Test
State:Unchanged
更多参考:

http://www.cnblogs.com/CreateMyself/p/4747605.html

http://www.cnblogs.com/libingql/p/3390012.html

这篇关于EF实体对象变动跟踪的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

在java中如何将inputStream对象转换为File对象(不生成本地文件)

《在java中如何将inputStream对象转换为File对象(不生成本地文件)》:本文主要介绍在java中如何将inputStream对象转换为File对象(不生成本地文件),具有很好的参考价... 目录需求说明问题解决总结需求说明在后端中通过POI生成Excel文件流,将输出流(outputStre

MySQL新增字段后Java实体未更新的潜在问题与解决方案

《MySQL新增字段后Java实体未更新的潜在问题与解决方案》在Java+MySQL的开发中,我们通常使用ORM框架来映射数据库表与Java对象,但有时候,数据库表结构变更(如新增字段)后,开发人员可... 目录引言1. 问题背景:数据库与 Java 实体不同步1.1 常见场景1.2 示例代码2. 不同操作

一文详解SQL Server如何跟踪自动统计信息更新

《一文详解SQLServer如何跟踪自动统计信息更新》SQLServer数据库中,我们都清楚统计信息对于优化器来说非常重要,所以本文就来和大家简单聊一聊SQLServer如何跟踪自动统计信息更新吧... SQL Server数据库中,我们都清楚统计信息对于优化器来说非常重要。一般情况下,我们会开启"自动更新

Pytorch微调BERT实现命名实体识别

《Pytorch微调BERT实现命名实体识别》命名实体识别(NER)是自然语言处理(NLP)中的一项关键任务,它涉及识别和分类文本中的关键实体,BERT是一种强大的语言表示模型,在各种NLP任务中显著... 目录环境准备加载预训练BERT模型准备数据集标记与对齐微调 BERT最后总结环境准备在继续之前,确

C#原型模式之如何通过克隆对象来优化创建过程

《C#原型模式之如何通过克隆对象来优化创建过程》原型模式是一种创建型设计模式,通过克隆现有对象来创建新对象,避免重复的创建成本和复杂的初始化过程,它适用于对象创建过程复杂、需要大量相似对象或避免重复初... 目录什么是原型模式?原型模式的工作原理C#中如何实现原型模式?1. 定义原型接口2. 实现原型接口3

Java实现将byte[]转换为File对象

《Java实现将byte[]转换为File对象》这篇文章将通过一个简单的例子为大家演示Java如何实现byte[]转换为File对象,并将其上传到外部服务器,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言1. 问题背景2. 环境准备3. 实现步骤3.1 从 URL 获取图片字节数据3.2 将字节数组

Javascript访问Promise对象返回值的操作方法

《Javascript访问Promise对象返回值的操作方法》这篇文章介绍了如何在JavaScript中使用Promise对象来处理异步操作,通过使用fetch()方法和Promise对象,我们可以从... 目录在Javascript中,什么是Promise1- then() 链式操作2- 在之后的代码中使

MyBatis的配置对象Configuration作用及说明

《MyBatis的配置对象Configuration作用及说明》MyBatis的Configuration对象是MyBatis的核心配置对象,它包含了MyBatis运行时所需的几乎所有配置信息,这个对... 目录MyBATis配置对象Configuration作用Configuration 对象的主要作用C

SpringBoot实现导出复杂对象到Excel文件

《SpringBoot实现导出复杂对象到Excel文件》这篇文章主要为大家详细介绍了如何使用Hutool和EasyExcel两种方式来实现在SpringBoot项目中导出复杂对象到Excel文件,需要... 在Spring Boot项目中导出复杂对象到Excel文件,可以利用Hutool或EasyExcel

Springboot控制反转与Bean对象的方法

《Springboot控制反转与Bean对象的方法》文章介绍了SpringBoot中的控制反转(IoC)概念,描述了IoC容器如何管理Bean的生命周期和依赖关系,它详细讲解了Bean的注册过程,包括... 目录1 控制反转1.1 什么是控制反转1.2 SpringBoot中的控制反转2 Ioc容器对Bea