EFCore的异步
异步方法大部分是定义在Microsoft.EntityFrameworkCore这个命名空间下EntityFrameworkQueryableExtensions等类中的扩展方法,记得using。
如何异步遍历IQueryable
- 使用ToListAsync()、ToArrayAsync()。但是注意结果集不要太大。
foreach (var a in await ctx. Articles. ToListAsync())
{
Console. WriteLine(a. Title);
}
- 使用await foreach(Book b inctx.Books.AsAsyncEnumerable())
await foreach (var a in ctx. Articles. AsAsyncEnumerable ())
{
Console. WriteLine(a. Title);
}
EfCore执行非查询原生 SQL语句
使用dbCtx.Database. ExecuteSqllnterpolated ()
dbCtx.Database.ExecuteSqllnterpolatedAsync()方法来执
行原生的非查询SQL语句。
可以用来执行增删改
ctx.Database.ExecuteSqlInterpolatedAsync(@$"in
sert into T_Books(Title,PubTime,Price,AuthorName)
select Title, PubTime, Price,{aName} from T_Books
where Price > {price}");
FormattableString类型可以避免sql注入
还有一个ExecutesqlRow方法,类似于以前的sqlHelp
EFCore执行实体相关的原生查询sql语句
如果要执行的原生SQL是一个查询语句,并且查询的结
果也能对应一个实体,就可以调用对应实体的DbSet的
FromSqllnterpolated()方法来执行一个查询SQL语句,同样
使用字符串内插来传递参数。
只能执行单表查询
IQueryable<Book> books =
ctx.Books.FromSqlInterpolated(@$"select * from T_Books
where DatePart(year,PubTime)>{year}order by newid()");
- SQL 查询必须返回实体类型对应数据库表的所有列;
- 结果集中的列名必须与属性映射到的列名称匹配。
- 只能单表查询,不能使用Join语句进行关联查询。但
是可以在查询后面使用Include()来进行关联数据的获
取。
EFCore执行纯原生的查询语句
直接通过EFCore连接ADO.NET,获得数据库连接对象
dbCxt.Database.GetDbConnection()获得ADO.NET Core的数据库连接对象。
DbConnection conn = ctx.Database.GetDbConnection();
if (conn.State != ConnectionState.Open)
{
conn.Open();
}
using (var cmd = conn.CreateCommand())
{
cmd.CommandText =@"xxx";
var pl = cmd.CreateParameter();
p1.ParameterName ="@year";
pl.Value = year;
cmd.Parameters.Add(p1);
using (var reader = cmd.ExecuteReader());
}
using(MyDbContext ctx=new MyDbContext())
{
DbConnection conn = ctx.Database.GetDbConnection();//拿到Context对应的底层的Connection
if(conn. State != System. Data. ConnectionState. Open)
{
await conn. OpenAsync ();
}
using (var cmd = conn. CreateCommand ())
{
cmd. CommandText = "select Price, Count(*) from T_Articles group by Price";
using fvar reader = await cmd. ExecuteReaderAsync ())
{
while (await reader. ReadAsync ())
{
double price =reader. GetDouble(0);
int count =reader.GetInt32(1);
Console. WriteLine($"{price}:{count}");
}
}
}
}
推荐使用
Dapper等矿建执行原生复杂的sql语句。
如何知道实体数据变了
只要一个实体对象和Dbcontext发生关联,EFCore就会自动跟踪这个实体的值的变化。
在执行钱,会记录实体的快照,在执行结束后,会对比快照的变化
实体的状态
- 已添加(Added):DbContext正在跟踪此实体,但数据库中尚不存在
该实体。 - 未改变(Unchanged):DbContext正在跟踪此实体,该实体存在于数据
库中,其属性值和从数据库中读取到的值一致,未发生改变 - 已修改(Modified):DbContext正在跟踪此实体,并存在于数据库中,
并且其部分或全部属性值已修改。 - 已删除(Deleted):DbContext正在跟踪此实体,并存在于数据库中,
但在下次调用SaveChanges时要从数据库中删除对应数据。 - 已分离(Detached):DbContext未跟踪该实体。
EFCore优化值AsNoTracking
不让EFCore进行自动跟踪
var items = ctx. Articles. AsNoTracking (). Take (3)T;
foreach (var e in items)
{
Console. WriteLine(e. Message);
}
实体状态跟踪的妙用
不推荐使用,不利于维护
Book bl=new Book{Id=10};//跟踪通过Id定位
b1.Title = "yzk";
var entry1 = ctx.Entry(b1);
entryl.Property("Title").IsModified = true;
Console.WriteLine(entry1.DebugView.LongView);
ctx.SaveChanges();
数据的批量删除、更新、插入
addRange()后加Range表示循环批量执行sql语句。
全局查询筛选器
public void Configure (EntityTypeBuilder<Article> builder)
{
builder. ToTable("T_Articles");
builder. Property(a => a.Title). HasMaxLength(100). IsUnicode (). IsRequired() ;
builder. Property(a => a. Message). IsUnicode (). IsRequired();
builder. HasMany (a => a. Comments). WithOne(c => c. TheArticle). HasForeignKey(c =>c.TheArticleId).IsRequired();
builder. HasQueryFilter(a=>a. IsDeleted=false;
}
忽略查询过滤器
ctx.Books.lgnoreQueryFilters().Where(b=>b.Title.Contains("o")).ToArray()
有性能陷阱
EFCore悲观并发控制
并发控制:避免多个用户同时操作资源造成并发冲突问题。举例:网上购买车票。
最好的解决方案:非数据库解决方案,也就是在程序逻辑方面解决
数据库层面的两种策略:悲观和乐观。
悲观:给车票上锁,只能我买完其他人才能买
悲观控制一般使用行锁或者表锁
EF Core没有封装悲观并发控制的使用,需要开发
人员编写原生SQL语句来使用悲观并发控制。不同数
据库的语法不一样。
Mysql
select * from T_Home where Id=1 for update
select ... for update 为锁,执行后会释放
using(MyDbContext ctx=new MyDbContext())
using(var tx=ctx.Database.BeginTransaction())
{
var h = ctx. Houses.FromSqlInterpolated($"select * from T_Houses where
Id=1 for update"). Single();
ctx.SaveChange();//运行完释放
tx.Commit();
}
乐观并发控制
乐观:并发令牌
Update T_Home set Owner is 新值 where Id=1 and Owner is 旧值
把被并发修改的属性使用IsConcurrencyToken()设置为并发令
牌。
builder.Property(h => h.Owner).IsConcurrencyToken();
using(MyDbContext ctx =new MyDbContext())
{
var h=ctx.House.Single(h=>h.id==1);
h.Owner=name;
try
{
cty.SaveChanges();
}
catch(DbUpdateConcurrencyException ex)
{
//主线程捕获不到异步的异常,所以需要处理子线程的异常
var entry = ex.Entries.First();
var dbValues = await entry.GetDatabaseValuesAsync();
string newOwner = dbValues.GetValue<string>(nameof(Hose.Owner));
dbValues.GetValue<string>(nameof(House.Owner));
Console.WriteLine($"并发冲突,被{newOwner}提前抢走了”);
}
}
RowVersion
使用情景:一个实体内,有很多个字段都不可以并发修改。
- SQLServer数据库可以用一个byte[]类型的属性做并
发令牌属性,然后使用IsRowVersion()把这个属性设置
为RowVersion类型,这样这个属性对应的数据库列就
会被设置为ROWVERSION类型。对于ROWVERSION类型
的列,在每次插入或更新行时,数据库会自动为这一
行的ROWVERSION类型的列其生成新值。 - 在SQLServer中,timestamp和rowversion是同一种
类型的不同别名而已。
在实体类上增加RowVersion
class House
{
public log Id {get;set;}
public string Name {get;set;}
public string Owner {get;set;}
public byte[] RowVersion {get;set;}
}
配置实体类的配置项,设置并发属性
class HouseConfig : IEntityTypeConfiguration<House>
{
public void Configure (EntityTypeBuilder<House> builder)
{
builder. ToTable("T_Houses");
builder. Property (b => b. Name). IsRequired();
builder. Property(b => b. RowVersion). IsRowVersion();
}
}
更改代码和上面就一样了
EFCore表达式树
先跳过了
标签:builder,实体,ctx,中科,EFCore,var,数据库,跟着 From: https://www.cnblogs.com/guan-tou6/p/18252780