在LINQ中,StartsWith()不会转换为Like('abc%')

问题描述 投票:3回答:2

我有以下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''))

而且不仅仅是喜欢

非常感谢

sql linq asp.net-core entity-framework-core
2个回答
5
投票

EF Core中的SQL翻译规则仍然不清楚,远非完美,正在讨论中,并且随着每一个甚至是次要的版本都在变化。

StartsWithEndsWithContains的翻译已经多次讨论和更改 - 例如,发行#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为您提取。


3
投票

实体框架提供特殊功能EF.Functions.Like以将其用于具有标准SQL LIKE语法的LINQ表达式。对于StartWith模式,表达式将如下所示:

var likeExpression = name+"%";
... await db.ApplicationUsers.Where(u => EF.Functions.Like(u.Name,likeExpression)...
© www.soinside.com 2019 - 2024. All rights reserved.