学习ASP.NET Core Razor 编程系列十八——并发解决方案

2023-05-16

 

学习ASP.NET Core Razor 编程系列目录

学习ASP.NET Core Razor 编程系列一

学习ASP.NET Core Razor 编程系列二——添加一个实体

 学习ASP.NET Core Razor 编程系列三——创建数据表及创建项目基本页面

学习ASP.NET Core Razor 编程系列四——Asp.Net Core Razor列表模板页面

学习ASP.NET Core Razor 编程系列五——Asp.Net Core Razor新建模板页面

学习ASP.NET Core Razor 编程系列六——数据库初始化

学习ASP.NET Core Razor 编程系列七——修改列表页面

学习ASP.NET Core Razor 编程系列八——并发处理

学习ASP.NET Core Razor 编程系列九——增加查询功能

 学习ASP.NET Core Razor 编程系列十——添加新字段

学习ASP.NET Core Razor 编程系列十一——把新字段更新到数据库

学习ASP.NET Core Razor 编程系列十二——在页面中增加校验

学习ASP.NET Core Razor 编程系列十三——文件上传功能(一)

学习ASP.NET Core Razor 编程系列十四——文件上传功能(二)

学习ASP.NET Core Razor 编程系列十五——文件上传功能(三)

学习ASP.NET Core Razor 编程系列十六——排序

 学习ASP.NET Core Razor 编程系列十七——分组

 

 

     在文章(学习ASP.NET Core Razor 编程系列八——并发处理)中对于并发错误,我们只是简单粗暴的进行了异常捕获,然后抛出了异常。在本文中我们来看两个解决并发的方法。

    乐观并发的解决方案有以下三种:

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

    在这种情况下,数据不会丢失。 两个用户更新了不同的字段内容(例如:书名与出版社)。下次有人浏览书籍信息时,将看到书名和出版社两个人的更改。 这种更新方法可以减少导致数据丢失的冲突数。这种方法需要维持重要状态,以便跟踪所有数据库值与当前值,增加了应用复杂,可能会影响应用性能。通常不适用于 Web 应用。

    2) 可让后提交的用户更改覆盖之前用户提交的更改。

    这种方法称为“客户端优先”或“最后一个优先”方案。 (客户端的所有值优先于数据存储的值。)如果不对并发处理进行任何编码,则自动执行“客户端优先”。

    3) 可以阻止在数据库中更新后一用户提交的更改。

    这种方法,需要显示错误信息,显示当前数据和数据库中的数据,允许用户重新修改,并保存。这称为“存储优先”方案。 (数据存储值优先于客户端提交的值。)

一、客户端优化

    接下去我们来看看“客户端优先”方案。 此方法确保后一用户的提交为准,覆盖数据库中的数据。

    乐观并发允许发生并发冲突,并在并发冲突发生时作出正确反应。 例如,管理员访问用书籍信息编辑页面,将“Publishing”字段值修改为“清华大学出版社”。

1.首先,我们使用Visual Studio 2017打开Books\Edit.cshmtl.cs文件,看一下OnPostAsync()方法,代码如下。如下图。


 public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();

            }
            _context.Attach(Book).State = EntityState.Modified;

            try
            {
                await _context.SaveChangesAsync();

            }
            catch (DbUpdateConcurrencyException)
            {
                if (!_context.Book.Any(e => e.ID == Book.ID))

                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
            return RedirectToPage("./Index");
        }  

2.在Visual Studio 2017中按F5运行应用程序。在浏览器中浏览书籍信息,并在书籍列表页面中选择一条书籍信息。我们假设有两个用户要对此条书籍信息进行编辑。首先是管理员,对此条书籍信息修改了“Publishing”的信息。如下图。

 

3.在管理员单击“Save”按钮之前,Test用户访问了相同页面,并将“出版日期”修改为了“2018-01-08”。如下图。

 

4.Test用户先单击“保存”,并在浏览器的书籍信息列表页面中看到了他修改的出版日期数据保存到了数据库。如下图。

 

5.此时,管理员单击“编辑”页面上的“保存”,但页面的上的“出版日期”还是“2018-01-13”,按照“客户端优化”规则会把Test用户的修改覆盖掉。如下图。

 

 

 

二、存储优先

 

    接下去我们来看看“存储优先”方案。 此方法可确保用户在未收到警报时不会覆盖任何更改。

    首先我们来了解三组值:

  • “当前值”是应用程序尝试写入数据库的值。
  • “原始值”是在进行任何编辑之前最初从数据库中检索的值。
  • “数据库值”是当前存储在数据库中的值。

    处理并发冲突的常规方法是:

     1)在 SaveChanges 期间捕获 DbUpdateConcurrencyException

    2)使用 DbUpdateConcurrencyException.Entries 为受影响的实体准备一组新更改。

    3)刷新并发令牌的原始值以反映数据库中的当前值。

    4)重试该过程,直到不发生任何冲突。

   下面的示例,使用时间戳作为行级版本号。

1. 在Visual Studio 2017的“解决方案资源管理器”中使用鼠标左键双击打开 Models /Book.cs文件, 对User实体添加跟踪属性RowVersion,并在其上添加Timestamp特性。代码如下:


using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks; 

namespace RazorMvcBooks.Models
{

    public class Book
    { 

        public int ID { get; set; }
        [Required]
        [StringLength(50, MinimumLength = 2)]

        public string Name { get; set; }
        [Display(Name = "出版日期")]
        [DataType(DataType.Date)]

        public DateTime ReleaseDate { get; set; }
        [Range(1,200)]
        [DataType(DataType.Currency)]
        public decimal Price { get; set; }

        public string Author { get; set; }

        [ Required]
        public string Publishing { get; set; }
        [Timestamp]
        public byte[] RowVersion { get; set; }
    }

}  

 

2.在Visual Studio 2017中选择“菜单>Nuget包管理器>程序包管理器控制台”,然后在打开的程序包管理器控制台依次执行以下命令

Add-Migration RowVer

Update-Database  

3.在SQL Server Management Studio中查看Book表。如下图。

 

4.在Visual Studio 2017的“解决方案资源管理器”中使用鼠标左键双击打开 Pages/Books/Edit.cshtml.cs文件,对OnPostAsync方法进行修改。Entity Framework Core 使用包含原始 RowVersion 值的 WHERE 子句生成 SQL UPDATE 命令。如果没有行受到 UPDATE 命令影响(没有行具有原始 RowVersion 值),将引发 DbUpdateConcurrencyException 异常。代码如下:


  public async Task<IActionResult> OnPostAsync()
        {

            if (!ModelState.IsValid)
            {
                return Page();
            }

            var updBook = _context.Book.AsNoTracking().Where(u => u.ID == Book.ID).First();
            // 如果为null,则当前用户信息已经被 删除
            if (updBook == null)
            {
                return HandDeleteBook();
            }

            _context.Attach(Book).State = EntityState.Modified;
            if (await TryUpdateModelAsync<Book>(
                Book,
                "Book",
                s => s.Name, s =>s.Publishing, s => s.ReleaseDate, s => s.Price))

            {

                try
                {

                    await _context.SaveChangesAsync();
                    return RedirectToPage("./Index");
                }

                catch (DbUpdateConcurrencyException ex)
                {

                    var exceptionEntry = ex.Entries.Single();
                    var clientValues = (Book)exceptionEntry.Entity;
                    var databaseEntry = exceptionEntry.GetDatabaseValues();

                    if (databaseEntry == null)
                    {
                        ModelState.AddModelError(string.Empty, "保存失败!.当前用户信息已经被删除");
                        return Page();
                    }

                    var dbValues = (Book)databaseEntry.ToObject();
                    setDbErrorMessage(dbValues, clientValues, _context);
                    //用数据库中的 RowVersion 值设置为当前实体对象客户端界面中的RowVersion值。 用户下次单击“保存”时,将仅捕获最后一次显示编辑页后发生的并发错误。

                    Book.RowVersion = (byte[])dbValues.RowVersion;
                    //ModelState 具有旧的 RowVersion 值,因此需使用 ModelState.Remove 语句。 在 Razor 页面中,
//当两者都存在时,字段的 ModelState 值优于模型属性值。
ModelState.Remove("Book.RowVersion"); } } return Page(); } private PageResult HandDeleteBook() { Book deletedDepartment = new Book(); ModelState.AddModelError(string.Empty, "保存失败!.当前书籍信息已经被删除!"); return Page(); }

 

6.在Edit.cshtml.cs文件,添加setDbErrorMessage方法。为每列添加自定义错误消息,当这些列中的数据库值与客户端界面上的值不同时,给出相应的错误信息。代码如下:     


   private void setDbErrorMessage(Book dbValues,
                Book clientValues, BookContext context)
        {

            if (dbValues.Name != clientValues.Name)
            {
                ModelState.AddModelError("Book.Name",
                    $"数据库值: {dbValues.Name}");
            }

            if (dbValues.Publishing != clientValues.Publishing)
            {
                ModelState.AddModelError("Book.Publishing",
                    $"数据库值: {dbValues.Publishing}");
            }

            if (dbValues.ReleaseDate != clientValues.ReleaseDate)
            {
                ModelState.AddModelError("Book.ReleaseDate",
                    $"数据库值: {dbValues.ReleaseDate}");
            }
            if (dbValues.Price != clientValues.Price)
            {

                ModelState.AddModelError("Book.Price",
                    $"数据库值: {dbValues.Price}");
            }
            ModelState.AddModelError(string.Empty,"您尝试编辑的书籍信息记录被另一个用户修改了。编辑操作被取消,"
+ "数据库中的当前值已经显示。如果仍想编辑此记录,请单击“保存”按钮。");

        }     

7.在Visual Studio 2017的“解决方案资源管理器”中使用鼠标左键双击打开 Pages/Books/Edit.cshtml文件,  <form method="post">标签下面添加添加隐藏的行版本。必须添加 RowVersion,以便回发绑定值。

    <input type="hidden" asp-for="Book.RowVersion" />

8.在Visual Studio 2017中按F5运行应用程序。使用两个浏览器打开同一条书籍信息记录进行编辑,此时两个浏览器显示的书籍信息是一样的。浏览器1中的书籍信息界面。在修改了“Publishing”的数据由“清华大学出版社”修改为“机械工业出版社”,然后点击“Save”按钮。如下图。

 

9.在浏览器中单击“保存”之后,浏览器会自动跳转到书籍信息列表页面中看到了所修改的“Publishing”数据保存到了数据库。如下图。

 

 

10.在第二个浏览器中,修改“出版日期”的值,由“2018-01-13”改为“2018-01-08”。如下图。

 

11.然后使用单击“ Save”按钮。此时由于客户端界面上的信息与数据库中的值不一样,所以会出现错误提示信息。如下图。

 

  12. 把“Publishing”修改为“机械工业出版社”,再次单击“保存”,将第二个浏览器中输入的值保存到数据库。 浏览器自动跳转到书籍信息列表,可以看到保存的值。如下图。

 

13.当然如果你不做任何修改,再次点击保存,也会把当前页面上的数据保存到数据库中。如下图。

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

学习ASP.NET Core Razor 编程系列十八——并发解决方案 的相关文章

  • 《Programming in Lua 3》读书笔记(九)

    Part 已经看完了 xff0c 然后进入了part 阶段了 日期 xff1a 2014 7 6 Part 11 Data Structures Lua中的数据结构都是以table实现的 队列 List 61 function List n
  • 关于LINUX的NVIDIA显卡驱动安装

    LINUX 中已经集成了一些组件的相关驱动 xff0c 但是随着机器相关组件芯片不断更新 xff0c 相应的驱动程序也在不断的更新 xff0c LINUX 中集成的驱动程序难免有些不能满足需要 xff0c 其最突出的问题 xff0c 莫过于
  • 性能专题

    if与switch 当多个条件语句判断时 xff0c switch比多个if语句的性能高 转载于 https www cnblogs com lanchong archive 2011 11 03 2234180 html
  • 自制户外登山地图傻瓜书(转载)

    自制户外登山地图傻瓜书 2009 02 19 灰羊羊 转载请注明出处 第一章 前言 大概4年前喜欢上了户外运动 xff0c 从此一发不可收拾 xff0c 07年初买了一个GPS xff0c 最初只是为了避免在山中迷路 xff0c 随着使用的
  • 文件加密和解密 - 密钥存储

    当我们想要做一次加密系统 xff0c 或者只是有一个关于这个问题 xff0c 它是如何保存的加密和解密密钥 一般认为想要的文件加密和解密 xff0c 对称算法用于 一般是AES要么DES 这就存在密钥管理的问题 xff0c 它是如何 xff
  • spring mvc绑定复杂对象报错“Could not instantiate property type [com.ld.net.spider.pojo.WorkNode] to auto-gro...

    解决方法之一 xff1a 1 确保所有的Pojo都包含了默认的构造器 xff1b
  • Docker容器 暴露多个端口

    1 创建容器是指定 docker run p lt host port1 gt lt container port1 gt p lt host port2 gt lt container port2 gt 2 修改dockerfile ex
  • 巴特沃斯(Butterworth)滤波器 (1)

    下面深入浅出讲一下Butterworth原理及其代码编写 1 首先考虑一个归一化的低通滤波器 xff08 截止频率是1 xff09 xff0c 其幅度公式如下 xff1a 当n gt 时 xff0c 得到一个理想的低通滤波反馈 xff1c
  • 用预训练的densenet121模型训练cifar10数据集

    densenet121采用pytorch预训练模型 xff0c 这里用cifar10作为数据集 import torchvision models as models import ssl ssl create default https
  • Ubuntu环境下安装DBoW2

    简介 DBoW2 is an improved version of the DBow library an open source C 43 43 library for indexing and converting images in
  • java如何将二进制转换为十进制

    1 使用java内部提供的方法 xff0c 直接进行api的调用 public static void binaryTodecimal2 int n String res 61 Integer toBinaryString n System
  • Spring Cloud Feign 请求动态URL

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 1 FeignClient 中不要写url 使用 64 RequestLine 修饰方法 2 调用地方必须引入 FeignClientConfiguration 必须有De
  • 折半查找:查找成功的最少/多次数、平均次数,查找不成功的最少/多次数、平均次数...

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 最方面的方法是建立一个判定树 现在有11个数 xff1a xff08 第1行是索引 xff0c 第2行是数 xff09 0 1 2 3 4 5 6 7 8 9 10 7 1
  • 关于maven打包 “程序包com.sun.deploy.net不存在” 的问题

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 关于maven打包 程序包com sun deploy net不存在 的问题 遇到问题如下 xff1a INFO payGateway 1 0 SNAPSHOT SUCCE
  • 印度理工学院有多难考?

    http app myzaker com news article php pk 61 599546401bc8e08604000085 印度理工学院有多难考 xff1f 何赟08 17 原文是六月高考季时给公众号 34 中印对话 34 x
  • iOS系统下 的手机屏幕尺寸 分辨率 及系统版本 总结

    今天 我对iOS系统下 的手机屏幕尺寸 分辨率 及系统版本做了一次系统总结 供大家参考 首先 是系统 xff1a 随着iOS 系统不断升级 xff0c 现在已经到iOS7 0了 xff0c 并且TA有了很多新变化 xff0c 最震撼的就是
  • android ViewFlipper的使用

    屏幕切换指的是在同一个Activity内屏幕见的切换 xff0c 最长见的情况就是在一个FrameLayout内有多个页面 xff0c 比如一个系统设置页面 xff1b 一个个性化设置页面 通过查看 OPhone API文档可以发现 xff
  • Linux下路由配置梳理

    在日常运维作业中 xff0c 经常会碰到路由表的操作 下面就linux运维中的路由操作做一梳理 xff1a 先说一些关于路由的基础知识 xff1a 1 xff09 路由概念 路由 xff1a 跨越从源主机到目标主机的一个互联网络来转发数据包
  • ASP.NET成员角色系列(一)--验证与授权入门

    在当今的信息世界里 无论是门户网站 电子商务 社区论坛 都有一个共性 它们通常都需要验证当前用户的身份并根据验证结果判断用户所具有的权限 例如博客园 它允许未注册的匿名用户可能查看帖子 但是不允许他们发表帖子 为了能够发表帖子 匿名用户必须
  • 老赵谈IL(4):什么时候应该学IL,该怎么学IL

    又是一个拖了半年的系列 xff0c 可能是前几篇主要以事实为准 xff0c 举例子的文章总是比较容易写的 xff0c 因此十分顺畅 而最后一篇打算做一个总结 xff0c 以讲道理为主 却发现该将的似乎都已经讲完了 不过做事要有始有终 xff

随机推荐

  • FreeRTOS的第一个任务是怎么跑起来的

    一 一般在程序末尾会有一个vTaskStartSheduler 函数 span class hljs keyword int span main span class hljs keyword void span BSP INIT Bina
  • STM32-正弦波可调(50HZ~20KHZ可调、峰峰值0~3.3V可调)

    1 原理 通过定时器每隔一段时间触发一次DAC转换 然后通过DMA发送正玄波码表值给DAC 当需要改变频率HZ 时 只需要修改定时器频率 即可 最高只能达到20KHz 当需要改变 正玄波的正峰峰值 负峰峰值 时 只需要修改正玄波码表 即可
  • .Net ASP.NET 打开指定文件夹

    比如要打开指定的文件夹 xff0c 而不是弹出对话框 System Diagnostics Process Start 64 34 D 34 这样就打开了D盘 和正常打开D盘是一样的
  • 几种更新(Update语句)查询的方法

    正 文 数据库更新就一种方法Update xff0c 其标准格式 xff1a Update 表名 set 字段 61 值 where 条件 只是依据数据的来源不同 xff0c 还是有所差别的 xff1a 1 从外部输入 这样的比較简单 例
  • mysql8.0.13 cmd 登陆报错

    今天打算配置一个php运行环境 xff0c 将php mysql apache依次下载好 xff0c 我首先安装的是mysql xff0c 安装过程很顺利 xff0c 在cmd输入mysql uroot p的时候 xff0c 我靠 xff0
  • vue移动端的自适应布局的两种解决方案

    目标 前端开发移动端及H5时候 xff0c 不需要再关心移动设备的大小 xff0c 只需要按照固定 设计稿的px值布局 基础知识 dpr xff08 设备像素比 xff09 css的像素px不等于设备像素 分辨率 各种值 xff0c css
  • 对单片机数码管显示段选位选的理解

    在51单片机的数码管的应用开发中一些小的细节还是应该注意到的 其中位选信号应该在段选之前打开 xff0c 下面是一段示例代码 xff08 我用的是国信长天开发板 xff09 xff1a include lt reg51 h gt 包含51单
  • http请求中get请求可以缓存和post请求不可缓存

    2019独角兽企业重金招聘Python工程师标准 gt gt gt GET请求后退 刷新无害 xff0c POST后退 刷新则会导致重新提交数据 GET书签可被收藏 POST为书签不可收藏 GET能被缓存 POST不能被缓存 GET编码类型
  • VMWare中虚拟机(CentOS)如何开启虚拟化功能

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 需求说明 xff1a VMware版本如下示 xff0c 在此VMware上创建了虚拟机并安装了CentOS6 5系统 现在需要在此客户机 xff08 VM xff09 上
  • C# 常见的错误类型

    Exception 应用程序执行期间发生错误 SystemException 系统异常 所有Exception的基类 ArgumentException 当方法提供的任意一个参数无效时 xff0c 引发此异常 ArithmeticExcep
  • 字典树 trie树 学习

    一字典树 字典树 xff0c 又称单词查找树 xff0c Trie树 xff0c 是一种树形结构 xff0c 哈希表的一个变种 二 性质 根节点不包含字符 xff0c 除根节点以外的每一个节点都只包含一个字符 xff1b 从根节点到某一节点
  • 《Linux 内核完全注释》阅读笔记

    在阅读源代码之前 xff0c 有必要对Linux内核的体系结构 源代码的目录结构有个宏观地了解 xff0c Linux内核完全注释 非常详细地介绍了这方面的内容 xff0c 所以 这里仅仅进行概述性的讨论 xff0c 以便让所有的笔记构成一
  • 抽象工厂模式(C++)

    define win 0 define mac 1 include lt iostream gt using namespace std class button public button virtual button virtual v
  • 大智慧显示切换服务器,大智慧怎么改界面 大智慧改界面教程

    很多软件的界面都可以根据每个用户不同的需求进行定制 xff0c 大智慧炒股软件也是如此 在大智慧的版面设计功能中 xff0c 用户可以将几十种不同功能的窗口自由组合摆放 xff0c 直到配置出满意的界面 大智慧的版面设计可以建立分析功能窗口
  • threadx将linux作为进程,如何在Windows操作系统上模拟ThreadX应用程序

    是的 xff0c 你可以的 xff0c 如果你愿意投入的工作 首先观察到每个线程系统调用都有一个等价的posix调用 xff0c 除了事件 因此 xff0c 您的线程程序可以使用posix线程 xff0c 互斥锁等作为单个进程运行 事件可以
  • STL"源码"剖析-重点知识总结

    STL是C 43 43 重要的组件之一 xff0c 大学时看过 STL源码剖析 这本书 xff0c 这几天复习了一下 xff0c 总结出以下LZ认为比较重要的知识点 xff0c 内容有点略多 1 STL概述 STL提供六大组件 xff0c
  • inter处理器(CPU)的分类

    对于台式机和笔记本电脑 xff0c 最常见的是酷睿 奔腾和赛扬系列 xff0c 同代产品中他们的性能依次减弱 xff0c 酷睿最强 xff0c 奔腾次之 xff0c 赛扬最弱 xff08 酷睿 gt 奔腾 gt 赛扬 xff09 对于智能手
  • 利用iftop查看网络带宽使用情况

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 利用iftop查看服务器实时流量 yum install y gcc flex byacc libpcap ncurses ncurses devel libpcap de
  • matlab程序改为m文件名,在MATLAB中,程序文件的扩展名为.m,所以程序文件也称为M文件...

    在MATLAB中 xff0c 程序文件的扩展名为 m xff0c 所以程序文件也称为M文件 答 xff1a 磷酸果糖激酶 2催化6 磷酸果糖生成的产物是 答 xff1a 2 xff0c 6 二磷酸果糖 人类行为的经济学分析 的作者是 答 x
  • 学习ASP.NET Core Razor 编程系列十八——并发解决方案

    学习ASP NET Core Razor 编程系列目录 学习ASP NET Core Razor 编程系列一 学习ASP NET Core Razor 编程系列二 添加一个实体 学习ASP NET Core Razor 编程系列三 创建数据