我需要使用EF编译查询来进行性能优化

问题描述 投票:0回答:1

我有生成复杂 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

sql-server entity-framework sql-server-2022
1个回答
0
投票

编译查询是一种微优化。你需要找到更大的问题。最大的问题是您过早地预测到 DTO。这个

                var qry = _ctx.Books
                    .ProjectTo<BookDto>(_mpr.ConfigurationProvider, new { currentUsername = _userAccessor.GetUsername() })
                    .AsQueryable();

将确保所有 Books 都被获取到客户端,转换为 BookDtos,并且所有额外的查询逻辑都将在客户端上发生。

而是将所有查询逻辑添加到 _ctx.Books 中,并将结果投影到 BookDto 作为最后一步。

© www.soinside.com 2019 - 2024. All rights reserved.