欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

asp.net core 系列之并发冲突的深入理解

程序员文章站 2023-12-06 12:10:52
本文介绍如何处理多个用户并发更新同一实 体(同时)时出现的冲突 。 主要是两种:一种,检查属性并发冲突,使用 [concurrencycheck] ;另一种,检测行...

本文介绍如何处理多个用户并发更新同一实 体(同时)时出现的冲突 。

主要是两种:一种,检查属性并发冲突,使用 [concurrencycheck] ;另一种,检测行的并发冲突,使用 rowversion 跟踪属性,如果在保存之前有修改,就报错

发生并发冲突的情况:

1.用户导航到实体编辑页面;

2.第一个用户的更改还未写入数据库之前,另一个用户更新同一实体;

此时,如果未启用并发检测,当发生更新时:

最后一个更新优先。即最后一个更新的值保存到数据库。而第一个保存的值将丢失。

举个例子:

1. jane 访问 院系 编辑页 面,将英 语 系的 预 算 从 350,000.00 美元更改 为 0.00 美元 (第一个用户把金额改为0)

asp.net core 系列之并发冲突的深入理解

2. 在 jane 单击 “ 保存 ” 之前, john 访问 了相同 页 面,并将开始日期字段从 2007/1/9 更改 为 2013/1/9。 (在第一个用户保存之前,第二个用户把时间从07年改为13年,注意此时第二个用户看到的金额还不是0)

asp.net core 系列之并发冲突的深入理解

3. jane 先 单击 “ 保存 ” ,并在 浏览 器 显 示索引 页时 看到她的更改。 (第一个用户先保存,并且可以在浏览器看到他的修改,金额变0,时间不变)

asp.net core 系列之并发冲突的深入理解

4.john 单击 “ 编辑 ” 页 面上的 “ 保存 ” ,但 页 面的 预 算仍 显 示 为 350,000.00 美元。 (第二个用户保存,此时的页面的预算显示未350000美元,时间为13年)

其实这个结果取决于 并发冲突的处理方式

首先声明,这是一个乐观并发冲突,那么什么是乐观并发冲突呢?

乐观并发冲突允许发生并发冲突,并在并发冲突发生时作出正确的反映。

说了这么多,那么,并发冲突的处理方式呢?

1. 可以跟踪用户已修改的属性,并只更新数据库中相应的列。

这样,当两个用户更新了不同的属性,下次查看时,都将生效。

但是,这种方法,也有一些问题:

  • 当对同一个属性进行竞争性更改的话,无法避免数据丢失
  • 通常不适用于web应用。它需要维持重要状态,以便跟踪所有提取值和新值。 维持大量状态可能影响应用性能。
  • 可能会增加应用复杂性(与实体上的并发检测相比)。

体现在例子中,就是如果 下次有人 浏览 英 语 系 时 ,将看到 jane 和 john 两个人的更改。

2.客户端优先

即客户端的值优先于数据库存储的值。并且如果不对并发处理进行任何编码,将自动进行客户端优先

即john 的更改覆盖 jane 的更改 。也就是说,下次有人 浏览 英 语 系 时 ,将看到 2013/9/1 和提取的 值 350,000.00 美元

3.存储优先

这种方式可以阻止在数据库中john的更改。并且可以

  • 显示错误消息
  • 显示数据的当前状态
  • 允许用户重新应用更改。

处理并发

当属性配置 为 并 发 令牌 时 : 

数据库和数据模型必须配置为支持引发dbupdateconcurrencyexception 。

检测属性的并发冲突

可使用 concurrencycheck 特性在属性级别检测并发冲突。 该特性可应用于模型上的多个属性 。[concurrencycheck] 特性

检测行的并发冲突

要检测并发冲突,请将 rowversion 跟踪列添加到模型。

注意:rowversion , 

1.它是 sql server 特定的。 其他数据库可能无法提供类似功能。

2.用于确定从数据库提取实体后未更改实体。

数据库生成rowversion序号,该数字随着每次行的更新递增。

在 update 或 delete 命令中,where 子句中包括 rowversion提取值 的判断 。

如果要更新的行已经修改,则 rowversion提取值与现在数据库中rowversion的值不匹配;

update 或 delete 命令不能找到行。引发一个 dbupdateconcurrencyexception 异常

例子

向 department 实 体添加跟踪属性

using system;
using system.collections.generic;
using system.componentmodel.dataannotations;
using system.componentmodel.dataannotations.schema;
namespace contosouniversity.models
{
public class department
{
public int departmentid { get; set; }
[stringlength(50, minimumlength = 3)]
public string name { get; set; }
[datatype(datatype.currency)]
[column(typename = "money")]
public decimal budget { get; set; }
[datatype(datatype.date)]
[displayformat(dataformatstring = "{0:yyyy-mm-dd}", applyformatineditmode = true)]
[display(name = "start date")]
public datetime startdate { get; set; }
public int? instructorid { get; set; }

[timestamp]
public byte[] rowversion { get; set; } //跟踪属性

public instructor administrator { get; set; }
public icollection<course> courses { get; set; }
}
}

timestamp 特性 指定此列包含在 update 和 delete 命令的 where 子句中。

也可以用 fluent api 指定跟踪属性:

modelbuilder.entity<department>()
.property<byte[]>("rowversion")
.isrowversion();

以下代 码显 示更新 department 名称 时 由 ef core 生成的部分 t-sql :

set nocount on;

update [department] set [name] = @p0
where [departmentid] = @p1 and [rowversion] = @p2;

select [rowversion]
from [department]
where @@rowcount = 1 and [departmentid] = @p1;

前面的 代 码显 示包含 rowversion 的 where 子句。 如果数据 库 rowversion 不等于 rowversion 参数( @p2
), 则 不更新行。

@@rowcount 返回受上一 语 句影响的行数。 在没有行更新的情况下, ef core 引 发
dbupdateconcurrencyexception

此文主要是为了方便自己记录学习,如有错误,欢迎指正

这里附上参考资料:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

  • ef core 验证提取属性后是否未更改属性。 调用 savechanges 或 savechangesasync 时会执行此检查。
  • 如果提取属性后更改了属性,将引发 dbupdateconcurrencyexception。