TimeStamp

更新操作可能伴随数据冲突,我们可以通过并发处理妥善解决这一方面的问题。避免数据冲突比较方便的做法是自动加入字节数组(byte[])类型的TimeStamp属性,对应到数据表中的rowvewsion类型字段,自动监控数据的更新操作。

下面通过一个例子来说明:

新建一个项目,名称为TimeStampDemo,新增实体类如下图所示:

    public class Product{public int Id { get; set; }public string Name { get; set; }public int Price { get; set; }public int Quantity { get; set; }[Timestamp]public byte[] Timestamp { get; set; }}

上下文代码如下图所示:

    public class TimeStampModel : DbContext{public TimeStampModel(): base("name=TimeStampModel"){}public virtual DbSet<Product> Product { get; set; }}

首次运行项目,在数据库中生成了如下表结构:

下面编写程序来查看其中的Timestamp字段值:

static void Main(string[] args)
{using (TimeStampModel model = new TimeStampModel()){Product product = model.Product.Where(p => p.Id == 1).First();long ts = BitConverter.ToInt64(product.Timestamp,0);Console.WriteLine("Quantity:{0} \t Timestamp:{1}",product.Quantity,ts);product.Quantity = 150;model.SaveChanges();ts = BitConverter.ToInt64(product.Timestamp,0);Console.WriteLine("Quantity:{0} \t Timestamp:{1}",product.Quantity,ts);Console.ReadKey();}
}

运行效果下图所示:

可以看到,每一次更新数据时,EF都会修改Timestamp的值。如果期间有其他程序抢先完成更新造成数据变更,那么EF就会报错。

我们重新执行程序,在首次出现Timestamp语句时,打上断点。然后在SQL查询界面直接更新用SQL语句更新数据:

UPDATE dbo.Products SET Quantity = 200 WHERE Id = 1

SQL执行之前Timestamp的值:

SQL执行之后Timestamp的值:

可以看到每次更新数据后,Timestamp的数值会发生改变, 这时继续运行程序,就会报错:

此信息描述找不到要更新的数据,这是因为EF在更新每一项数据时,除了主键之外,还会对比当时取出的TimeStamp的值。由于我们使用外部SQL更新,EF无法检测这个字段就出现报错。

异常DbUpdateConcurrencyException派生自DbUpdateException,表示一个并发更新冲突,当这个异常出现时,显示SaveChanges更新失败,通过DbUpdateException.Entries属性可以获取其返回的IEnumerable<DbEntityEntry>对象,其中存取未成功更新的实体数据对象。

重新调整Main方法中的代码如下图所示:

static void Main(string[] args)
{using (TimeStampModel model = new TimeStampModel()){Product product = model.Product.Where(p => p.Id == 1).First();long ts = BitConverter.ToInt64(product.Timestamp, 0);Console.WriteLine("Quantity:{0} \t TimeStamp:{1}", product.Quantity, ts);try{Console.WriteLine("新的 Quantity 值:");int quantity = 500;product.Quantity = quantity;model.SaveChanges();ts = BitConverter.ToInt64(product.Timestamp, 0);Console.WriteLine("Quantity:{0} \t TimeStamp:{1}", product.Quantity, ts);}catch (DbUpdateConcurrencyException ex){DbEntityEntry entry = ex.Entries.Single();DbPropertyValues current = entry.CurrentValues;//当前实体数据int quantity = current.GetValue<int>("Quantity");long timestamp = BitConverter.ToInt64(current.GetValue<byte[]>("Timestamp"),0);DbPropertyValues dbvalue = entry.GetDatabaseValues();//数据库中的实体数据int dbquantity = dbvalue.GetValue<int>("Quantity");long dbtimestamp = BitConverter.ToInt64(dbvalue.GetValue<byte[]>("Timestamp"),0);Console.WriteLine("DbUpdateConcurrentException.....");Console.WriteLine("当前实体的属性值: Quantity:{0} \t TimeStamp:{1}",quantity,timestamp);Console.WriteLine("数据库中的字段值:Quantity:{0} \t TimeStamp:{1}",dbquantity,dbtimestamp);}}
}

再次在首次出现Timestamp语句时,打上断点。然后在SQL查询界面直接更新用SQL语句更新数据后,继续运行程序,如下图所示:


并发冲突处理——Database Wins 或者 Client Wins

处理更新冲突有几种方式,想以数据库中的值来覆盖当前数据对象的值,则可以调用DbEntityEntry类定义的Reload方法,此方法执行的是Database Wins(数据库优先)策略,执行完毕之后,数据对象当前的值将与数据库同步。

entry.Relaod();
entry.SaveChanges();

另一种方式则是反向以当前的值覆盖数据库的值,称为Clinet  Wins(客户优先)策略。

entry.OriginalValues.SetValues(entry.GetDatabaseValues());
model.SaveChanges()

添加一个ResolveConcurrency方法来支持并发冲突处理:

public static void ResolveConcurrency(TimeStampModel model, DbEntityEntry entry)
{Console.WriteLine("1.Database wins  2.Client wins:");int i = int.Parse(Console.ReadLine());if (i == 1){entry.Reload();model.SaveChanges();Console.WriteLine("与数据库同步完成——DataBase Wins");}else{entry.OriginalValues.SetValues(entry.GetDatabaseValues());model.SaveChanges();Console.WriteLine("重新更新数据库完成——Client Wins");}
}

ResolveConcurrency方法插入上述例子的catch语句中,并在并发冲突中选择与数据库同步完成,结果如下图所示:


ConcurrencyCheck注解

TimeStamp注解属性会导致EF在更新时监控整项数据的更新状态,如果对特定字段进行数据冲突的监控,则可以通过ConcurrencyCheck注解来达到目的。

下面通过程序来说明,新建一个控制台应用程序ConcurrencyCheckDemo,有如下一个实体类:

    public class Product{public int Id { get; set; }public string Name { get; set; }public int Price { get; set; }[ConcurrencyCheck]public int SPrice { get; set; }}

在特价字段(SPrice)设置【ConcurrencyCheck】,当程序更新任何一项Product数据时,SPrice字段将受到监控,期间不受EF监控的程序变更了SPrice字段的值时,便会产生冲突。其他字段没有设置,因此数据的更新不会有冲突。

在Main函数中增加如下图代码:

static void Main(string[] args)
{using (ConcurrencyCheckModel db = new ConcurrencyCheckModel()){try{Console.WriteLine("指定更新操作:A.商品名称 B.商品特价");string ab = Console.ReadLine();if (ab == "A"){Console.WriteLine("输入第一项商品的新名称:");string name = Console.ReadLine();db.Product.First().Name = name;}if (ab == "B"){Console.WriteLine("输入第一项商品的新特价:");int price = int.Parse(Console.ReadLine());db.Product.First().SPrice = price;}Console.WriteLine("按任意键完成更热");Console.ReadKey();db.SaveChanges();Console.WriteLine("完成更新");}catch (Exception ex){Console.WriteLine(ex.ToString());Console.WriteLine(ex.Message);}}
}

当修改商品特价时,去数据库修改SPrice字段的值,便会报错:


EntityFramework进阶——数据变更冲突相关推荐

  1. EF Core 数据变更自动审计设计

    EF Core 数据变更自动审计设计 Intro 有的时候我们需要知道每个数据表的变更记录以便做一些数据审计,数据恢复以及数据同步等之类的事情, EF 自带了对象追踪,使得我们可以很方便的做一些审计工 ...

  2. 使用SQLServer 2008的CDC功能实现数据变更捕获

    原文: 使用SQLServer 2008的CDC功能实现数据变更捕获 最近由于工作需要,研究了一下2008 CDC功能,觉得还不错,下面整理了一下研究过程,虽然比较粗略,但是基本上能用了,如果有补充请 ...

  3. EntityFramework进阶——数据编辑与维护

    实体数据对象状态 在EF环境下,应用程序更改数据对象会引发数据集状态的变更,可能的状态有以下几种: 数据对象状态列表 Added 添加实体对象创建到实体集中,数据未添加进数据库 Modified 实体 ...

  4. RestAPI的进化之路,后端MVVM模式或许来临,通过观察者模式,后端收集前端的GET类请求,主动推送数据变更到前端

    RestAPI的进化之路,后端MVVM模式或许来临,通过观察者模式,后端收集前端的GET类请求,主动推送数据变更到前端 最近几年,前端MVVM模式彻底变革了前端的开发模式,那这股火焰会烧到后端嘛? 我 ...

  5. canal 记录 数据变更类型 QUERY ROWDATA INSERT xxx XXXXX

    canal 记录 数据变更类型 QUERY ROWDATA INSERT xxx XXXXX 10:40:11.355 [Thread-0] INFO  org.canal.CanalClient - ...

  6. 勇斗勒索软件的英雄疑因旧罪被捕;华为、腾讯为用户数据起冲突

    (点击上方蓝字,快速关注我们) 参考:开源中国.腾讯科技.IT168.solidot.cnBeta.IT之家等 0.勇斗勒索软件英雄被捕:3 年前曾制作分发木马 今年 5 月份,22 岁的英国安全研究 ...

  7. DB数据变更缓存分布式更新的zk分布式锁解决方案

    DB数据变更缓存分布式更新的zk分布式锁解决方案 KafkaConsumer kafak消费线程,DB数据变更后,将消息推送到kafka topic,由消费线程进行消费 属性  1.ConsumerC ...

  8. ogg同步的是语句还是数据变更

    以前跟人解释ogg同步,都是说:源端来个insert,目标端也是执行了相同的insert,所以源端和目标端的数据才是一致的. 兴许是加班导致脑袋不清醒,在因对某表大批量操作而考虑拆分进程的时候,忽然对 ...

  9. CloudQuery v2.0.0 发布 新增数据保护、数据变更、连接管理等功能

    哈喽社区的小伙伴们,经过一个月的努力,CloudQuery 社区版发布了全新 v2.0.0系列! 对比 v1.5.0,v2.0.0 在整体 UI 界面上就做了很大调整,功能排布我们做了重新梳理,可以说 ...

最新文章

  1. 词向量, BERT, ALBERT, XLNet全面解析(ALBERT第一作者亲自讲解)
  2. 一步步在Kubernetes里运行Web应用
  3. 转:GridView 模板列中的数据绑定
  4. 接口文档要写在概要设计里吗_写代码的五个步骤,你会几个?
  5. php支付回调怎么写_php 微信支付回调校验的两种做法
  6. 想悄悄的做渗透测试?这里的工具足够你用了
  7. 如何在延迟后触发一个块,比如-performSelector:withObject:afterDelay:?
  8. bzoj 1620: [Usaco2008 Nov]Time Management 时间管理(贪心)
  9. springboot+vue全栈开发_springboot+vue(一)___开发环境以及前后端项目搭建
  10. FFmpeg4.0.2 over版本av_register_all()流程(二十九)
  11. android手机之-------64位操作系统 与 64位处理器
  12. FreeWheel创始人/CTO于晶纯访谈:具备大局观方能洞若观火
  13. 解决win10任务栏卡死无响应点不动
  14. 这五款牛逼的 IDEA 插件,堪称代码质量检查利器!
  15. 反汇编入门试手 简单程序
  16. imgcook设计稿生成代码插件
  17. java cloud五大神兽_SpringCloud五大神兽之Eureka
  18. C4D模型工具—对齐法线
  19. 嵌入式常用的算法 - 斜波函数
  20. Python - Excel转json

热门文章

  1. 微信公众号开发 ssl connect error
  2. idea中Error:java: Compilation failed: internal java compiler error
  3. Android 获取CellId以及IMEI 获取基站id
  4. 修改yum的镜像服务器为阿里云
  5. Hive的数据模型-分区表
  6. 解决win10安卓虚拟机每十几分钟蓝屏重启问题
  7. php ci框架 实例化类,php框架CI(codeigniter)自动加载与自主创建对象操作实例分析...
  8. [转载] java中数组的反射的探究
  9. [转载] JAVA泛型杂谈--擦除,协变,逆变,通配符等
  10. [转载] Java 重写paint绘图