C# 之 Entity Framework 性能优化

一,理论部分

  1. 使用延迟加载

在 Entity Framework 中,延迟加载是一种查询优化技术,它可以减少查询的数量和复杂性。延迟加载是指只有在需要时才加载相关的数据。例如,如果一个实体有一个关联实体集合,那么在访问该集合之前,不会立即加载它。这种方式可以减少查询的数量和复杂性,从而提高查询的性能和效率。

  1. 使用投影查询

投影查询是一种查询优化技术,它可以减少查询所返回的列的数量。当我们只需要查询一些特定的列时,使用投影查询可以减少数据库的工作量和网络带宽的消耗。例如,如果我们只需要查询客户的姓名和电话号码,那么我们可以使用以下投影查询:

var query = from c in context.Customers
        select new { c.Name, c.Phone };
  1. 使用索引

索引是一种优化技术,它可以加快查询的速度。在Entity Framework中,我们可以使用索引来加快查询的速度。例如,如果我们经常对客户的姓名进行查询,那么我们可以为姓名列创建索引,以加快查询的速度。

  1. 避免使用Like操作符

Like操作符是一种模糊查询,它可以匹配包含指定字符串的值。但是,在 Entity Framework 中,Like操作符会导致全表扫描,从而降低查询的性能和效率。因此,我们应该避免使用Like操作

二,实践部分

  1. 使用投影查询:一定要写Select

不写Select的时候就是Select * 会带出很多无用的字段,特别是有些大字段查询和传输都会带来性能问题。

var list = from a in list
		join b in listBind on a.Idcard equals b.Idcard into lb
		from bind in lb.DefaultIfEmpty() //bind是左连接后的结果
		join c in listPayRecord on a.DonBloodNo equals c.DonBloodNo into pr
		from payRecode in pr.DefaultIfEmpty() //左连接
		select MapMulti2One<PayInfoDto>(new { bind?.BindTime, bind?.WechatUserId }, new { PayTime = payRecode?.CreationTime, payRecode?.OrderNo });

  1. 使用延迟加载: 非必要不触发SQL执行

以下函数会触发生成SQL并执行,所以不能随便用,只用在必要的地方
for循环中一定不能用

GetAllListAsync/GetAllList
FirstAsync/First
FirstOrDefaultAsync/FirstOrDefault
ToListAsync/ToList
GetAsync/Get
  1. 主键不是Id的时候使用Get/GetAsync方式时必须添加入参

没有入参时,生成的脚本里面的Where条件会有Id=xxx

public async Task<TraApplyBillMaster> GetXLA() {
	//正确的写法
	var entitty =await  _xxxRepository.GetAll().FirstOrDefaultAsync(t=>t.BillNo== "20221226001");
	//错误的写法
	var entitty2 = await _xxxRepository.GetAsync( "20221226001");
	return entitty;
}
  1. Queryable内尽量不要带函数,否则会导致全表查询

下面的Linq中的ToString()无法翻译为SQL,产生的SQL是全表查询,再到内存中进行过滤。

var entitty =  _xxxRepository.GetAll()
	.Where(t =>  t.BillNo.ToString() == "20221226001")
	.ToList();

如下已知安全的函数,可以放心在linq中使用:

Contains =sql的 like
日期类型的.Date
  1. 连表查询尽可能不要带函数

如下代码中的OrderByDescending会导致两个全表查询,再到内存中进行连接

var entitty = _XXXMasterRepository.GetAll()
	.Join(_XXXDetailRepository.GetAll().OrderByDescending(t => t.BillNo))
	.Where(t => t.BillNo == "20221226001")
  1. 连接中不用匿名类

//这个写法会产生两个SQL,再到内存里面Join:   new { sa, se } 为匿名类,在join中无法识别出SQL
public async Task<TraApplyBillMaster> GetXLC()
{
	var entitty = _xxxRepository.GetAll()
	.Join(_xxx33Repository.GetAll(), sa => sa.BillNo, se => se.BillNo, (sa, se) => new  { sa, se });


	var entitty2 = await _xxx22Repository.GetAll()
	.Join(entitty, sc => sc.BillNo, sd => sd.sa.BillNo, (sc, sd) => sd.sa)
	.FirstOrDefaultAsync();

	return entitty2;
}
//正确的写法: 使用定义好的类XLCDto
public async Task<XLCDto> GetXLC1()
{
	var entitty = _xxxRepository.GetAll()
	.Join(_xxx33Repository.GetAll(), sa => sa.BillNo, se => se.BillNo, (sa, se) => new XLCDto { BillNo=sa.BillNo, BillNoDetail=se.BillNo });

	var entitty2 = await _xxx22Repository.GetAll()
	.Join(entitty, sc => sc.BillNo, sd => sd.BillNo, (sc, sd) => sd)
	.FirstOrDefaultAsync();

	return entitty2;
}
  1. 不要对导航属性进行过滤或map

//有问题的写法
var product = await _xxx33Repository.GetAll()
   .Join(_productRepository.GetAll().Include(t => t.BasBloodVariety)
   .Where(t => t.BasBloodVariety.Name !="1"), sa => sa.BasBloodVarietyId, se => se.BasBloodVariety.Id, (sa, se) => se.BasBloodVariety.Name)
   .FirstOrDefaultAsync();

改成下面这样

var product = await _productRepository.GetAll().Include(t => t.BasBloodVariety).Where(t => t.BasBloodVariety.Name != "1")
   .Join(_xxx33Repository.GetAll(), sa => sa.BasBloodVarietyId, se => se.BasBloodVarietyId, (sa, se) => sa).FirstOrDefaultAsync();
  1. 不要在循环中执行数据库查询,会导致大量的IO,性能极差

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值