我有生成复杂 SQL 查询的控制器端点。这是我的主要应用程序端点,在启动期间它会加载大约 500 个项目。作为示例生成的 sql 查询如下,我只放了一部分,因为这是一个很长的查询
SELECT [t].[Bk_ID], [t].[Md_AudioEn_ID], [t].[Md_AudioAr_ID], [m].[Md_URL], [m0].[Md_URL], [t].[Bk_Code], [t].[Bk_Name], [t].[Bk_Name_Ar], [t].[Bk_Title], [t].[Bk_Title_Ar], [t].[Bk_Introduction], [t].[Bk_Introduction_Ar], [t].[Bk_Summary], [t].[Bk_Summary_Ar], [t].[Bk_Characters], [t].[Bk_Characters_Ar], [t].[Bk_Desc], [t].[Bk_Desc_Ar], [t].[Bk_Language], CASE
WHEN LOWER([t].[Bk_Language]) = N'arabic' THEN N'عربي'
WHEN LOWER([t].[Bk_Language]) = N'english' THEN N'إنجليزي'
ELSE N'الكل'
END, [t].[Bk_Trial], [t].[Bk_Active], [t].[Bk_CreatedOn], [a].[Us_DisplayName], [t].[Bk_ModifyOn], [a0].[Us_DisplayName], [m].[Md_ID], [m0].[Md_ID], [a].[Id], [a0].[Id], [t1].[Ct_ID], [t1].[Ct_Name], [t1].[Ct_Name_Ar], [t1].[Ct_Title], [t1].[Ct_Title_Ar], [t1].[Ct_Desc], [t1].[Ct_Desc_Ar], [t1].[c], [t1].[Ct_Active], [t1].[Ct_CreatedOn], [t1].[Us_DisplayName], [t1].[Ct_ModifyOn], [t1].[Us_DisplayName0], [t1].[c0], [t1].[c1], [t1].[c2], [t1].[c3], [t1].[Bk_ID], [t1].[Ct_ID0], [t1].[Id], [t1].[Id0], [t1].[Ct_ID1], [t1].[Tg_ID], [t1].[Tg_Title], [t1].[Tg_Title_Ar], [t1].[Tg_Desc], [t1].[Tg_Desc_Ar], [t1].[Tg_Total], [t1].[Tg_Summaries], [t1].[Tg_Categories], [t1].[Tg_Active], [t1].[Tg_CreatedOn], [t1].[Tg_Creator], [t1].[Tg_ModifyOn], [t1].[Tg_Modifier], [t1].[Ct_ID2], [t1].[Tg_ID0], [t1].[Id1], [t1].[Id00], [t3].[Gn_ID], [t3].[Gn_Title], [t3].[Gn_Title_Ar], [t3].[Gn_Desc], [t3].[Gn_Desc_Ar], [t3].[Gn_Total], [t3].[Gn_Summaries], [t3].[Gn_Authors], [t3].[Gn_Publishers], [t3].[Gn_Active], [t3].[Gn_CreatedOn], [t3].[Gn_Creator], [t3].[Gn_ModifyOn], [t3].[Gn_Modifier], [t3].[Bk_ID], [t3].[Gn_ID0], [t3].[Id], [t3].[Id0], [t4].[Pb_ID], [t4].[Md_ID], [t4].[Cn_ID], [t4].[Pb_Name], [t4].[Pb_Name_Ar], [t4].[Pb_Title], [t4].[Pb_Title_Ar], [t4].[Pb_Desc], [t4].[Pb_Desc_Ar], [t4].[Pb_Active], [t4].[Bk_ID], [t4].[Pb_ID0], [t5].[Th_ID], [t5].[Th_Title], [t5].[Th_Title_Ar], [t5].[Th_Desc], [t5].[Th_Desc_Ar], [t5].[Th_Summaries], [t5].[Th_Active], [t5].[Th_CreatedOn], [t5].[Th_Creator], [t5].[Th_ModifyOn], [t5].[Th_Modifier], [t5].[Bk_ID], [t5].[Th_ID0], [t5].[Id], [t5].[Id0], [t7].[At_ID], [t7].[Md_ID], [t7].[At_Name], [t7].[At_Name_Ar], [t7].[At_Title], [t7].[At_Title_Ar], [t7].[At_Desc], [t7].[At_Desc_Ar], [t7].[At_Active], [t7].[Bk_ID], [t7].[At_ID0], [t8].[Tg_ID], [t8].[Tg_Title], [t8].[Tg_Title_Ar], [t8].[Tg_Desc], [t8].[Tg_Desc_Ar], [t8].[Tg_Total], [t8].[Tg_Summaries], [t8].[Tg_Categories], [t8].[Tg_Active], [t8].[Tg_CreatedOn], [t8].[Tg_Creator], [t8].[Tg_ModifyOn], [t8].[Tg_Modifier], [t8].[Bk_ID], [t8].[Tg_ID0], [t8].[Id], [t8].[Id0], [t10].[Md_ID], [t10].[Md_FileType], [t10].[Md_Extension], [t10].[Md_Title], [t10].[Md_Title_Ar], [t10].[Md_URL]
.........
这部分代码负责生成上述 sql 查询。
public async Task<Result<PagedList<BookDto>>> Handle(Query req, CancellationToken cancellationToken)
{
var user = await _ctx.Users.FirstOrDefaultAsync(s =>
s.UserName == _userAccessor.GetUsername() && !s.Us_Deleted, cancellationToken: cancellationToken);
bool Subscribed = (user != null && user.Us_SubscriptionExpiryDate > DateTime.Now);
string property = string.Empty;
if (Subscribed) property = "Bk_Trial";
var qry = _ctx.Books
.ProjectTo<BookDto>(_mpr.ConfigurationProvider, new { currentUsername = _userAccessor.GetUsername() })
.AsQueryable();
#region Filter
char[] Chars = new char[] { ' ', '.', '?', '!', ' ', ';', ':', ',' };
string Search = req.Params.Search;
if (!String.IsNullOrEmpty(Search)) {
Search = Search.ToLower();
List<string> SearchWordList = Search.Split(" ").ToList();
qry = qry
.Where(s => s.Bk_Name.ToLower().Contains(Search)
|| s.Bk_Name_Ar.ToLower().Contains(Search)
|| s.Bk_Title.ToLower().Contains(Search)
|| s.Bk_Title_Ar.ToLower().Contains(Search)
|| s.Bk_Desc.ToLower().Contains(Search)
|| s.Bk_Desc_Ar.ToLower().Contains(Search)
}
string Language = req.Params.Language;
if (!String.IsNullOrEmpty(Language))
{
Language = Language.ToLower();
qry = qry
.Where(s => s.Bk_Language.ToLower().Contains(Language));
}
if (!String.IsNullOrEmpty(req.Params.Status)) {
switch (req.Params.Status.ToLower())
{
case "active":
qry = qry.Where(s => s.Bk_Active == true);
break;
case "disable":
qry = qry.Where(s => s.Bk_Active == false);
break;
}
}
#endregion
#region Order
if (!String.IsNullOrEmpty(req.Params.OrderBy))
{
switch (req.Params.OrderBy.ToLower())
{
case "name":
if (req.Params.OrderAs.ToLower() == "desc") qry = qry.OrderByDescending(s => s.Bk_Name);
else qry = qry.OrderBy(s => s.Bk_Name);
break;
case "name_ar":
if (req.Params.OrderAs.ToLower() == "desc") qry = qry.OrderByDescending(s => s.Bk_Name_Ar);
else qry = qry.OrderBy(s => s.Bk_Name_Ar);
break;
case "title":
if (req.Params.OrderAs.ToLower() == "desc") qry = qry.OrderByDescending(s => s.Bk_Title);
else qry = qry.OrderBy(s => s.Bk_Title);
break;
case "title_ar":
if (req.Params.OrderAs.ToLower() == "desc") qry = qry.OrderByDescending(s => s.Bk_Title_Ar);
else qry = qry.OrderBy(s => s.Bk_Title_Ar);
break;
case "trial":
if (req.Params.OrderAs.ToLower() == "desc") qry = qry.OrderByDescending(s => s.Bk_Trial);
else qry = qry.OrderBy(s => s.Bk_Trial);
break;
case "language":
if (req.Params.OrderAs.ToLower() == "desc") qry = qry.OrderByDescending(s => s.Bk_Language);
else qry = qry.OrderBy(s => s.Bk_Language);
break;
default:
qry = qry.OrderBy(s => s.Bk_Title);
break;
}
}
我的问题是,编译查询是否有助于动态更改参数的性能提高? Microsoft 建议,对于动态更改的参数,编译查询是不可能的(here)。在我的移动应用程序中,第一个请求花费了太多时间。并且参数是固定的。实际的终点就像
Cook/getCooks?search=&status=active&pageSize=1000&orderBy=Title&orderAs=DESC
。
编译查询是一种微优化。你需要找到更大的问题。最大的问题是您过早地预测到 DTO。这个
var qry = _ctx.Books
.ProjectTo<BookDto>(_mpr.ConfigurationProvider, new { currentUsername = _userAccessor.GetUsername() })
.AsQueryable();
将确保所有 Books 都被获取到客户端,转换为 BookDtos,并且所有额外的查询逻辑都将在客户端上发生。
而是将所有查询逻辑添加到 _ctx.Books 中,并将结果投影到 BookDto 作为最后一步。