之前有关EF并发探讨过几次,但是呢,博主感觉还是有问题,为什么会觉得有问题,其实就是理解不够透彻罢了,于是在项目中都是用的存储过程或者SQL语句来实现,利用放假时间好好补补EF Core并发的问题,本文比较长,请耐心点看。
EntityFramework Core并发初级版初探关于并发无非就两种:乐观并发和悲观并发,悲观并发简言之则是当客户端对数据库中同一值进行修改时会造成阻塞,而乐观并发则任何客户端都可以对可以对数据进行查询或者读取,在EF Core中不支持悲观并发,结果则产生并发冲突,所以产生的冲突则需要我们去解决。
为了便于理解我们从基础内容开始讲起,稍安勿躁,我们循序渐进稍后会讲到并发冲突、并发解决、并发高级三个方面的内容。我们建立实体类如下:
public class Blog { public int Id { get; set; } public string Name { get; set; } public string Url { get; set; } public int Count { get; set; } }
接下来简单配置下映射:
public class EFCoreContext : DbContext { public DbSet<Blog> Blogs { get; set; } OnConfiguring(DbContextOptionsBuilder optionsBuilder) => optionsBuilder.UseSqlServer(); OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<Blog>(pc => { pc.ToTable().HasKey(k => k.Id); pc.Property(p => p.Name).IsRequired(); pc.Property(p => p.Url).IsRequired(); pc.Property(p => p.Count).IsRequired(); }); } }
接下来我们简单封装下进行查询和更新数据的类 DbQueryCommit
public class DbQueryCommit : IDisposable { private readonly EFCoreContext context; public DbQueryCommit(EFCoreContext context) => this.context = context; public TEntity Query<TEntity>(params object[] keys) where TEntity : class => this.context.Set<TEntity>().Find(keys); public int Commit(Action change) { change(); return context.SaveChanges(); } public DbSet<TEntity> Set<TEntity>() where TEntity : class => context.Set<TEntity>(); public void Dispose() => context.Dispose(); }
接下来我们来看看非并发的情况,进行如下查询和修改:
NoCheck( DbQueryCommit readerWriter1, DbQueryCommit readerWriter2, DbQueryCommit readerWriter3) { int id = 1; Blog blog1 = readerWriter1.Query<Blog>(id); Blog blog2 = readerWriter2.Query<Blog>(id); readerWriter1.Commit(() => blog1.Name = nameof(readerWriter1)); readerWriter2.Commit(() => blog2.Name = nameof(readerWriter2)); Blog category3 = readerWriter3.Query<Blog>(id); Console.WriteLine(category3.Name); }
当前博主VS版本为2017,演示该程序在控制台,之前我们有讲过若要进行迁移需要安装 Microsoft.EntityFrameworkCore.Tools.DotNet 程序包,此时我们会发现根本都安装不上,如下:
不知为何错误,此时我们需要在项目文件中手动添加如上程序包,(解决方案来源于:https://docs.microsoft.com/en-us/ef/core/miscellaneous/cli/dotnet)如下:
<ItemGroup> <DotNetCliToolReference Include=Version=/> </ItemGroup>
然后添加程序包 Microsoft.EntityFrameworkCore.Design ,此时我们再来 dotnet restore 则会看到如下执行EF的命令:
接下来我们实例化上下文进行修改数据。
var efContext1 = new EFCoreContext(); var d1 = new DbQueryCommit(efContext1); var efContext2 = new EFCoreContext(); var d2 = new DbQueryCommit(efContext2); var efContext3 = new EFCoreContext(); var d3 = new DbQueryCommit(efContext3); Concurrency.NoCheck(d1, d2, d3);
此时我们在数据库中默认插入一条数据:
此时界面打印最后读取到的Name值如下:
数据库也对应进行了更新,这也充分说明EF Core对于并发为乐观并发:
接下来我们对Name属性定义为并发Token。
pc.Property(p => p.Name).IsRequired().IsConcurrencyToken();
此时为了很好演示各个方法,我们同样再来定义并发方法,如下: