catch (DbUpdateConcurrencyException ex)
{
//通过DbUpdateConcurrencyException类的Entries属性获取并发修改冲突的EntityEntry
//并通过EntityEntry类中的GetDatabaseValuesAsync获取当前数据库的值
var entry = ex.Entries.First();
var dbValues = await entry.GetDatabaseValuesAsync();
string newOwner = dbValues.GetValue<string>(nameof(House.Owner));
Console.WriteLine($"并发冲突,被{newOwner}提前抢走了");
}
IEnumerable和IQueryable
对普通集合使用where等方法查询出来的返回值为IEnumerable类型
但是对DbSet使用用where等方法出查询出来的返回值为IQueryable类型
IQueryable继承自IEnumerable,IQueryable是延迟执行,能减少内存的压力,使用IQueryable是把where方法转换成SQL语句,使得可以在数据库中进行过滤查询。
*判断是否为立即执行方法:*如果返回值类型是IQueryable则是非立即执行方法
立即执行方法 | 非立即执行方法 |
---|---|
遍历IQueryable方法 | Where |
ToArray | GroupBy |
ToList | OrderBy |
Min | Include |
Max | Skip |
Count | Take |
-------------------------------------------------------------------------------------------------------------------------------
原理
ADO.NET中的DataReader中是分批从数据库中读取数据,而DataTable则是一次性读取数据
在遍历IQueryable时,是使用DataReader的方式读取数据,这样会一直占用一个数据库的连接。如果使用ToArray等方法,则使用了DataTable方式
在使用时如果返回的是IQueryable,请注意上下文的生效周期
使用dbCtx.Database.ExecuteSqlInterpolated
或者异步的ctx.Database.ExecuteSqlInterpolatedAsync
方法执行SQL语句
-----------------------------------------------------------------------------
新特性
1、增加了实体类的跟踪状态
2、增加了全局查询筛选器 典型场景应用:伪删除
例如在Book类中增加一个bool属性值IsDeleted,标记是否被删除
在配置类中增加builder.HasQueryFilter(b=>b.IsDeleted==false)
,这样在对Book实体类的查询都会自动加上b.IsDeleted==false这个筛选器
如果需要查询被删除的数据,可以使用IgnoreQueryFilters
来临时忽略过滤器
ctx.Books.IgnoreQueryFilters().Where(b=>b.Title.Contains("a"))
3、配置类
实现一个配置类,用来说明实体类和数据库表是怎么映射的,该配置类要实现IEnityTypeConfiguration
接口
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
class BookEntityConfig : IEntityTypeConfiguration<Book> //指定是对那个类进行配置
{
public void Configure(EntityTypeBuilder<Book> builder) //实现接口
{
builder.ToTable("T_Books"); //实体对象在数据库中表的名字是“T_Books”
//没有详细设置每个属性在数据库中的列明和数据类型
//会默认吧属性名字作为列明,并根据属性类型来推断数据库中的数据类型
//可以根据需要修改实体类的配置,进而修改数据库的表
//HasMaxLength(50):最大长度为50 IsRequired():不可为空
builder.Property(e => e.Title).HasMaxLength(50).IsRequired();
builder.Property(e => e.AuthorName).HasMaxLength(20).IsRequired();
}
}
悲观并发控制
使用事务+锁
using var tx = await ctx.Database.BeginTransactionAsync();
//for update创建用于更新的锁,如果其他用户也使用for update查询id=1的数据,则查询被挂起,这是mysql的语法
var h1 = await ctx.Houses.FromSqlInterpolated($"select * from T_Houses where Id=1 for update")
.SingleAsync();
if (string.IsNullOrEmpty(h1.Owner))
{
}
else
{
}
await tx.CommitAsync();//提交事务
乐观并发控制
EF Core内置了使用并发令牌列实现乐观并发控制,并发令牌列通常就是被并发操作影响的列
执行update hourse set Owner=新值 where Id=1 and Owner = 旧值
,如果其他人更改了Owner则where语句就是false,此时SaveChanges方法会抛出DbUpdateConcurrencyException
class HouseConfig : IEntityTypeConfiguration<House>
{
public void Configure(EntityTypeBuilder<House> builder)
{
builder.ToTable("T_Houses");
//在配置类中使用IsConcurrencyToken把Owner列设置为并发并令牌属性
builder.Property(h => h.Owner).IsConcurrencyToken();
}
}