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

Tec*_*chy 4 sql linq entity-framework-core asp.net-core

我有以下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();
Run Code Online (Sandbox Code Playgroud)

不幸的是,这转化为以下内容:

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]
Run Code Online (Sandbox Code Playgroud)

我想知道为什么它有这个部分:

(CHARINDEX(@__name_0, [u0].[Name]) = 1)) OR (@__name_0 = N''))
Run Code Online (Sandbox Code Playgroud)

而且不仅仅是喜欢

非常感谢

R.T*_*tov 9

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

var likeExpression = name+"%";
... await db.ApplicationUsers.Where(u => EF.Functions.Like(u.Name,likeExpression)...
Run Code Online (Sandbox Code Playgroud)


Iva*_*oev 5

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

几次的翻译StartsWith,EndsWith并且Contains已经过多次讨论和更改 - 例如,问题#474:查询:改进String的StartsWith,EndsWith和Contains的翻译.的翻译StartsWith已经在最新的官方版本V1.1.2甚至被改变,所以V1.1.1翻译

(CHARINDEX(@__name_0, [u0].[Name]) = 1)) OR (@__name_0 = N''))
Run Code Online (Sandbox Code Playgroud)

现在将是这样的

[u0].[Name] LIKE @__name_0 + '%' AND (CHARINDEX(@__name_0, [u0].[Name]) = 1)) OR (@__name_0 = N''))
Run Code Online (Sandbox Code Playgroud)

这个想法是LIKE条件允许查询优化器使用索引,然后像第二个条件那样进行慢速过滤(这完全是关于处理字符串中的通配符以及空搜索字符串的正确处理(类似于C#)) ).

所以你可以尝试升级,看看它是否有帮助.即将发布的v2将为db等特定运营商提供更多自然支持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();
Run Code Online (Sandbox Code Playgroud)

请注意,该FromSql方法支持参数,因此SQL注入不应该是一个问题.您仍然需要知道表名,列名和具体的数据库SQL语法 - 这应该由ORM为您提取.

  • FromSql 违背了 EntityFramework 作为数据库抽象的全部目的。 (2认同)