首页 > 其他分享 >Entity Framework教程-异步和并发操作(Asynchronous And Concurrency Operations)

Entity Framework教程-异步和并发操作(Asynchronous And Concurrency Operations)

时间:2022-10-26 14:46:13浏览次数:109  
标签:Operations 异步 令牌 并发 数据库 Entity Framework public

更新记录
转载请注明出处:
2022年10月26日 发布。
2022年10月22日 从笔记迁移到博客。

待完善

C#综合揭秘——Entity Framework 并发处理详解 - 风尘浪子 - 博客园 (cnblogs.com)
Entity Framework 并发冲突解决方案_喵叔-CSDN博客

EF Core中的异步方法

异步方法大部分是定义在Microsoft.EntityFrameworkCore这个命名空间下EntityFrameworkQueryableExtensions等类中的扩展方法,记得using。

比如下列这些方法:

AddAsync()、AddRangeAsync()、AllAsync()、AnyAsync、
AverageAsync、ContainsAsync、CountAsync、FirstAsync、
FirstOrDefaultAsync、ForEachAsync、LongCountAsync、MaxAsync、
MinAsync、SingleAsync、SingleOrDefaultAsync、SumAsync

有的方法没有异步方法,比如IQueryable的这些异步的扩展方法都是“立即执行”方法,而GroupBy、OrderBy、Join、Where“非立即执行”方法则没有对应的异步方法。为什么?因为“非立即执行”方法并没有实际执行SQL语句,并不是消耗IO的操作。

如何异步遍历IQUERYABLE

方式1、ToListAsync()、ToArrayAsync().结果集不要太大。
方式2、await foreach (Book b in ctx.Books.AsAsyncEnumerable())
不过,一般没必要这么做。

并发说明

说明

并发控制:避免多个用户同时操作资源造成的并发冲突问题。

数据库层面的两种策略:悲观、乐观。

最好的解决方案:非数据库解决方案。

举例:统计点击量。

EF中的并发问题

比如下述的执行流程:

用户1取得表1的数据,并且正在修改中

​ 用户2在用户1后,取得表1的数据,并且正在修改中

​ 此时用户1提交数据到表1中

​ 然后用户2提交数据到表1中

​ 这时造成用户1的提交的数据全部消失

并发冲突(concurrency conflicts)

乐观并发(Optimistic concurrency):并发令牌

乐观并发控制(Optimistic concurrency):RowVersion

1、SQLServer数据库可以用一个byte[]类型的属性做并发令牌属性,然后使用IsRowVersion()把这个属性设置为RowVersion类型,这样这个属性对应的数据库列就会被设置为ROWVERSION类型。对于ROWVERSION类型的列,在每次插入或更新行时,数据库会自动为这一行的ROWVERSION类型的列其生成新值。
2、在SQLServer中,timestamp和rowversion是同一种类型的不同别名而已。
3、乐观并发控制能够避免悲观锁带来的性能、死锁等问题,因此推荐使用乐观并发控制而不是悲观锁。
4、如果有一个确定的字段要被进行并发控制,那么使用IsConcurrencyToken()把这个字段设置为并发令牌即可;
5、如果无法确定一个唯一的并发令牌列,那么就可以引入一个额外的属性设置为并发令牌,并且在每次更新数据的时候,手动更新这一列的值。如果用的是SQLServer数据库,那么也可以采用RowVersion列,这样就不用开发者手动来在每次更新数据的时候,手动更新并发令牌的值了。

悲观并发(Pessimistic concurrency)

1、悲观并发控制一般采用行锁、表锁等排他锁对资源进行锁定,确保同时只有一个使用者操作被锁定的资源。
2、EF Core没有封装悲观并发控制的使用,需要开发人员编写原生SQL语句来使用悲观并发控制。不同数据库的语法不一样。
3、锁是独占、排他的,如果系统并发量很大的话,会严重影响性能,如果使用不当的话,甚至会导致死锁。
4、不同数据库的语法不一样。

悲观并发控制的使用比较简单,使用对应数据库的锁SQL语句即可。

解决并发问题的方法

说明

使用EF提供的并发令牌实现自动解决并发问题,无需时间戳

数据表设置最后一次更新时间字段,每次提交之前手动检测是否相同

使用EF提供的令牌解决并发

使用数据注解方式设置

public class Blog
{
  //...其他属性
  [ConcurrencyCheck]
  public string Context { get; set; }
}

使用FluentAPI方式设置

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
    .ToTable("Blog")
    .Property(x=>x.Context)
    .IsConcurrencyToken();
}

使用最后一次更新时间解决并发

使用数据注解方式设置

public class Post 
{
    [Timestamp]
    public byte[] Timestamp { get; set; }
}

使用FluentAPI方式设置

public class Blog
{
    // Code removed for brevity
    public byte[] Timestamp { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
    .ToTable("Blog")
    .Property(x=>x.Timestamp)
    .ValueGeneratedOnAddOrUpdate()
    .IsConcurrencyToken();
}

处理并发冲突(Concurrency Conflicts)

处理并发冲突的三种方式

说明

Database wins(数据库优先)

Client wins(客户端优先)

User-specific custom resolution(自定义规则)

Database wins(数据库优先)

将用户提交的内容丢弃,重载Entity再提交即可

var transactions = new TransactionScope();
try
{
    // 执行代码
}
catch (DbUpdateConcurrencyException dbUpdateConcurrencyException)
{
    try
    {
        //出现异常,不保存用户数据,直接将实体进行重载再提交
        dbUpdateConcurrencyException.Entries.Single().Reload();
        await _context.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    catch (Exception exception)
    {
        ExceptionDispatchInfo.Capture(exception.InnerException).Throw();
    }
}

Client wins(客户端优先)

直接将entity的原始值设置为指定的值,再进行提交到数据库

var transactions = new TransactionScope();
try
{
    //....
}
catch (DbUpdateConcurrencyException dbUpdateConcurrencyException)
{
    try
    {
        //获得
        var entry = dbUpdateConcurrencyException.Entries.Single();
        //强制将entity原始值设置为指定值
        entry.OriginalValues.SetValues(entry.GetDatabaseValues());
        await _context.SaveChangesAsync();
        return RedirectToAction("Index");
    }
    catch (Exception exception)
    {
        ExceptionDispatchInfo.Capture(exception.InnerException).Throw();
    }
}

User-specific custom resolution(自定义规则)

标签:Operations,异步,令牌,并发,数据库,Entity,Framework,public
From: https://www.cnblogs.com/cqpanda/p/16820809.html

相关文章

  • rest_framework权限源码分析
    位置APIView---->dispatch方法---->initial方法--->self.check_permissions(request)(APIView的对象方法)分析defcheck_permissions(self,request):"""Chec......
  • rest_framework认证源码分析
    认证源码分析位置:APIVIew----》dispatch方法---》self.initial(request,*args,**kwargs)---->有认证,权限,频率三个版块分析:只读认证源码:self.perform_authenticatio......
  • Entity Framework教程-测试(Unit Testing, Integration Testing, and Mocking)
    更新记录转载请注明出处:2022年10月25日发布。2022年10月22日从笔记迁移到博客。生成测试数据(seeddata)说明有几种方法可以创建模拟数据:使用FluentAPIOnModelC......
  • 【Vapor】05 Chapter 7: CRUD Database Operations
    0x00Chapter7:CRUDDatabaseOperations在​​routes.swift​​​文件内写各种路由操作数据库的记录1.create创建记录,之前的文章已经写过了需要提交数据url:​​ht......
  • Mono 2.11.3 发布包含微软的 Entity Framework
    在Mono团队Xamarin获得1200W美金融资后,Xamarin的MigueldeIcaza's(Mono项目创始人)在博客上发布了他们的Mono2.11的第四个预览版2.11.3.Mono2.11带来......
  • Spring Boot异常org.springframework.beans.factory.NoUniqueBeanDefinitionException
    https://blog.csdn.net/qq_33697094/article/details/114579346?spm=1001.2101.3001.6650.4&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-4......
  • MASA Framework 缓存入门与设计
    概念什么是缓存,在项目中,为了提高数据的读取速度,我们会对不经常变更但访问频繁的数据做缓存处理,我们常用的缓存有:本地缓存内存缓存:IMemoryCache分布式缓存Redis:......
  • AMF: ASP.NET Mobile Framework
    ASP.NET移动框架(AMF)是一种最常用的平板电脑和智能手机上Web应用程序的框架,基于jQueryMobile(http://jquerymobile.com)的asp.net开源项目,项目...ASP......
  • Jetbrains Rider:缺少.NET Framework 4.5.2
    前提:由于我们是应用程序的开发者,所以我们要下载的是SDK或者DeveloperPack;而不是Runtime包(runtime是给应用程序的用户使用的)。前提2:SDK安装完毕后,Rider的反应可能......
  • Entity Framework教程-数据迁移(migrations)
    更新记录转载请注明出处:2022年10月23日发布。2022年10月22日从笔记迁移到博客。EF迁移介绍EF迁移说明EFCoreAPIbuildstheEFCoremodelfromthedomain......