我有以下asp.net核心LINQ代码:
List<UserSearchResult> results = await db.ApplicationUsers.Where(u => u.Name.StartsWith(name) && !u.Deleted && u.AppearInSearch)
.OrderByDescending(u => u.Verified)
.ThenBy(u => u.DateAdded) // Added to prevent duplication of results in different pages
.Skip(page * recordsInPage)
.Take(recordsInPage)
.Select(u => new UserSearchResult()
{
Name = u.Name,
Verified = u.Verified,
PhotoURL = u.PhotoURL,
UserID = u.Id,
Subdomain = u.Subdomain
}).ToListAsync();
不幸的是,这转化为以下内容:
SELECT [t].[Name], [t].[Verified], [t].[PhotoURL], [t].[Id], [t].[Subdomain] FROM ( SELECT [u0].* FROM [AspNetUsers] AS [u0] WHERE ((([u0].[Name] LIKE @__name_0 + N'%' AND (CHARINDEX(@__name_0, [u0].[Name]) = 1)) OR (@__name_0 = N'')) AND ([u0].[Deleted] = 0)) AND ([u0].[AppearInSearch] = 1) ORDER BY [u0].[Verified] DESC, [u0].[DateAdded] OFFSET @__p_1 ROWS FETCH NEXT @__p_2 ROWS ONLY ) AS [t]
我想知道为什么会有这个部分:
(CHARINDEX(@__name_0, [u0].[Name]) = 1)) OR (@__name_0 = N''))
而不仅仅是LIKE
非常感谢
答案 0 :(得分:5)
EF Core中的SQL翻译规则仍然不明确,远非完美,正在讨论中,并且随着每一个小版本的发布而变化。
StartsWith
,EndsWith
和Contains
的翻译已经多次讨论和更改 - 例如,问题#474: Query: Improve translation of String's StartsWith, EndsWith and Contains)。 StartsWith
的翻译在最新的官方v1.1.2版本中甚至已经改变,因此v1.1.1翻译
(CHARINDEX(@__name_0, [u0].[Name]) = 1)) OR (@__name_0 = N''))
现在将是
[u0].[Name] LIKE @__name_0 + '%' AND (CHARINDEX(@__name_0, [u0].[Name]) = 1)) OR (@__name_0 = N''))
这个想法是LIKE
条件允许查询优化器使用索引,然后像第二个条件那样进行慢速过滤(它完全正确处理(类似于C#))搜索字符串中的通配符以及空搜索字符串。)
因此,您可以尝试升级并查看是否有帮助。即将发布的v2将为数据库特定的运营商提供更自然的支持,例如LIKE
等。
目前的另一个解决方法(如果上面确实是性能瓶颈)是使用SQL直接构建查询过滤部分,其余部分使用LINQ(与EF6相比,EF Core允许):
var results = await db.ApplicationUsers
//.Where(u => u.Name.StartsWith(name) && !u.Deleted && u.AppearInSearch)
.FromSql("select * from ApplicationUsers where Name like {0}", name + "%")
.Where(!u.Deleted && u.AppearInSearch)
.OrderByDescending(u => u.Verified)
.ThenBy(u => u.DateAdded) // Added to prevent duplication of results in different pages
.Skip(page * recordsInPage)
.Take(recordsInPage)
.Select(u => new UserSearchResult()
{
Name = u.Name,
Verified = u.Verified,
PhotoURL = u.PhotoURL,
UserID = u.Id,
Subdomain = u.Subdomain
}).ToListAsync();
请注意FromSql
方法支持参数,因此不应该关注SQL注入。您还需要知道表名,列名和具体数据库SQL语法 - 这些内容应该由ORM为您提取。
答案 1 :(得分:3)
实体框架提供特殊功能EF.Functions.Like以将其用于具有标准SQL LIKE语法的LINQ表达式。对于StartWith模式,表达式将如下所示:
var likeExpression = name+"%";
... await db.ApplicationUsers.Where(u => EF.Functions.Like(u.Name,likeExpression)...