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

相关文章

Python实现对阿里云OSS对象存储的操作详解

《Python实现对阿里云OSS对象存储的操作详解》这篇文章主要为大家详细介绍了Python实现对阿里云OSS对象存储的操作相关知识,包括连接,上传,下载,列举等功能,感兴趣的小伙伴可以了解下... 目录一、直接使用代码二、详细使用1. 环境准备2. 初始化配置3. bucket配置创建4. 文件上传到os

SpringMVC高效获取JavaBean对象指南

《SpringMVC高效获取JavaBean对象指南》SpringMVC通过数据绑定自动将请求参数映射到JavaBean,支持表单、URL及JSON数据,需用@ModelAttribute、@Requ... 目录Spring MVC 获取 JavaBean 对象指南核心机制:数据绑定实现步骤1. 定义 Ja

Python打印对象所有属性和值的方法小结

《Python打印对象所有属性和值的方法小结》在Python开发过程中,调试代码时经常需要查看对象的当前状态,也就是对象的所有属性和对应的值,然而,Python并没有像PHP的print_r那样直接提... 目录python中打印对象所有属性和值的方法实现步骤1. 使用vars()和pprint()2. 使

MySQL JSON 查询中的对象与数组技巧及查询示例

《MySQLJSON查询中的对象与数组技巧及查询示例》MySQL中JSON对象和JSON数组查询的详细介绍及带有WHERE条件的查询示例,本文给大家介绍的非常详细,mysqljson查询示例相关知... 目录jsON 对象查询1. JSON_CONTAINS2. JSON_EXTRACT3. JSON_TA

C#之List集合去重复对象的实现方法

《C#之List集合去重复对象的实现方法》:本文主要介绍C#之List集合去重复对象的实现方法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C# List集合去重复对象方法1、测试数据2、测试数据3、知识点补充总结C# List集合去重复对象方法1、测试数据

Spring中管理bean对象的方式(专业级说明)

《Spring中管理bean对象的方式(专业级说明)》在Spring框架中,Bean的管理是核心功能,主要通过IoC(控制反转)容器实现,下面给大家介绍Spring中管理bean对象的方式,感兴趣的朋... 目录1.Bean的声明与注册1.1 基于XML配置1.2 基于注解(主流方式)1.3 基于Java

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

golang 对象池sync.Pool的实现

《golang对象池sync.Pool的实现》:本文主要介绍golang对象池sync.Pool的实现,用于缓存和复用临时对象,以减少内存分配和垃圾回收的压力,下面就来介绍一下,感兴趣的可以了解... 目录sync.Pool的用法原理sync.Pool 的使用示例sync.Pool 的使用场景注意sync.

SpringBoot项目中Redis存储Session对象序列化处理

《SpringBoot项目中Redis存储Session对象序列化处理》在SpringBoot项目中使用Redis存储Session时,对象的序列化和反序列化是关键步骤,下面我们就来讲讲如何在Spri... 目录一、为什么需要序列化处理二、Spring Boot 集成 Redis 存储 Session2.1