IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> EF Core并发冲突处理方式 -> 正文阅读

[大数据]EF Core并发冲突处理方式


前言

介绍EF Core如何处理多个用户同时更新同一实体数据时出现的冲突。


一、并发冲突的处理方式

1. 悲观并发(锁定)

在从数据库读取一行内容之前,请求锁定为只读或更新访问
如果将一行锁定为更新访问,则其他用户无法将该行锁定为只读或更新访问,因为他们得到的是正在更改的数据的副本。
如果将一行锁定为只读访问,则其他人也可将其锁定为只读访问,但不能进行更新。
缺点: 编程复杂; 性能消耗; 有的数据库管理系统不支持; Entity Framework Core 未提供对它的内置支持.

2. 乐观并发

乐观并发允许发生并发冲突,并在并发冲突发生时作出正确反应.

本文只介绍乐观并发的处理方式

二、乐观并发的处理步骤

按照并发冲突检测 -> 触发异常 -> 处理异常 的步骤进行处理.
在关系数据库中,EF Core 会从 UPDATE 和 DELETE 语句的 WHERE 子句中查看并发标记的值,以检测并发冲突。
因此必须将模型配置为启用冲突检测, EF 提供两种使用并发标记的方法:

1.在模型的属性上应用并发标记(不推荐)

将 [ConcurrencyCheck] 应用于模型上的属性。
代码如下(示例):

 // 实体模型定义------------------------------------
 [Table("task_test")]
 public class TaskTest    {
   public int Id { get; set; }
   public string? Name { get; set; }      
   [ConcurrencyCheck] // 并发标记
   public int Value { get; set; }        
 }

// 并发冲突处理代码--------------------------------------------------
using (var context = new TaskContext())
{
    // 由数据库中取出一条数据 
    var task = context.TaskTests.Single(p => p.Id == 1);
    // 直接修改数据库中的这条数据来模拟并发冲突
    context.Database.ExecuteSqlRaw(
        $"UPDATE task_test SET value = {task.Value + 1} WHERE Id = 1");
    // 修改取出的数据值
    task.Value = task.Value + 2;
    var saved = false;
    while (!saved)
    {
        try
        {
            // 尝试写入该条数据
            context.SaveChanges();
            saved = true;
        }
        catch (DbUpdateConcurrencyException ex)
        {
            // 并发冲突异常处理
            foreach (var entry in ex.Entries)
            {
                if (entry.Entity is TaskTest)
                {
                    var proposedValues = entry.CurrentValues;	// 准备写入的值
                    var databaseValues = entry.GetDatabaseValues();	// 数据库中的值
                    foreach (var property in proposedValues.Properties)
                    {
                        var proposedValue = proposedValues[property];
                        var databaseValue = databaseValues[property];
                        Console.WriteLine($"属性名: {property.Name} 准备写入: {proposedValue}  数据库中: {databaseValue}");
                        // TODO: 决定将哪个值写入数据库
                        // proposedValues[property] = <value to be saved>;
                        // 将要写入数据库中的值赋给proposedValues[property]
                    }
                    // 刷新实体的原始值以进行下一次并发冲突检查
                    entry.OriginalValues.SetValues(databaseValues);
                }
                else
                {
                    throw new NotSupportedException(
                        "Don't know how to handle concurrency conflicts for "
                        + entry.Metadata.Name);
                }
            }
        }
    }
}

2.在模型中新增时间戳作为并发标记(推荐)

新增属性用[Timestamp]标记
代码如下(示例):

// 实体模型定义------------------------------------
[Table("task_row")]
public class TaskRow
{
    public int Id { get; set; }    
    public string? Name { get; set; }    
    public int Value { get; set; }   
    [Timestamp] // 并发冲突检测标志
    public byte[] RowVersion { get; set; }
}


// 并发冲突处理代码--------------------------------------------------
using var context = new TaskContext();
// 从数据库中取出一条数据
var taskRow = context.TaskRows.Single(p => p.Id == 1);
// 直接修改数据库中的这条数据来模拟并发冲突
context.Database.ExecuteSqlRaw(
    $"UPDATE task_row SET value = {taskRow.Value + 1} WHERE Id = 1");
// 修改取出的数据值
taskRow.Value = taskRow.Value + 2;
var saved = false;
while (!saved)
{
    try
    {
        await context.SaveChangesAsync();
        saved = true;
    }
    catch (DbUpdateConcurrencyException ex)
    {
        var exceptionEntry = ex.Entries.Single();
        var clientValue = (TaskRow)exceptionEntry.Entity;
        var databaseEntry = exceptionEntry.GetDatabaseValues();                    
        var databaseValue = (TaskRow)databaseEntry.ToObject();
        Console.WriteLine($"Proposed: {clientValue.Value}  DB: {databaseValue.Value}");
        // 更新实体的时间戳以进行下一次写入尝试, 也可以根据情况放弃写入
        context.Entry(taskRow).Property("RowVersion").OriginalValue = (byte[])databaseValue.RowVersion;
    }
}
}

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-04-27 11:23:07  更:2022-04-27 11:26:45 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:44:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码