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

相关文章

C++对象布局及多态实现探索之内存布局(整理的很多链接)

本文通过观察对象的内存布局,跟踪函数调用的汇编代码。分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等 文章链接:http://dev.yesky.com/254/2191254.shtml      论C/C++函数间动态内存的传递 (2005-07-30)   当你涉及到C/C++的核心编程的时候,你会无止境地与内存管理打交道。 文章链接:http://dev.yesky

API-环境对象

学习目标: 掌握环境对象 学习内容: 环境对象作用 环境对象: 指的是函数内部特殊的变量this,它代表着当前函数运行时所处的环境。 作用: 弄清楚this的指向,可以让我们代码更简洁。 函数的调用方式不同,this指代的对象也不同。【谁调用,this就是谁】是判断this指向的粗略规则。直接调用函数,其实相当于是window.函数,所以this指代window。

Python分解多重列表对象,isinstance实现

“”“待打印的字符串列表:['ft','bt',['ad',['bm','dz','rc'],'mzd']]分析可知,该列表内既有字符对象,又有列表对象(Python允许列表对象不一致)现将所有字符依次打印并组成新的列表”“”a=['ft','bt',['ad',['bm','dz','rc'],'mzd']]x=[]def func(y):for i in y:if isinst

基于动力学的六自由度机器人阻抗恒力跟踪控制

1.整个代码的控制流程图如下: 2.正逆运动学计算 略 3.动力学模型 采用拉格朗日法计算机械臂的动力学模型,其输入的是机械臂的关节角度、角速度和角加速度;其中M、C、G本别是计算的惯性力、科式力和重力项,相关部分如下: 4.RBF神经网络自适应参数调节 采用RBF自适应调节阻抗控制器参数,末端每个方向单独进行参数的调整,其中rbf的输入的是力和位置,输出的是阻抗控制器的参数,rb

Java面试题:内存管理、类加载机制、对象生命周期及性能优化

1. 说一下 JVM 的主要组成部分及其作用? JVM包含两个子系统和两个组件:Class loader(类装载)、Execution engine(执行引擎)、Runtime data area(运行时数据区)、Native Interface(本地接口)。 Class loader(类装载):根据给定的全限定名类名(如:java.lang.Object)装载class文件到Runtim

Class 对象在执行引擎中的初始化过程

一个 class 文件被加载到内存中需要经过 3 大步:装载、链接、初始化。 装载 装载是指 Java 虚拟机查找 .class 文件并生成字节流,然后根据字节流创建 java.lang.Class 对象的过程。 链接 链接过程分为 3 步:验证、准备、解析。 验证: 初始化 这是 class 加载的最后一步,这一阶段是执行类构造器方法的过程,并真正初始化类变量。 1.文件格式检验:检

添加一个对象到集合中时,集合里面存放的是对象的引用还是对象本身?

问题:添加一个对象到集合中时,集合里面存放的是对象的引用还是对象本身?   答:对象的引用。以下代码可以证明: Java代码   import java.util.ArrayList;   import java.util.List;      public class Test5 {       public static void main(String args[])

java中匿名对象分析

转自:xiazdong http://blog.csdn.net/xiazdong/article/details/6723101 一、两种实例化方式: String str = “abc”; String str = new String("abc"); 一个字符串就是String的匿名对象。 "hello".equals(str)    一

双层嵌套json字符串(即json对象内嵌json数组)解析为Map

无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点这里可以跳转到教程。 之前我层写过一篇文章,介绍了json与map的相互转化,但当时只涉及到单一的json对象或json数组,对json对象内嵌套这json数组的json字符串无法处理,这篇文章主要解决这个问题。 之前的那篇文章址:http://blo

基于感知哈希算法的视觉目标跟踪

偶然看到这三篇博文[1][2][3],提到图片检索网站TinEye和谷歌的相似图片搜索引擎的技术原理。以图搜图搜索引擎的使命是:你上传一张图片,然后他们尽全力帮你把互联网上所有与它相似的图片搜索出来。当然了,这只是他们认为的相似,所以有时候搜索结果也不一定对。事实上,以图搜图三大搜索引擎除了上面的老牌的TinEye和Google外,还有百度上线不算很久的新生儿:百度识图。之前听余凯老师的一个D