C# ef core中查询的长时间响应
我正在为事务创建一个select,当按用户和日期对我们进行分组时,我还需要显示累加器。结果是正确的,但处理此查询的时间为60秒 我使用的是ef core 2.2,我尝试使用类似的linq表达式,但响应时间是相同的C# ef core中查询的长时间响应,c#,entity-framework,.net-core,ef-core-2.2,C#,Entity Framework,.net Core,Ef Core 2.2,我正在为事务创建一个select,当按用户和日期对我们进行分组时,我还需要显示累加器。结果是正确的,但处理此查询的时间为60秒 我使用的是ef core 2.2,我尝试使用类似的linq表达式,但响应时间是相同的 public async Task<ListDataPagination<AdvanceIn, TotalizerAdvanceInDate>> AdvanceInDateAsync(int page, int? limit=15) {
public async Task<ListDataPagination<AdvanceIn, TotalizerAdvanceInDate>> AdvanceInDateAsync(int page, int? limit=15)
{
IQueryable<AdvanceIn> query;
var data = new ListDataPagination<AdvanceIn, TotalizerAdvanceInDate>();
query = Context.SaleTransaction
.Include(s => s.AdminSale)
.ThenInclude(a => a.PlaceCategory)
.Include(s => s.Transaction)
.Where(s => s.PayIn.Date <= DateTimeOffset.UtcNow.Date.AddDays(6)
&& s.Paid == false
&& s.ProcessingPaid == false
&& (s.Transaction.Status == TransactionStatus.Authorized || s.Transaction.Status == TransactionStatus.Paid) )
.GroupBy(s => new { s.AdminSaleId, s.PayIn.Date })
.Select(s => new AdvanceIn()
{
Count = s.Count(),
NetAmountSum = s.Select(t => t.NetAmount).Sum(),
GrossAmountSum = s.Select(t => t.Transaction.GrossAmountWithoutSaleDiscount).Sum(),
AdminSale = new SaleTransferReadModel {
Id = s.Select(t => t.AdminSale).First().Id,
PlaceName = s.Select(t => t.AdminSale).First().PlaceName,
PlacePhoto = s.Select(t => t.AdminSale).First().PlacePhoto
},
PayIn = s.Select(t => t.PayIn).First(),
SaleTransactionId = String.Join(",", s.Select(x => x.Id.ToString()))
})
.OrderBy(s => s.PayIn);
var list = await query.AsNoTracking().ToListAsync();
data.Totalizer = new TotalizerAdvanceInDate {
Count = list.Sum(t => t.Count),
NetAmountSum = list.Sum(a => a.NetAmountSum),
GrossAmountSum = list.Sum(a => a.GrossAmountSum)
};
data.Page = page;
data.TotalItems = list.Count();
data.TotalPages = data.TotalItems / limit.Value;
data.Data = query.Skip(limit.Value * page)
.Take(limit.Value)
.ToList();
data.PageTotalizer = new TotalizerAdvanceInDate {
Count = data.Data.Sum(a => a.Count),
NetAmountSum = data.Data.Sum(a => a.NetAmountSum),
GrossAmountSum = data.Data.Sum(a => a.GrossAmountSum)
};
return data;
}
public异步任务高级数据同步(int page,int?limit=15)
{
可查询查询;
var data=new ListDataPagination();
query=Context.SaleTransaction
.包括(s=>s.AdminSale)
.然后包括(a=>a.PlaceCategory)
.Include(s=>s.Transaction)
.Where(s=>s.PayIn.Date new{s.AdminSaleId,s.PayIn.Date})
.Select(s=>newadvancein()
{
Count=s.Count(),
NetAmountSum=s.Select(t=>t.NetAmount).Sum(),
GrossAmountSum=s.Select(t=>t.Transaction.GrossAmountWithoutsAle折扣)。Sum(),
AdminSale=新的SaleTransferReadModel{
Id=s.Select(t=>t.AdminSale).First().Id,
PlaceName=s.Select(t=>t.AdminSale).First().PlaceName,
PlacePhoto=s.Select(t=>t.AdminSale.First().PlacePhoto
},
PayIn=s.Select(t=>t.PayIn).First(),
SaleTransactionId=String.Join(“,”,s.Select(x=>x.Id.ToString())
})
.OrderBy(s=>s.PayIn);
var list=wait query.AsNoTracking().toListSync();
data.Totalizer=新的TotalizerAdvanceInDate{
Count=list.Sum(t=>t.Count),
NetAmountSum=list.Sum(a=>a.NetAmountSum),
GrossAmountSum=list.Sum(a=>a.GrossAmountSum)
};
data.Page=Page;
data.TotalItems=list.Count();
data.TotalPages=data.TotalItems/limit.Value;
data.data=query.Skip(limit.Value*页)
.Take(limit.Value)
.ToList();
data.PageTotalizer=新的TotalizerAdvanceInDate{
Count=data.data.Sum(a=>a.Count),
NetAmountSum=data.data.Sum(a=>a.NetAmountSum),
GrossAmountSum=data.data.Sum(a=>a.GrossAmountSum)
};
返回数据;
}
我希望能够缩短获得此查询响应的时间。一些建议:
.ToList()
调用是一个主要问题。这是将整个集合具体化为内存,这意味着将所有数据从DbServer拉到web服务器。据我所知,这只是为了数一数query = Context.SaleTransaction
.Where(s => s.PayIn.Date <= DateTimeOffset.UtcNow.Date.AddDays(6)
&& s.Paid == false
&& s.ProcessingPaid == false
&& (s.Transaction.Status == TransactionStatus.Authorized || s.Transaction.Status == TransactionStatus.Paid) )
.GroupBy(s => new { s.AdminSaleId, s.PayIn.Date });
var rowCount = query.Count();
这将运行第二个查询,仅从数据库中提取所选的数据页。我会考虑在这里使用async,以允许您的web服务器在该查询运行时处理请求,因为这可能需要一秒钟左右的时间,具体取决于所涉及的数据
此处的关键更改/注意事项:不要重复第一次调用来填充内部视图模型。根据第一个所需的子记录,使用选择对其进行投影。首先使用时
等。您应该始终包含一个OrderBy
,因为您不能依赖任何默认顺序
接下来,代码将嵌入一个实体PayIn。从PayIn获取要返回的字段,或将其投影到ViewModel中。您不希望在视图模型中嵌入任何实体,因为当序列化程序将其转换为JSON或其他格式时,它将触及PayIn上的任何属性,并可能导致延迟加载
最后是ID列表上的string.Join()
。这可能行得通,但可能行不通,而且EF core有一个坏习惯,即抛出一个警告,警告它将过早地将一个查询具体化为两步投影,从而破坏了整个目的。相反,我会在视图模型中选择ID的列表,然后在视图模型上公开一个额外的属性,该属性执行string.Join()
public ICollection SalesTransactionId{get;set;}//将其填充到Linq表达式中。。。
公共字符串DisplaySalesTransactionId
{
获取{return string.Join(“,”,salesTransactionId);}
}
**注意:如果这让您对类型感到不安,您可以使用saletransactionids.Select(x=>x.ToString())
在Join
中
public ICollection<Guid> SalesTransactionIds { get; set; } // Populate this in your Linq expression...
public string DisplaySalesTransactionIds
{
get { return string.Join(",", SalesTransactionIds); }
}
当序列化程序对此进行爬网时,视图/网格应该在视图的模型中获得一个名为DisplaySalesTransactionId的string属性,该属性将为您提供逗号分隔的列表
试试其中的一些,看看它是否能加快速度。最后,这是一个SQL查询。在调试器中运行它,在调用toListSync
时使用断点。查看query
及其在调试器中生成的SQL。对SQL执行健全性检查。将其复制到SSMS并查看运行是否需要很长时间。然后做一件正常的事情来尝试优化查询(查看查询计划,可能添加索引等)。为什么要运行两次查询—一次创建列表
,一次设置数据。数据
—为什么不在那里使用列表
?
public ICollection<Guid> SalesTransactionIds { get; set; } // Populate this in your Linq expression...
public string DisplaySalesTransactionIds
{
get { return string.Join(",", SalesTransactionIds); }
}