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

相关文章

sqlserver、mysql、oracle、pgsql、sqlite五大关系数据库的对象名称和转义字符

《sqlserver、mysql、oracle、pgsql、sqlite五大关系数据库的对象名称和转义字符》:本文主要介绍sqlserver、mysql、oracle、pgsql、sqlite五大... 目录一、转义符1.1 oracle1.2 sqlserver1.3 PostgreSQL1.4 SQLi

JavaScript对象转数组的三种方法实现

《JavaScript对象转数组的三种方法实现》本文介绍了在JavaScript中将对象转换为数组的三种实用方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友... 目录方法1:使用Object.keys()和Array.map()方法2:使用Object.entr

使用MapStruct实现Java对象映射的示例代码

《使用MapStruct实现Java对象映射的示例代码》本文主要介绍了使用MapStruct实现Java对象映射的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、什么是 MapStruct?二、实战演练:三步集成 MapStruct第一步:添加 Mave

Java中实现对象的拷贝案例讲解

《Java中实现对象的拷贝案例讲解》Java对象拷贝分为浅拷贝(复制值及引用地址)和深拷贝(递归复制所有引用对象),常用方法包括Object.clone()、序列化及JSON转换,需处理循环引用问题,... 目录对象的拷贝简介浅拷贝和深拷贝浅拷贝深拷贝深拷贝和循环引用总结对象的拷贝简介对象的拷贝,把一个

使用Java读取本地文件并转换为MultipartFile对象的方法

《使用Java读取本地文件并转换为MultipartFile对象的方法》在许多JavaWeb应用中,我们经常会遇到将本地文件上传至服务器或其他系统的需求,在这种场景下,MultipartFile对象非... 目录1. 基本需求2. 自定义 MultipartFile 类3. 实现代码4. 代码解析5. 自定

javaSE类和对象进阶用法举例详解

《javaSE类和对象进阶用法举例详解》JavaSE的面向对象编程是软件开发中的基石,它通过类和对象的概念,实现了代码的模块化、可复用性和灵活性,:本文主要介绍javaSE类和对象进阶用法的相关资... 目录前言一、封装1.访问限定符2.包2.1包的概念2.2导入包2.3自定义包2.4常见的包二、stati

SQL Server跟踪自动统计信息更新实战指南

《SQLServer跟踪自动统计信息更新实战指南》本文详解SQLServer自动统计信息更新的跟踪方法,推荐使用扩展事件实时捕获更新操作及详细信息,同时结合系统视图快速检查统计信息状态,重点强调修... 目录SQL Server 如何跟踪自动统计信息更新:深入解析与实战指南 核心跟踪方法1️⃣ 利用系统目录

MyBatis-Plus 自动赋值实体字段最佳实践指南

《MyBatis-Plus自动赋值实体字段最佳实践指南》MyBatis-Plus通过@TableField注解与填充策略,实现时间戳、用户信息、逻辑删除等字段的自动填充,减少手动赋值,提升开发效率与... 目录1. MyBATis-Plus 自动赋值概述1.1 适用场景1.2 自动填充的原理1.3 填充策略

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