1.基本优化方式:
- 避免使用大的数据库事务,尽量控制在有需求是打开,不需要是及时关闭,因为会锁定资源
- 批量插入表数据,尽量避免在同一DBContext下做多次SavceChange操作
- 若有大批数据需要插入表,尽量采用单表集中插入后再操作后续集,避免插入一条数据savaChange一次
- 读取数据尽量按批量读取,避免取得一条数据读取一次
- 查询100次单条记录与一次性查询100条记录是有很大差别的
2.LInq的真假分页
query.ToList().Skip((PageIndex - 1) * PageSize).Take(PageSize);
query.Skip((PageIndex - 1) * PageSize).Take(PageSize).ToList();
- 第一条(假分页)
先把数据查询出来,放到内存中;转换成List;从List中进行分页操作,查询出结果 - 第二条(真分页)
将参数传到数据库,生成分页sql语句,然后再查询出结果
3.合理使用EF的加载方式
4.事务尽量简短
- 尽量要把与事务无关的东西放到事务外执行,如果让一个事务的执行时间过长,很容易引起资源死锁的问题,当用压力测时,马上就出现资源被锁的错误。
- 查询语句或者其他事务外的语句
5.NoTracking的使用
若查询出的实体不需要删除和修改,使用NoTracking
using (var context = new MyDbContext())
{
var people = context.People
.Where(p => p.PersonID >100)
.AsNoTracking()
.ToList();
}
有时我们的实体只需要显示,无需更新,所以为了提高性能,我们不需要实体被EF context追踪。此时可以使用NoTracking的查询来得到实体,这样实体的状态会是Detached状态
6.SQL Server Profiler的使用
对于逻辑相对复杂的查询,要随时监控生成的Sql语句
毕竟EF生成的语句,往往比我们生成的语句更加复杂,这个时候我们就要考虑是否通过其他方式来提高性能。比如自己写原生的sql语句,有时候原生SQL语句是更好的选择。另外我们要善于使用SQL Server Profiler这个工具,实时监控生成的sql语句。
7.EF的根源拓展
#region 按条件查询:LoadItems(Func<T, bool> whereLambda)
/// <summary>
/// 按条件查询
/// </summary>
/// <param name="whereLambda">lambda表达式</param>
/// <returns>IQueryable 泛型集合</returns>
public IQueryable<T> LoadItems(Func<T, bool> whereLambda)
{
return MyBaseDbContext.Set<T>().Where(whereLambda).AsQueryable();
}
#endregion
方法的参数whereLambda,其类型为Func<T, bool> ,在调用LoadItems方法时,通过SQL Server Profiler检测生成的sql语句,你会发现它只生成了一条查询全部结果的语句。然后通过一步步断点调试发现,将数据全部加载完毕后,还会根据查询条件whereLambda进行很多次的循环,才能把最终结果返回。并且当调用带条件参数的查询方法时,都会遇到这个问题。不过当发现问题之后,基本也就能解决问题了。随后我们发现了与Func<TObject, bool> 非常相似的类型Expression<Func<TObject, bool>>
解决方案
- Expression<Func<TObject, bool>>为表达式类型,它会带上查询条件一并生成sql语句,将查询结果返回,大大提高查询效率
Lambda表达式作为表达式树形式的数据。
修改后代码
#region 按条件查询:
LoadItems(Expression<Func<T, bool>> whereLambda)
/// <summary>
/// 按条件查询
/// </summary>
/// <param name="whereLambda">lambda表达式</param>
/// <returns>IQueryable 泛型集合</returns>
public IQueryable<T> LoadItems(Expression<Func<T, bool>> whereLambda)
{
return MyBaseDbContext.Set<T>().Where(whereLambda).AsQueryable();
}
#endregion
8.其他:
- Include()方法的使用
- 通过edmx中表的导航属性名字,来查询出关联的表的数据
var students= conext.Person.Include("Students");
//就是在person的表中有Students这个Propetry连接另外一个表
- 如果需要查出关联表的关联,则在Include中可以用.连接
var students= conext.Person.Include("Students.Student_Course.Courses");
//查询Person对象的也查出来了student,course的信息
-
也可以通过=>的方式
-
基本
var blogs1 = context.Blogs
.Include(b => b.Posts)
.ToList();
- 进阶
var blogs1 = context.Blogs
.Include(b => b.Posts.Select(p => p.Comments))
.ToList();
- Query()方法的使用(显式加载):1
有过滤条件时
static void QueryLodgingDistance()
{
using (var ctx = new BreakAwayContext())
{
var france = ctx.Destinations.First(t => t.Country == "法国");
var lessTenMilesLodgings = ctx.Entry(france)
.Collection(t => t.Lodgings)
.Query()
.Where(t => t.MilesFromNearestAirport < 10);
foreach (var lodging in lessTenMilesLodgings)
{
Console.WriteLine(lodging.Name);
}
}
}
当有关联,有需要做Count()操作时,可以用Query方法。
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
// Count how many posts the blog has.
var postCount = context.Entry(blog)
.Collection(b => b.Posts)
.Query()
.Count();
}
EF学习和使用(八)你必须知道的效率问题根源之Expression与Func
https://www.cnblogs.com/nianming/archive/2013/01/09/2846952.html ↩︎