【问题标题】:EF Core 3 - Using an extension method of string inside Where clauseEF Core 3 - 在 Where 子句中使用字符串的扩展方法
【发布时间】:2020-11-24 09:30:55
【问题描述】:

我有一个简单的字符串扩展方法:

public static class FrenchStringExtensions
  {
    public static string ReplaceAccents(this string str)
    {
      return str
        .Replace("ç", "c")
        .Replace("é", "e")
        .Replace("ê", "e")
        .Replace("è", "e")
        .Replace("ë", "e")
        .Replace("â", "a")
        .Replace("à", "a")
        .Replace("î", "i")
        .Replace("ï", "i")
        .Replace("ô", "o")
        .Replace("û", "u")
        .Replace("ù", "u")
        .Replace("ü", "u");
    }
  }

当我尝试像这样在Where() 子句中调用此方法时:

var supportTeamsQuery = this.MasterContext.IncidentSupportTeams
        .AsNoTracking()
        .IsActive();

      if (!string.IsNullOrEmpty(pattern))
      {
        pattern = pattern.ToLower().ReplaceAccents().Trim();

        supportTeamsQuery = supportTeamsQuery
          .Where(st =>
            st.Name.ToLower().ReplaceAccents().Contains(pattern)
          );
      }

我有一个错误:无法翻译 LINQ 表达式...

如果我在Where() 内使用Replace() 调用,它工作正常。例如:

supportTeamsQuery = supportTeamsQuery
          .Where(st =>
            st.Name
            .ToLower()
            .Replace("ç", "c")
            .Replace("é", "e")
            .Replace("ê", "e")
            ...
            .Contains(pattern)
          );

但我的代码中有几个地方需要以这种方式转换字符串,因此我想将其移至单独的方法。

有可能让它工作吗?

【问题讨论】:

  • IMO,与其这样查询数据(查询将无效),不如存储规范化名称。也就是说,您应该在 在数据库中存储名称之前调用 ReplaceAccents。只需存储“带重音的名称”和“标准化名称”。第一个用于向用户显示数据,第二个用于搜索。
  • 发生此错误是因为您的 LINQ 将被转换为 SqlServer Query 并且它不能(没有内置版本ReplaceAccent 在 SQL Server 中)。如果你的数据在内存中,你的函数就可以正常工作。
  • 我认为您可以反之亦然,将您的pattern 字符串更改为重音类型。这样你的查询就可以工作了。
  • 您正在尝试做的似乎是重新发明排序规则重音敏感性。只需将数据库的排序规则更改为不区分重音 (AI),或者如果您只需要某个查询,则需要使用 EF Core 5,请参阅docs.microsoft.com/en-us/ef/core/miscellaneous/…
  • 您还可以通过使用不区分大小写 (CI) 排序规则而不是区分大小写 (CS) 来摆脱 ToLower

标签: c# entity-framework-core


【解决方案1】:

不同之处在于,当您在Where 子句中内联方法时,编译器会生成带有多个替换调用的表达式树。当您调用ReplaceAccents 时,编译器仅生成此调用,EF 无法访问该方法的主体。所以你需要一种扩展表达式树的方法。

有很多解决方案可以做到这一点。但是试试这个扩展,它是为此而设计的。 https://github.com/axelheer/nein-linq/

根据文档,您必须进行以下代码更改:

public static class FrenchStringExtensions
{
    [InjectLambda]
    public static string ReplaceAccents(this string str)
    {
       _replaceAccentsFunc ??= ReplaceAccents().Compile();
       return _replaceAccentsFunc(str);
    }

    Func<string, string> _replaceAccentsFunc;

    private static Expression<Func<string, string>> ReplaceAccents()
    {
       return str =>
         .Replace("ç", "c")
         .Replace("é", "e")
         .Replace("ê", "e")
         .Replace("è", "e")
         .Replace("ë", "e")
         .Replace("â", "a")
         .Replace("à", "a")
         .Replace("î", "i")
         .Replace("ï", "i")
         .Replace("ô", "o")
         .Replace("û", "u")
         .Replace("ù", "u")
         .Replace("ü", "u");
    }
}

那么你就可以在ToInjectable()调用之后使用你的函数了

var supportTeamsQuery = this.MasterContext.IncidentSupportTeams
        .ToInjectable()
        .AsNoTracking()
        .IsActive();

      if (!string.IsNullOrEmpty(pattern))
      {
        pattern = pattern.ToLower().ReplaceAccents().Trim();

        supportTeamsQuery = supportTeamsQuery
          .Where(st =>
            st.Name.ToLower().ReplaceAccents().Contains(pattern)
          );
      }

【讨论】:

  • 谢谢!这正是我想要的!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-13
  • 1970-01-01
  • 2021-04-29
  • 2022-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多