广告
返回顶部
首页 > 资讯 > 数据库 >EntityFramework Core解决并发详解
  • 907
分享到

EntityFramework Core解决并发详解

2024-04-02 19:04:59 907人浏览 泡泡鱼
摘要

话题(EntityFramework Core并发)对于并发问题这个话题相信大家并不陌生,当数据量比较大时这个时候我们就需要考虑并发,对于并发涉及到的内容也比较多,在EF Core中我们将并发分为几个小节来

话题(EntityFramework Core并发

对于并发问题这个话题相信大家并不陌生,当数据量比较大时这个时候我们就需要考虑并发,对于并发涉及到的内容也比较多,在EF Core中我们将并发分为几个小节来陈述,让大家看起来也不太累,也容易接受,我们由浅入深。首先我们看下给出的Blog实体类。

 EntityFramework Core解决并发详解

    public class Blog : IEntityBase
    {        public int Id { get; set; }        public string Name { get; set; }        public string Url { get; set; }        public ICollection<Post> Posts { get; set; }
    }

 EntityFramework Core解决并发详解

对于在VS2015中依赖注入仓储我们就不再叙述,比较简单,我们看下控制器中的两个方法,一个是渲染数据,一个是更新数据的方法,如下:

 EntityFramework Core解决并发详解

    public class HomeController : Controller
    {        private IBlogRepository _blogRepository;        public HomeController(IBlogRepository blogRepository)
        {
            _blogRepository = blogRepository;
        }        public IActionResult Index()
        {            var blog = _blogRepository.GetSingle(d => d.Id == 1);            return View(blog);
        }

        [HttpPost]        public IActionResult Index(Blog obj)
        {            try
            {
                _blogRepository.Update(obj);
                _blogRepository.Commit();
            }            catch (Exception ex)
            {
                ModelState.AddModelError("", ex.Message);
            }            return View(obj);
        }
    }

 EntityFramework Core解决并发详解

视图渲染数据如下:

 EntityFramework Core解决并发详解

@using StudyEFCore.Model.Entities
@model Blog<html><head>
    <title></title></head><body>
    @using (Html.BeginFORM("Index", "Home", FormMethod.Post))
    {        <table border="1" cellpadding="10">
            <tr>
                <td>博客ID :</td>
                <td>
                    @Html.TextBoxFor(m => m.Id,
       new { @readonly = "readonly" })            </td>
        </tr>
        <tr>
            <td>博客名称 :</td>
            <td>@Html.TextBoxFor(m => m.Name)</td>
        </tr>
        <tr>
            <td>博客地址:</td>
            <td>@Html.TextBoxFor(m => m.Url)</td>
        </tr>
        <tr>
            <td colspan="2">
                <input type="submit" value="更新" />
            </td>
        </tr>
    </table>
    }
    @Html.ValidationSummary()</body></html>

 EntityFramework Core解决并发详解

最终在页面上渲染的数据如下:

 EntityFramework Core解决并发详解

接下来我们演示下如何引起并发问题,如下:

 EntityFramework Core解决并发详解

上述我们通过在视图页面更新值后然后在SaveChanges之前打断点,然后我们在数据库中改变其值,再来SaveChanges此时会报异常,错误信息如下:


 
See http:

因为在我们页面上改变其值后未进行SaveChanges,但是此时我们修改了Name的值,接着再来SaveChanges,此时报上述错误也就是我们本节所说的并发问题。既然出现了这样的问题,那么我们在EF Core中该如何解决出现的并发问题呢?在这里我们有两种方式,我们一一来陈述。

EF Core并发解决方案一(并发Token)

既然要讲并发Token,那么在此之前我们需要讲讲并发Token到底是怎样工作的,当我们对属性标识为并发Token,当我们从数据库中加载其值时,此时对应的属性的并发Token也就通过上下文而分配,当对分配的并发Token属性的相同的值进行了更新或者删除,此时会强制该属性的并发Token去进行检测,它会去检测影响的行数量,如果并发已经匹配到了,然后一行将被更新到,如果该值在数据库中已经被更新,那么将没有数据行会被更新。对于更新或者删除通过在WHERE条件上包括并发Token。接下来我们对要更新的Name将其设置为并发Token,如下:

 EntityFramework Core解决并发详解

    public class BlogMap : EntityMappinGConfiguration<Blog>
    {        public override void Map(EntityTypeBuilder<Blog> b)
        {
            b.ToTable("Blog");
            b.HasKey(k => k.Id);            b.Property(p => p.Name).IsConcurrencyToken();          
            b.Property(p => p.Url);
            b.HasMany(p => p.Posts).WithOne(p => p.Blog).HasForeignKey(p => p.BlogId);
        }
    }

 EntityFramework Core解决并发详解

当我们进行如上设置后再来迁移更新模型,最终还是会抛出如下异常:

Database operation expected to affect  row(s) but actually affected  row(s). 
Data may have been modified or deleted since entities were loaded. 
See http:

接下来我们再来看看解决并发而设置行版本的情况。

EF Core并发解决方案二(行版本)

当我们在插入或者更新时都会产生一个新的timestamp,这个属性也会被当做一个并发Token来对待,它会确保当我们更新值时但是其值已经被修改过时一定会如上所述抛出异常。那么怎么使用行版本呢,(我们只讲Fluent api关于Data Annotations请自行查找资料)在实体中定义如下属性:

 public byte[] RowVersion { get; set; }

接着对该属性进行如下配置。

b.Property(p => p.RowVersion).IsConcurrencyToken().ValueGeneratedOnAddOrUpdate();

当我们再次进行如上演示时肯定会抛出同样的异常信息。 

上述两种从本质上都未能解决在EF Core中的并发问题只是做了基础的铺垫,那么我们到底该如何做才能解决并发问题呢,请继续往下看。

解析EF Core并发冲突

我们通过三种设置来解析EF Core中的并发冲突,如下:

当前值(Current values):试图将当前修改的值写入到到数据库。

原始值(Original values):在未做任何修改时的需要从数据库中检索到的值。

数据值(Database values):当前保存在数据库中的值。

由于并发会抛出异常,所以我们需要 在SaveChanges时在并发冲突所产生的异常中来进行解决,并发异常呈现在 DbUpdateConcurrencyException 类中,我们只需要在此并发异常类解决即可。比如上述我们需要修改Name的值,我们做了基础的铺垫,设置了并发Token。但是还是会引发并发异常,未能解决问题,这个只是解决并发异常的前提,由于我们利用的仓储来操作数据,但是并发异常会利用到EF上下文,所以我们额外定义接口,直接通过上下文来操作,如下我们定义一个接口

    public interface IBlogRepository : IEntityBaseRepository<Blog>
    {        void UpdateBlog(Blog blog);
    }

解决并发异常通过EF上下文来操作。

 EntityFramework Core解决并发详解

     public class BlogRepository : EntityBaseRepository<Blog>,
        IBlogRepository
    {        private EFCoreContext _efCoreContext;        public BlogRepository(EFCoreContext efCoreContext) : base(efCoreContext)
        {
            _efCoreContext = efCoreContext;
        }        public void UpdateBlog(Blog blog)
        {            try
            {
                _efCoreContext.Set<Blog>().Update(blog);
                _efCoreContext.SaveChanges();
            }            catch (DbUpdateConcurrencyException ex)
            {                foreach (var entry in ex.Entries)
                {                    if (entry.Entity is Blog)
                    {                        var databaseEntity = _efCoreContext.Set<Blog>().AsNoTracking().Single(p => p.Id == ((Blog)entry.Entity).Id);                        var databaseEntry = _efCoreContext.Entry(databaseEntity);                        foreach (var property in entry.Metadata.GetProperties())
                        {                            var proposedValue = entry.Property(property.Name).CurrentValue;                            var originalValue = entry.Property(property.Name).OriginalValue;                            var databaseValue = databaseEntry.Property(property.Name).CurrentValue;                            // TODO: Logic to decide which value should be written to database
                            var propertyName = property.Name;                            if (propertyName == "Name")
                            {                                // Update original values to
                                entry.Property(property.Name).OriginalValue = databaseEntry.Property(property.Name).CurrentValue;                                break;
                            }
                        }
                    }                    else
                    {                        throw new NotSupportedException("Don't know how to handle concurrency conflicts for " + entry.Metadata.Name);
                    }
                }                // Retry the save operation                _efCoreContext.SaveChanges();
            }
        }
    }

 EntityFramework Core解决并发详解

上述则是通用解决并发异常的办法,我们只是注意上述表明的TODO逻辑,我们需要得到并发的属性,然后再来更新其值即可,我们对于Name会产生并发,所以遍历实体属性时获取到Name,然后更新其值即可,简单粗暴,完胜。我们看如下演示。

 EntityFramework Core解决并发详解

上述我们将Name修改为efcoreefcore,在SaveChanges前修改数据库中的Name,接着再来进行SaveChanges时,此时肯定会走并发异常,我们在并发异常中进行处理,最终我们能够很清楚的看到最终数据库中的Name更新为efcoreefcore,我们在最后重试一次在一定程度上可以保证能够解决并发。


您可能感兴趣的文档:

--结束END--

本文标题: EntityFramework Core解决并发详解

本文链接: https://www.lsjlt.com/news/34646.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • EntityFramework Core解决并发详解
    话题(EntityFramework Core并发)对于并发问题这个话题相信大家并不陌生,当数据量比较大时这个时候我们就需要考虑并发,对于并发涉及到的内容也比较多,在EF Core中我们将并发分为几个小节来...
    99+
    2022-10-18
  • EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解
    EntityFramework Core 1.1方法理论详解当我们利用EF Core查询数据库时如果我们不显式关闭变更追踪的话,此时实体是被追踪的,关于变更追踪我们下节再叙。就像我们之前在EF 6.x中讨论...
    99+
    2022-10-18
  • GoLang切片并发安全解决方案详解
    目录1.介绍切片并发问题2.实践检验真理3.回答切片并发安全问题4.解决切片并发安全问题方式5.附1.介绍切片并发问题 关于切片的,Go语言中的切片原生支持并发吗? 2.实践检验真理...
    99+
    2022-11-11
  • Java 并发 - ThreadLocal详解
    ThreadLocal是通过线程隔离的方式防止任务在共享资源上产生冲突, 线程本地存储是一种自动化机制,可以为使用相同变量的每个不同线程都创建不同的存储。 @pdaiJava 并发 - ThreadLocal详解带着BAT大厂的面试问题去理...
    99+
    2022-12-02
    java框架 java全栈 java学习路线 java全栈知识 java面试 知识体系 java技术体系 java编程
  • Mysq详细讲解如何解决库存并发问题
    目录面临的问题如何实现需求具体实现的方案总结面临的问题 长话短说,假设我们现在面临以下需求 商品的库存有两千,卖完为止某商品本日的售卖只允许卖出一百,卖完为止 如何实现 我提出的方案...
    99+
    2022-11-13
  • java高并发的并发级别详解
    目录阻塞无饥饿(Starvation-Free)无障碍(Obstruction-Free)无锁(Lock-Free)等待总结阻塞、无饥饿、无障碍、无锁、无等待几种。 阻塞 一个线程是...
    99+
    2022-11-12
  • java并发编程详解
    一.synchronized的缺陷synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢?如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其...
    99+
    2016-07-17
    java
  • 高并发解决方案
    好的,下面是高并发解决方案的详细教程: 水平扩展    - 水平扩展是指增加机器数量,通过将负载分散到多个机器上来解决高并发问题。常用的方式包括负载均衡、分布式缓存等。    - 负载均衡:将流量分发到多台服务器上,防止单台服务器过载。 ...
    99+
    2023-09-22
    数据库 服务器 缓存
  • mysql并发怎么解决
    本篇文章给大家分享的是有关mysql并发怎么解决,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。mysql并发怎么解决1.利用水平分库分表,由单...
    99+
    2022-10-18
  • 详解利用redis + lua解决抢红包高并发的问题
    抢红包的需求分析 抢红包的场景有点像秒杀,但是要比秒杀简单点。 因为秒杀通常要和库存相关。而抢红包则可以允许有些红包没有被抢到,因为发红包的人不会有损失,没抢完的钱再退回给发红包的人即可。 另外像小米这...
    99+
    2022-06-04
    详解 抢红包 redis
  • 详解go语言的并发
    目录1、启动go语言的协程2、runtime.Goexit()方法。立即终止当前的协程3、runtime.GOMAXPROCS()表示go使用几个cpu执行代码4、管道定义和创...
    99+
    2022-06-07
    详解go语言 GO 并发 go语言
  • golang并发锁使用详解
    目录互斥锁 sync.Mutex 读写锁 sync.RWMutex 如果程序用到的数据是多个groutine之间的交互过程中产生的,那么使用上文提到的channe...
    99+
    2023-02-23
    golang并发与锁 Golang 并发死锁 Golang 并发锁
  • Redis解决高并发问题
    1 模拟商品抢购和并发的效果 这里模拟一个商品抢购的过程所带来的问题,以及解决问题的思路。 这里模拟的商品抢购过程是一个商品正常购买的过程,其中包含了两个主要的步骤:商品库存减少和商品购买记录的添加。...
    99+
    2023-09-21
    redis 数据库 mysql
  • dubbo怎么解决高并发
    要解决Dubbo的高并发问题,可以从以下几个方面着手:1. 调整Dubbo的配置:可以通过调整Dubbo的线程池和连接池等参数,增加...
    99+
    2023-10-20
    dubbo
  • ASP.NET Core依赖注入详解
    目录一、什么是依赖注入二、使用框架提供的服务三、注册服务四、生命周期五、请求服务六、设计你的依赖服务ASP.NET Core的底层设计支持和使用依赖注入。ASP.NET Core应用...
    99+
    2022-11-13
  • 详解asp.net core 依赖注入
    前言   好久没有写微博了,因为前段时间由于家庭原因决定从工作了3年多的北京转移到上海去。依赖注入在学习net core的时候也有写过类似的东西,只是实践的较少,结果来到上海新...
    99+
    2022-06-07
    net ASP.NET core ASP
  • 详解.NET Core中的Worker Service
    目录Worker Service项目程序和后台服务Worker Service 中使用Logging运行Worker Service当你想到ASP.NET Core时,可能会想到We...
    99+
    2022-11-11
  • Go并发之RWMutex的源码解析详解
    目录使用场景源码解析RWMutex结构体Lock()方法Unlock()方法RLock()方法RUnlock()方法RWMutex是一个支持并行读串行写的读写锁。RWMutex具有写...
    99+
    2023-03-15
    Go RWMutex源码 Go RWMutex
  • 详解Java多线程与并发
    目录一、进程与线程二、并发与并行1、线程安全问题2、共享内存不可见性问题三、创建线程1、继承Thread类2、实现Runable接口3、实现Callable接口四、Thread类详解...
    99+
    2022-11-12
  • java ReentrantLock并发锁使用详解
    目录一、ReentrantLock是什么1-1、ReentrantLock和synchronized区别1-2、ReentrantLock的使用1-2-1、ReentrantLock...
    99+
    2022-11-13
    java ReentrantLock并发锁 java ReentrantLock
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作