1、使用AsNoTracking()
使用AsNoTracking()
后,将不监听对象的状态(是否被改变);当确定查询出来的数据不会改变的时候使用AsNoTracking();
使用context.Entry<User>(user).State
查看对象监听状态。
关闭所有监听:context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
开启所有监听:context.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.TrackAll;
2、正确使用Find(id=10)
来代替FirstOrDefault(t=>t.id=10)
Find
会优先查询缓存,当前面已经查询过这条数据的时候使用,而FirstOrDefault
每次都会查询数据库;当id=10
的数据被修改之后,find
查出的数据是新数据。
3、正确区分IQueryable
和IEnumerable
3.1 IEnumerable
(linq to object)用于操作内存对象。是个迭代器的实现。
3.1.1 Where(t=>t.id>10)
中的“t=>t.id>10
”是个委托
3.1.2 封装的函数的时候将返回值设为IEnumerable<T>
,即使返回return IQueryable<T>
也会立刻查询数据库
3.2 IQueryable
(linq to sql)用于操作数据库,且继承了IEnumerable
。 IQueryable
中实现了表达式目录树(Expression
),IQueryProvider
根据表达式目录树来构建sql语句。
3.2.1 Where(t=>t.id>10)
中的“t=>t.id>10
”是个表达式目录树
3.2.2 AsEnumerable()
和 AsQueryable()
如果后面不继续跟过滤条件等,效果是一样的。 如果后面加了Where / Select / Take() /Skip
等条件,AsEnumerable()
先查数据库再过滤,AsQueryable()
将条件生成sql,一起在数据库中过滤。
4、正确使用导航属性 和 延迟查询(延迟加载)
在主表对象中包含一个子表集合对象的属性就是导航属性。跟数据库中的主外键设置无关。
利用延迟加载可以叠加多次查询条件,一次性提交给数据库。
使用建议:在开发中不确定后面是否需要关联表数据,可以使用延迟加载来按需获取数据。
4.1 导航属性要延迟加载必须具备两个条件:
a、导航属性是virtual
的;
b、延迟查询必须是开启的。
EF:context.Configuration.LazyLoadingEnabled=true;
(默认就是true
的)
EF Core:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
var builder = optionsBuilder.UseSqlServer(_connStr);
optionsBuilder.UseLazyLoadingProxies(); //启用延迟加载
}
4.2 延迟查询关闭之后可以使用显示加载的方式:
加载集合: context.Entry<Company>(company).Collection(t=>t.Users).Load();
加载单个: context.Entry<Company>(company).Reference(t=>t.Users).Load();
4.3 使用ToList()
的时候会立马执行数据库sql查询,看到过很多同学先ToList()
再Where()过滤。
其它的还有:Count()
、FirstOrDefault()
、迭代器IEnumerable/foreach
等都会立刻执行sql。
封装函数的时候可以返回IQueryable<T>
,而不是IEnumable<T>
,防止立马查询数据库。
4.4 迭代(如foreach
)使用延迟查询的时候,迭代完了才关闭链接,应当避免使用这种场景。可以在迭代之前ToList()
。
4.6 延迟加载的对象(IQueryable<T>
)脱离了DbContext
上下文对象的范围后不能被查询,因为DbContext
被释放了。
5、预先加载
使用Include
,查询主表时把子表(导航属性)也一次性查出来。延迟查询关闭后也不影响的。
例如:context.Set<Company>().Include("Users").Where(t=>t.id<10);
使用建议:当开发中能确定后面一定会用到子表数据的时候可以使用预先加载。
6、使用TransactionScope
TransactionScope
可以完成多个context
多个SaveChange
的事务问题,默认一个SaveChange
是一个事务
还可以使用context.Database.BeginTransaction
做单库事务。
using (DbContext context = new DbContext())
{
using (TransactionScope tran = new TransactionScope())
{
context.SaveChange();
context.SaveChange();
//无异常会执行Complete 提交事务
tran.Complete();
}
}
7、添加Z.EntityFramework.Plus.EFCore
依赖使用一些特殊的语法
这个是免费的,但 Z.EntityFramework.Plus
的一些批量数据操作的包是收费的