在LINQ

时间:2017-06-04 13:57:58

标签: sql linq asp.net-core entity-framework-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();

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

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

非常感谢

2 个答案:

答案 0 :(得分: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为您提取。

答案 1 :(得分:3)

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

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