【问题标题】:StartsWith() doesn't translate to Like('abc%') in LINQStartsWith() 不会在 LINQ 中转换为 Like('abc%')
【发布时间】:2017-11-05 09:30:24
【问题描述】:

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

不仅喜欢

非常感谢

【问题讨论】:

  • 你应该用entity-framework-core标记它。 #474 - Query: Improve translation of String's StartsWith, EndsWith and Contains 已对此进行了跟踪。已添加对本机 SQL LIke 的 AFAIK 支持,并将在 v2 中提供
  • @IvanStoev 我已经修改了标签。我尝试升级到 2.0,但我无法单独为实体框架执行此操作,必须对整个 project.json 执行此操作,但出现了问题。你能建议一个替代方案吗?非常感谢。
  • v2 无论如何都没有正式发布。在那之前,不幸的是,我能想到的唯一解决方法(如果它对您很重要)是使用db.ApplicationUsers.FromSql(...).OrderByDescending(...)....,即在 SQL 级别应用WhereLIKE,然后在 LINQ 中完成其余的工作:(
  • @IvanStoev 非常感谢您的支持!但我担心这会将我的应用程序暴露给 SQL 注入,因为它使用原始 sql,你不这么认为吗?谢谢。
  • FromSql 允许您使用参数,因此无需注入。我不喜欢它的原因是它破坏了 ORM 封装和数据库独立性,因此 :( 在我之前评论的末尾:) 同样,仅在关键时使用它

标签: sql linq asp.net-core entity-framework-core


【解决方案1】:

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

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

【讨论】:

  • 我同意 R.Titov 的观点,我想提一下,在 OnModelCreating() 方法的 DbContext 类中为 u.Name 添加索引也是合适的,例如:modelBuilder.Entity&lt;ApplicationUsers&gt;().HasIndex(o =&gt; o.Name);
【解决方案2】:

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 等 db 特定运算符提供更自然的支持。

目前的另一种解决方法(如果上述确实是性能瓶颈)是直接使用 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 应该为你抽象出来的东西。

【讨论】:

  • FromSql 违背了 EntityFramework 作为数据库抽象的全部目的。
  • @thomasgalliker 确实如此,但 EF 也有其局限性/错误。您的应用程序的用户不关心抽象,他们想要性能 :) 顺便说一句,这篇文章已经过时了,现在 EF Core 有 EF.Like 方法,所以这在撰写本文时是可行的。您的用户也不能等待 MS 修复他们的错误/限制 :)
  • 我完全同意用户不关心后端内部的观点。如果您可以放弃 ORM 原则,我宁愿坚持使用纯 SQL。如果性能为王,请不要打扰 EFCore。这是一个架构决策。我可以忍受 EFCore 的性能影响,但我不想失去良好的可维护性优势。我的经验是,当您开始在 EF 中使用 SQL 时,简单的重命名可能会破坏您的代码,从而导致错误和不愉快的用户。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多