博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Entity Framework Code First实体对象变动跟踪
阅读量:5768 次
发布时间:2019-06-18

本文共 6921 字,大约阅读时间需要 23 分钟。

  Entity Framework Code First通过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:UnchangedAfter 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 > 10                select p;    foreach (var province in query)    {        Console.WriteLine(province.ProvinceName);    }}

using (var ctx = new PortalContext()){    var query = from p in ctx.Provinces                where p.ProvinceID > 10                select 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
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: UnchangedAfter 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:TestState:Unchanged

 

转载于:https://www.cnblogs.com/libingql/p/3390012.html

你可能感兴趣的文章
GCM 3.0采用类似方式向Android、iOS和Chrome发送消息
查看>>
如何成为一家敏捷银行
查看>>
Oracle在JavaOne上宣布Java EE 8将会延期至2017年底
查看>>
Javascript 深入浅出原型
查看>>
简单之极,搭建属于自己的Data Mining环境(Spark版本)
查看>>
Ruby 2.5.0概览
查看>>
如何通过解决精益问题提高敏捷团队生产力
查看>>
Comment2Wechat —— Typecho 插件
查看>>
Apache下.htaccess文件配置及功能介绍
查看>>
Magento XML cheatsheet
查看>>
Egg 2.19.0 发布,阿里开源的企业级 Node.js 框架
查看>>
Kubernetes 弹性伸缩全场景解析 (四)- 让核心组件充满弹性 ...
查看>>
使用MySQLTuner-perl对MySQL进行优化
查看>>
Swoole 4.1.0 正式版发布,支持原生 Redis/PDO/MySQLi 协程化 ...
查看>>
开发网络视频直播系统需要注意的地方
查看>>
haproxy mysql实例配置
查看>>
强化学习的未来— 第一部分
查看>>
TableStore:用户画像数据的存储和查询利器
查看>>
2019 DockerCon 大会即将召开,快来制定您的专属议程吧!
查看>>
15分钟构建超低成本数据大屏:DataV + DLA
查看>>