前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >asp.net core 系列之并发冲突

asp.net core 系列之并发冲突

作者头像
Vincent-yuan
发布2019-09-10 18:13:20
1.6K0
发布2019-09-10 18:13:20
举报
文章被收录于专栏:Vincent-yuanVincent-yuan

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

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

发生并发冲突的情况:

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

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

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

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

举个例子:

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

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

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

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

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

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

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

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

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

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

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

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

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

2.客户端优先

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

即John 的更改覆盖 Jane 的更改 。也就是说,下次有人浏览英语系时,将看到 2013/9/1 和提取的值 350,000.00 美元 3.存储优先

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

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

处理并发

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

  • EF Core 验证提取属性后是否未更改属性。 调用 SaveChanges 或 SaveChangesAsync 时会执行此检查。
  • 如果提取属性后更改了属性,将引发 DbUpdateConcurrencyException。

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

检测属性的并发冲突

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

检测行的并发冲突

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

注意:rowversion , 

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

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

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

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

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

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

例子

向 Department 实体添加跟踪属性

代码语言:javascript
复制
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 指定跟踪属性:

代码语言:javascript
复制
modelBuilder.Entity<Department>()
.Property<byte[]>("RowVersion")
.IsRowVersion();

以下代码显示更新 Department 名称时由 EF Core 生成的部分 T-SQL:

代码语言:javascript
复制
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 此文主要是为了方便自己记录学习,如有错误,欢迎指正

这里附上参考资料:

https://docs.microsoft.com/en-us/aspnet/core/data/ef-rp/concurrency?view=aspnetcore-2.2&tabs=visual-studio

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019-04-25 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 发生并发冲突的情况:
  • 举个例子:
  • 说了这么多,那么,并发冲突的处理方式呢?
  • 处理并发
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档