首页 > 数据库 >快读《ASP.NET Core技术内幕与项目实战》EFCore2.7:杂项(查看SQL、查询筛选器、原生SQL、并发、状态跟踪)

快读《ASP.NET Core技术内幕与项目实战》EFCore2.7:杂项(查看SQL、查询筛选器、原生SQL、并发、状态跟踪)

时间:2022-10-29 20:46:35浏览次数:55  
标签:Core ASP ctx var 并发 SQL Owner public

本节内容,涉及4.5(P96-P97)、5.2(P131-P141)。主要NuGet包:如前章节所述

 

一、查看SQL语句,调试LINQ语句

//在DbContext类的OnConfiguring方式中,增加显示SQL的配置。即可在控制台中查询生成的SQL
public class MyDbContext: DbContext
{
    ......
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        ......
        optionsBuilder.LogTo(Console.WriteLine);
    }
    ......
}

//EFCore可以对接多种数据库,所以同样的LINQ,生成的SQL并不相同
//EFCore相当于一个机翻器,将LINQ翻译成不同数据库的SQL
//因为是机翻,所以翻译成的SQL,可能存在性能问题,所以需要调整LINQ
//LINQ非常灵活,同样的结果,会有多种表达方式,如果碰到性能问题,就需要进行调试
//通过optionsBuilder.LogTo可以简单的输出SQL,从而进行调试
//如果是复杂的Linq,推荐使用专业工具LinqPad进行编写和调试

 

 

二、查询筛选器,实现软删除和多租户等功能

//我们为Book实体类增加一个软删除属性
public bool IsDeleted { get; set; }

//在Book的映射配置FluentApi中设置全局筛选器
modelBuilder.Entity<Book>(b =>
{
    ......
    b.HasQueryFilter(b => b.IsDeleted == false);
});

//通过以上两部设置,所有对Book实体的查询,都会在Where查询中,自动增加IsDelete == false的查询

//查询时,可以设置忽略筛选器
ctx.Books.IgnoreQueryFilters().Where(b => b.Id > 0);

 

 

三、EFCore框架自带的执行原生SQL的方法(实际项目中,一般使用Dapper执行原生SQL,特别是做一些复杂报表)

//执行SQL非查询语句
//内插值方式传入参数,参数为FomattableString类型,不会有SQL注入问题

//定义最低价格,作为内插值参数
var lowPrice = 35.0; 
//ExecuteSqlInterpolatedAsync方法,返回插入数据库的总行数
int rows = await ctx.Database.ExecuteSqlInterpolatedAsync(@$"
    insert into T_Books(Title, PubTime, Price, AuthorName) 
    select Title, PubTime, Price, Name from T_Books where Price>{lowPrice}"
");



//执行SQL查询语句
//books为IQueryable,可以利用延迟执行和复用等特性
//FromSqlInterpolated方法只能查询单表,且属性和列要对应
//可以在返回的集合中,继续使用Include、Select等方向,进一步查询
int year = 2000;
var books = ctx.Books.FromSqlInterpolated(@$"
    select * from T_Books where DataPart(year, PubTime)>{year} order by newid()
");
foreach(var item in books.Skip(3).Take(6))
{
    Console.WriteLine(b.Title);
}

 

 

四、悲观并发和乐观并发

1、悲观并发一般采用数据库的表锁和行锁,实际开发中较少使用,此略过。但书中有个知识点,补充如下:

//锁需要结合事务使用
using var ctx = new MyDbContext();
using var tx = await ctx.Database.BeginTransactionAsync(); //启动事务
var h1 = await ctx.Houses.FromSqlInterpolated($@"
    select * from T_Houses where Id = 1 for update //for update加锁
");
......
await tx.CommitAsync(); //提交事务

 

2、乐观并发,实际开发中一般使用乐观并发。基本原理是设置一个并发令牌列,更新时,曾加一个旧值的Where查询条件,如Update T_Houses set Owner = 新值 where Id =1 and Owner = 旧值。假设现在有两个并发更新,它们同时取出旧值,并发1快一点,先执行,此时Owner等于旧值,更新成功;而并发2慢了一步,此时Owner 已经被并发1更新,不等于旧值,更新失败。乐观并发的设置有两种方式,一种是直接将更新列作为并发令牌列,另一种是新建一个timestamp类型的列作为并发令牌列(也叫rowversion列),每次插入或更新时,数据库会自动生成列值(MySQL目前不支持自动生成,可以手动生成一个Guid值)。

①方式一:将需要更新的属性Owner,设置为并发令牌列。从异常对象中获取当前数据库值有一些难度,先了解。

//实体类,属性Owner为房子的拥有者,如果有值,则说明房子被占
//House.cs
public class House
{
    public long Id { get; set; }
    public string Name { get; set; }
    public string? Owner { get; set; }
}


//DbContext类
//配置映射时,设置Owner属性列为并发令牌列,b.Property(h => h.Owner).IsConcurrencyToken();
public class MyDbContext: DbContext
{
    public DbSet<House> Houses { get; set; }
    ......

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<House>(b =>
        {
            ......
            b.Property(h => h.Owner).IsConcurrencyToken();
        });
    }
}


//模拟一个抢房子过程,生成编译后,在Debug目录,同时打开两个控制台程序进行测试
using var ctx = new MyDbContext();

/*新增几个房间
var h1 = new House { Name = "Room101" };
var h2 = new House { Name = "Room201" };
var h3 = new House { Name = "Room301" };
ctx.AddRange(h1, h2, h3);
await ctx.SaveChangesAsync();
*/

//模拟抢房子
Console.WriteLine("请输入您的姓名");
string ownName = Console.ReadLine();

var h1 = await ctx.Houses.SingleAsync(h => h.Name == "Room101");

//如果无房主
if (string.IsNullOrEmpty(h1.Owner))
{
    await Task.Delay(5000);
    h1.Owner = ownName;
    try
    {
        await ctx.SaveChangesAsync();
        Console.WriteLine("抢到手了");
    }
    catch (DbUpdateConcurrencyException ex)
    {
        var entry = ex.Entries.First();
        var dbValues = await entry.GetDatabaseValuesAsync();
        string newOwner = dbValues.GetValue<string>(nameof(House.Owner));
        Console.WriteLine($"并发冲突,被{newOwner}提前抢走了");
    }

}
//如果有房主
else
{
    if (h1.Owner == ownName)
    {
        Console.WriteLine("房间已经是你的了,不用抢了");
    }
    else
    {
        Console.WriteLine($"房子已经被 {h1.Owner} 抢走了!");
    }
}

Console.ReadLine();

 

 ②新增一个rowversion类型的并发令牌列

//首先,实体增加一个byte[]类型的列
public class House
{
    public long Id { get; set; }
    public string Name { get; set; }
    public string? Owner { get; set; }
    public byte[] RowVersion { get; set; }
}


//其次,在DbContext的映射配置中,配置这个属性列为IsRowVersion
b.Property(h => h.RowVersion).IsRowVersion();

//乐观并发的配置,都非常简单。只有原理和模拟实现,有一些复杂

 

 

五、状态跟踪。这部分留到《EFCore7的重磅更新-批量操作》章节来学习

标签:Core,ASP,ctx,var,并发,SQL,Owner,public
From: https://www.cnblogs.com/functionMC/p/16838933.html

相关文章

  • SQL高级属性-事务
    事务是在数据库上按照一定逻辑顺序执行的任务队列,既可以由用户手动执行,也可以由某种数据库程序自动执行事务的属性A原子性:保证事务中所有操作都执行完毕,否则,事务......
  • MySQL数据库数据类型
    MySQL数据库数据类型 MySQL数据类型,大致可以分为三类:数值、日期/时间和字符串(字符)类型。 数值类型:类型大小范围(有符号)范围(无符号)用途TINY......
  • Mysql日期函数
    --当前年SELECTDATE_FORMAT(NOW(),'%Y');--当前年月SELECTDATE_FORMAT(NOW(),'%Y%m');--当前年月日SELECTDATE_FORMAT(NOW(),'%Y%m%d');--当前年月日时......
  • mysql高可用架构了解
    转自:https://www.cnblogs.com/gomysql/p/3675429.html,https://www.cnblogs.com/gomysql/p/3671896.htmlhttps://cloud.tencent.com/developer/article/10315421.MHA  ......
  • mysql longer than the server configured value of ‘wait_timeout‘
    问题现象:com.mysql.jdbc.CommunicationsException:Thelastpacketsuccessfullyreceivedfromtheserverwas58129secondsago.Thelastpacketsentsuccessfullyto......
  • MySQL高级篇-第01章 Linux下MySQL的安装与使用
    1.安装前的说明1.1Linux系统以及工具的准备安装并启动好两台虚拟机CentOS7(192.168.31.200,192.168.31.201)安装xshell和xftp工具1.2查看是否安装过MySQLrpm-qa|gre......
  • 1418 - This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in
    原因分析:我们创建函数时必须指定我们的函数是否是DETERMINISTIC不确定的NOSQL没有SQl语句,当然也不会修改数据READSSQLDATA只是读取数据,当然也不会修改数据MODIFIES......
  • 达梦数据库运维常用基础SQL(三)
    作为数据库DBA运维人员,经常需要查询和监控数据库的运行情况,定位某些问题,为此我们整理出部分常用SQL,帮助大家更好的使用达梦数据库。本次整理出数据库对象信息、用户、权限、......
  • 达梦数据库运维常用基础SQL(二)
    作为数据库DBA运维人员,经常需要查询和监控数据库的运行情况,定位某些问题,为此我们整理出部分常用运维SQL,帮助大家更好的使用达梦数据库。本次整理出数据库、表和索引等相关维......
  • 达梦数据库运维常用基础SQL(一)
    作为数据库DBA运维人员,经常需要查询和监控数据库的运行情况,定位某些问题,本章整理出部分常用运维SQL,帮助大家更好的使用达梦数据库。1、查询数据库版本:查询数据库大版本号:Se......