EF Core并发控制

这篇具有很好参考价值的文章主要介绍了EF Core并发控制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

EF Core并发控制

并发控制概念

  1. 并发控制:避免多个用户同时操作资源造成的并发冲突问题。
  2. 最好的解决方案:非数据库解决方案
  3. 数据库层面的两种策略:悲观、乐观

悲观锁

悲观并发控制一般采用行锁 ,表锁等排他锁对资源进行锁定,确保同时只有一个使用者操作被锁定的资源。

EF Core没有封装悲观并发控制的使用,需要开发人员编写原生SQL语句来使用悲观并发控制。不同数据库语法不一样。

MySQL方案:select * from T_Houses where Id = 1 for update

如果有其他查询操作也使用for update来查询Id=1的这条数据的话,那些查询就会被挂起,一直到针对这条数据的更新操作完成从而释放这个行锁,代码才会继续执行。

代码实现

根据数据库安装对应Nuget包,Mysql如下:

也可以使用官方的,没什么影响

Pemelo.EntityFrameworkCore.MySql

House类

class House
{
	public long Id { get; set; }
	public string Name {get;set;}	
	public string Owner {get;set;}
}

HouseConfig类

public class HouseConfig:IEntityTypeConfiguration<House>
{
    public void Configure(EntityTypeBuilder<House> builder)
    {
        builder.ToTable("T_Houses");
        builder.Property(b => b.Name).IsRequired();
    }
}

DbContext类

public class MyDbContext:DbContext
{
     public DbSet<House> Houses { get; set; }
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        base.OnConfiguring(optionsBuilder);
        var connString = "server=localhost;user=root;password=root;database=ef1";
        var serverVersion = new MySqlServerVersion(new Version(5, 7, 35));
        optionsBuilder.UseMySql(connString, serverVersion);
    }
}

迁移数据库

然后执行数据库迁移

安装Nuget:Microsoft.EntityFrameworkCore.Design,Microsoft.EntityFrameworkCore.Tools

  • Add-Migration Init
  • Update-database

随便给数据库添加几条信息

没有悲观版本

    public static void Main(string[] args)
    {
        Console.WriteLine("请输入您的名字");
        string name = Console.ReadLine();
        using (MyDbContext db = new MyDbContext())
        {
            var h = db.Houses.Single(h => h.Id == 1);
            if (!string.IsNullOrEmpty(h.Owner))
            {
                if (h.Owner == name)
                {
                    Console.WriteLine("房子已经被你抢到了");
                }
                else
                {
                    Console.WriteLine($"房子已经被【{h.Owner}】占了");
                }
                return;
            }
            h.Owner = name;
            Thread.Sleep(10000);
            Console.WriteLine("恭喜你,抢到了");
            db.SaveChanges();
            Console.ReadLine();
        }
    }

EF Core并发控制

EF Core并发控制

可以看到实际上是jack抢到了,但是tom也打印了抢到!

有悲观锁的版本

锁和事务是相关的,因此通过BeginTransactionAsync()创建一个事务,并且在所有操作完成后调用CommitAsync()提交事务

Console.WriteLine("请输入您的名字");
string name = Console.ReadLine();
using MyDbContext db = new MyDbContext();
using (var tx = db.Database.BeginTransaction())
{
    Console.WriteLine($"{DateTime.Now}准备select from update");
    //加锁
    var h = db.Houses.FromSqlInterpolated($"select * from T_houses where Id = 1 for update").Single();
    Console.WriteLine($"{DateTime.Now}完成select from update");
    if (!string.IsNullOrEmpty(h.Owner))
    {
        if (h.Owner == name)
        {
            Console.WriteLine("房子已经被你抢到了");
        }
        else
        {
            Console.WriteLine($"房子已经被【{h.Owner}】占了");
        }
        Console.ReadKey();
        return;
    }
    h.Owner = name;
    Thread.Sleep(5000);
    Console.WriteLine("恭喜你,抢到了");
    db.SaveChanges();
    Console.WriteLine($"{DateTime.Now}保存完成");
    //提交事务
    tx.Commit();
    Console.ReadKey();
}

EF Core并发控制

可以看到tom 在27:58秒的时候完成了锁,所以程序提交的时候是tom抢到了,而不是jack,当执行SaveChanges()之前,行的锁会一直存在,直到Commit()事务提交之后才会释放锁,这时jack才会完成锁。

EF Core并发控制

问题

  1. 悲观并发控制的使用比较简单。
  2. 锁是独占、排他的,如果系统并发量很大的话,会严重影响性能,如果使用不当的话,甚至会导致死锁。
  3. 不同数据库的语法不一样。

乐观锁

原理

Update T_House set Owner = 新值 where Id = 1 and Owner = 旧值

当Update的时候,如果数据库中的Owner值已经被其他操作更新为其他值了,那么where语句的值就会为false,因此这个Update语句影响的行数就是0,EF Core就知道发生并发冲突了,因此SaveChanges()方法就会抛出DbUpdateConcurrencyException异常。

EF Core配置

  1. 把被并发修改的属性使用IsConcurrencyToken()设置为并发令牌,

  2. public class HouseConfig:IEntityTypeConfiguration<House>
    {
        public void Configure(EntityTypeBuilder<House> builder)
        {
            builder.ToTable("T_Houses");
            builder.Property(b => b.Name).IsRequired();
            builder.Property(h => h.Owner).IsConcurrencyToken(); //这里设置列
        }
    }
    
  3. Console.WriteLine("请输入您的名字");
    string name = Console.ReadLine();
    using (MyDbContext db = new MyDbContext())
    {
        var h = db.Houses.Single(h => h.Id == 1);
        if (!string.IsNullOrEmpty(h.Owner))
        {
            if (h.Owner == name)
            {
                Console.WriteLine("房子已经被你抢到了");
            }
            else
            {
                Console.WriteLine($"房子已经被【{h.Owner}】占了");
            }
    
            Console.ReadKey();
            return;
        }
        h.Owner = name;
        Thread.Sleep(5000);
        try
        {
            db.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            Console.WriteLine("并发访问冲突");
            var entry1 = ex.Entries.First();
            string newValue = entry1.GetDatabaseValues().GetValue<string>("Owner");
            Console.WriteLine($"被{newValue}抢先了");
        }
        Console.ReadLine();
    }
    

效果截图

EF 生成的sql语句

EF Core并发控制

EF Core并发控制

多字段RowVersion

  1. SQLServer数据库可以用一个byte[]类型的属性做并发令牌属性,然后使用IsRowVersion()把这个属性设置为RowVersion类型,这样这个属性对应的数据库列就会被设置为ROWVERSION类型。对于这个类型的列,在每次插入或更新行时,数据库会自动为这一行的ROWVERSION类型的列其生成新值。
  2. 在SQLServer中,timestamp和rowversion是同一种类型的不同别名而已。

注意这里换成SQLServer数据库了!

实体类及配置

public class House
{
    public long Id { get; set; }
    public string Name { get; set; }
    public string? Owner {get;set;}
    public byte[]? RowVer{get;set;}
}
//builder.Property(h => h.Owner).IsConcurrencyToken(); //删除掉
builder.Property(h=>h.RowVer).IsRowVersion();

效果截图

EF Core并发控制

EF Core并发控制

概念

  1. 在MySQL(某些版本)等数据库中虽然也有类似的timestamp类型,但是由于timestamp类型的精度不够,并不适合在高并发的系统。
  2. 非SQLServer中,可以将并发令牌列的值更新为Guid的值
  3. 修改其他属性值的同时,使用h1.Rowver = Guid.NewGuid()手动更新并发令牌属性的值。

总结

  1. 乐观并发控制能够避免悲观锁带来的性能、死锁等问题,因此推荐使用乐观并发控制而不是悲观锁。
  2. 如果有一个确定的字段要被进行并发控制,那么使用IsConcurrencyToken()把这个字段设置为并发令牌即可。
  3. 如果无法确定一个唯一的并发令牌列,那么就可以引入一个额外的属性设置为并发令牌,并且在每次更新数据的时候,手动更新这一列的值。如果用的是SQLServer数据库,那么也可以采用RowVersion列,这样就不用开发者手动来在每次更新数据的时候,手动更新并发令牌的值了。

参考链接

  • NuGet Gallery | Pomelo.EntityFrameworkCore.MySql 7.0.0 (https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql
  • 【.NET 6教程,.Net Core 2022视频教程,杨中科主讲】 https://www.bilibili.com/video/BV1pK41137He/?p=89&share_source=copy_web&vd_source=fce337a51d11a67781404c67ec0b5084

每日一道面试题

  1. 什么是装箱和拆箱?

    答:从值类型接口转换到引用类型装箱。从引用类型转换到值类型拆箱。

  2. 抽象类和接口的相同点和不同点有哪些?何时必须声明一个类为抽象类?

    相同点:

    1. 都是用来实现抽象和多态的机制。
    2. 都不能被实例化,只能被继承或实现。
    3. 都可以包含抽象方法,即没有具体实现的方法。
    4. 都可以被子类继承或实现,并在子类中实现抽象方法。

    不同点:

    1. 抽象类可以包含非抽象方法,而接口只能包含抽象方法。
    2. 类只能继承一个抽象类,但可以实现多个接口。
    3. 抽象类的子类可以选择性地覆盖父类的方法,而接口的实现类必须实现接口中定义的所有方法。
    4. 抽象类可以有构造方法,而接口不能有构造方法。、

    一个类必须声明为抽象类的情况:

    1. 当类中存在一个或多个抽象方法时,类必须声明为抽象类。
    2. 当类需要被继承,但不能被实例化时,类必须声明为抽象类。
    3. 当类中的某些方法需要在子类中实现,而其他方法已经有了具体实现时,类可以声明为抽象类。

    总结:抽象类和接口都是实现抽象和多态的机制,但抽象类更适合用于一些具有公共实现的类,而接口更适合用于定义一组相关的方法,供多个类实现。抽象类可以包含非抽象方法和构造方法,而接口只能包含抽象方法。文章来源地址https://www.toymoban.com/news/detail-692286.html

到了这里,关于EF Core并发控制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • EF Core入门

    EF Core是微软官方提供的ORM框架。EF Core不仅可以操作Microsoft SQL Server、MySQL、Oracle、PostgreSQL等数据库,而且可以操作Azure Cosmos DB等NoSQL数据库 前提条件:已经完整安装了Microsoft SQL Server 下面是一个实际操作EF Core的演示 这是项目最终的目录,这里需要关注的就是 .cs 文件 首先新

    2023年04月09日
    浏览(36)
  • 【EF Core】实体的主、从关系

    假设有以下两个实体: Homework 类表示家庭作业,它并不是独立使用的,而是与学生类(Student)有依赖关系。一位学生有多个家庭作业记录,即 Homework 对象用于记录每位同学的作业的。按照这样的前提,Student 是主对象,Homework 是从对象。 Student 对象有个 Homeworks 属性,用于引

    2024年02月11日
    浏览(48)
  • Net Core中使用EF Core连接Mysql数据库

    Entity Framework Core的前身是微软提供并主推的ORM框架,简称EF,其底层是对ADO.NET的封装。EF支持SQLServer、MYSQL、Oracle、Sqlite等所有主流数据库。 首先是使用时的几个模式的整理及其理解: Code First:根据代码自动创建数据库表结构甚至是数据库,可以支持多库开发,代码较少冗余

    2024年01月24日
    浏览(52)
  • EF Core + MySQL 基本增删改查

    基于EF Core + MySQL的基本增删改查,示例是基于.NET6 + EF Core + MySQL 创建实体和数据库、EFCore 数据迁移项目基础上的内容增加。同时也是对基于Canal实现MySQL 8.0 数据库数据同步项目的验证。 Controllers----添加----控制器,选择api----包含读写操作的API控制器。 将上下文类注入到User

    2024年02月08日
    浏览(51)
  • EF Core 在实际开发中,如何分层?

    分层就是将 EF Core 放在单独的项目中,其它项目如 Asp.net core webapi 项目引用它 这样的好处是解耦和项目职责的清晰划分,并且可以重用 EF Core 项目 但是也会数据库迁移变得复杂起来 创建一个 .NET 类库项目,项目名字为 BooksEFCore 引用以下 Nuget 包 Microsoft.EntityFrameworkCore.Relati

    2024年01月24日
    浏览(46)
  • 使用EF Core创建webapi接口(二)

    有错误欢迎大家给我指正 说明:netcore webapi+net6+EF Core版本,codefirst模式(代码创建数据库) 1.netcore webapi+net6+EF Core版本,dbfirst模式(代码生成数据库)见:使用EF Core创建webapi接口(一)-CSDN博客 2.netcore webapi+net6+EF Core+vue前后端联动版本,见netcore webapi+net6+EF Core+vue3前后端联动-CSD

    2024年02月21日
    浏览(44)
  • EF Core预编译模型Compiled Model

    最近还在和 npgsql 与 EF Core 斗争,由于 EF Core 暂时还不支持 AOT,因此在 AOT 应用程序中使用 EF Core 时,会提示问题: 听这个意思,似乎使用 Compiled Model 可以解决问题,于是就又研究了一下 EF Core 的这个功能。 在 EF Core 中,模型根据实体类和配置构建,默认情况下,每次创建

    2024年02月05日
    浏览(43)
  • Ef Core花里胡哨系列(4) 多租户

    当然,我们要考虑设计问题,例如,切换 Schema 或者改变数据库时, Ef Core 同样也会刷新改实体的缓存,所以,首次查询将会很慢,不适合大表。 在我的上一篇博客中 [Ef Core花里胡哨系列(3) 动态修改实体对应的表(分表)、多租户] 中我们实现了如何分表,同理,我们可以用近

    2024年02月03日
    浏览(37)
  • EF Core实操,数据库生成实体,迁移

    大家好,我是行不更名,坐不改姓的宋晓刚,下面将带领大家进入C#编程EF Core数据库基础入门知识,如何连接数据库,如何编写代码,跟上我的步伐进入EF Core数据库下的世界。 家人们,如果有什么不懂,可以留言,或者加我联系方式,一起进入微软技术的开拓。 微信:153

    2024年01月22日
    浏览(47)
  • ASP.NET Core Web API入门之三:使用EF Core

    一般来讲我们做项目都会用实体类跟数据库实体进行关系对应,这样的好处方便我们维护、增删改查,并且可以减少SQL的编写,从而统一风格,那么 Entity Framework Core 就是很不错的ORM框架。 1、跨数据库支持能力强大,只需修改配置就可以轻松实现数据库切换。 2、提升了开发效

    2024年02月10日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包