前言

对过年已经无感,不过还是有很多闲暇时间来学学东西和多陪陪爸妈,这一点是极好的,好了,本节我们来讲讲EntityFramework Core中的并发问题。

话题(EntityFramework Core并发)

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

    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; }
}

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

    public class HomeController : Controller
{
private IBlogRepository _blogRepository;
public HomeController(IBlogRepository blogRepository)
{
_blogRepository = blogRepository;
}
public IActionResult Index()
{
var blog = _blogRepository.GetSingle(d => d.Id == );
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);
}
}

视图渲染数据如下:

@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>

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

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

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

Database operation expected to affect 1 row(s) but actually affected 0 row(s). 
Data may have been modified or deleted since entities were loaded.
See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

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

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

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

    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);
}
}

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

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://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.

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

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上下文来操作。

     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();
}
}
}

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

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

总结

本节我们比较详细的讲解了EntityFramework Core中的并发问题以及该如何解决,到这里算是基本结束,我才发现在项目当中未经测试我居然用错了,明天去修改修改,这里算是一个稍微详细的讲解吧,如果进行压力测试不知道结果会怎样,后续进行压力测试若有进一步的进展再来完善,到时再来更新EF Core并发后续,好了,不早了,晚安。

EntityFramework Core解决并发详解的更多相关文章

  1. EntityFramework Core映射关系详解

    前言 Hello,开始回归开始每周更新一到两篇博客,本节我们回归下EF Core基础,来讲述EF Core中到底是如何映射的,废话少说,我们开始. One-Many Relationship(一对多关 ...

  2. 补知识:EntityFramework Core映射关系详解

    前言 本节我们回归下EF Core基础,来讲述EF Core中到底是如何映射的,废话少说,我们开始. One-Many Relationship(一对多关系) 首先我们从最简单的一对多关系说起,我们给 ...

  3. net core体系-web应用程序-4net core2.0大白话带你入门-5asp.net core环境变量详解

    asp.net core环境变量详解   环境变量详解 Windows操作系统的环境变量在哪设置应该都知道了. Linux(centos版本)的环境变量在/etc/profile里面进行设置.用户级的 ...

  4. Linux core dump file详解

    Linux core dump file详解 http://www.cnblogs.com/langqi250/archive/2013/03/05/2944931.html

  5. 基于ORA-12170 TNS 连接超时解决办法详解

    转自原文 基于ORA-12170 TNS 连接超时解决办法详解 1.开始----程序-----Oracle------配置和移植工具-----Net Manager----本地----服务命名---o ...

  6. 【转】asp.net core环境变量详解

    asp.net core环境变量详解 环境变量详解 Windows操作系统的环境变量在哪设置应该都知道了. Linux(centos版本)的环境变量在/etc/profile里面进行设置.用户级的环境 ...

  7. EntityFramework Core高并发深挖详解,一纸长文,你准备好了吗?

    前言 之前有关EF并发探讨过几次,但是呢,博主感觉还是有问题,为什么会觉得有问题,其实就是理解不够透彻罢了,于是在项目中都是用的存储过程或者SQL语句来实现,利用放假时间好好补补EF Core并发的问 ...

  8. asp.net core环境变量详解

    环境变量详解 Windows操作系统的环境变量在哪设置应该都知道了. Linux(centos版本)的环境变量在/etc/profile里面进行设置.用户级的环境变量在其它文件里面,不多说了,有兴趣的 ...

  9. MongoDB via Dotnet Core数据映射详解

    用好数据映射,MongoDB via Dotnet Core开发变会成一件超级快乐的事.   一.前言 MongoDB这几年已经成为NoSQL的头部数据库. 由于MongoDB free schema ...

随机推荐

  1. PL\SQL学习笔记

    注释 单行--多行 一.declare一般用于做变量的申明.begin 程序体开始执行  end; 程序体结束exception .. dbms_output.put_line('绝对值'||v_ab ...

  2. hibernate---一对一单向外键关联--XML

    Student.java: package com.bjsxt.hibernate; public class Student { private int id; private String nam ...

  3. mysql的数据类型int、bigint、smallint 和 tinyint取值范围

    使用整数数据的精确数字数据类型.   bigint   从 -2^63 (-9223372036854775808) 到 2^63-1 (9223372036854775807) 的整型数据(所有数字 ...

  4. (简单) POJ 3126 Prime Path,BFS。

    Description The ministers of the cabinet were quite upset by the message from the Chief of Security ...

  5. Linux监控体系

    监控体系 zabbix 博客:www.abcdocker.com 微信公众号:abcdocker 笔者QQ:381493251 Abcdocker交流群:454666672 如果遇到什么问题可以进群询 ...

  6. iOS 代码实现获得应用的版本号(Version/Build)

    http://www.open-open.com/lib/view/open1411817778203.html

  7. tools_list

    http://files.cnblogs.com/files/yansc/ExportQingtaoImage.rar

  8. S3C2440 ADC详解

    S3C2440拥有八通道的十位ADC, 最大转换率为2.5MHz A/D转换器时钟下的500KSPS.A/D转换器支持片上采样-保持功能和掉电模式的操作. 八个通道中有四个通道适用于电阻屏的触摸屏触摸 ...

  9. 【转】VS2013动态库文件的创建及其使用详解

    一.VS2013动态库文件的创建 1.新建项目,win32,win32项目,输入项目名称,例如MakeDll. 2.”确定“——”下一步“,选择”DLL“选项,再点”完成“: 3.菜单栏选择”项目“— ...

  10. AFNetworing进行POST上传 分类: ios技术 2015-04-01 17:03 73人阅读 评论(0) 收藏

    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager]; //申明返回的结果是json类型 m ...