【问题标题】:LINQ to Entities does not recognize the method 'System.String ToString()' method, and this method cannot be translated into a store expressionLINQ to Entities 无法识别方法 'System.String ToString()' 方法,并且此方法无法转换为存储表达式
【发布时间】:2011-08-19 11:19:51
【问题描述】:

我正在将一些东西从一个 mysql 服务器迁移到一个 sql 服务器,但我不知道如何使这段代码工作:

using (var context = new Context())
{
    ...

    foreach (var item in collection)
    {
        IQueryable<entity> pages = from p in context.pages
                                   where  p.Serial == item.Key.ToString()
                                   select p;
        foreach (var page in pages)
        {
            DataManager.AddPageToDocument(page, item.Value);
        }
    }

    Console.WriteLine("Done!");
    Console.Read();
}

当它进入第二个foreach (var page in pages) 时,它会抛出一个异常:

LINQ to Entities 无法识别方法 'System.String ToString()' 方法,并且这个方法不能翻译成store 表达。

有人知道为什么会这样吗?

【问题讨论】:

标签: c# mysql sql linq


【解决方案1】:

像这样改变它,它应该可以工作:

var key = item.Key.ToString();
IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == key
                           select p;

在声明 LINQ 查询的行但在 foreach 的行中没有抛出异常的原因是延迟执行功能,即在您尝试访问结果之前不会执行 LINQ 查询。这发生在foreach 而不是更早。

【讨论】:

    【解决方案2】:

    问题是您在 LINQ to Entities 查询中调用 ToString。这意味着解析器正在尝试将 ToString 调用转换为其等效的 SQL(这是不可能的......因此出现了异常)。

    您所要做的就是将 ToString 调用移到单独的行:

    var keyString = item.Key.ToString();
    
    var pages = from p in context.entities
                where p.Serial == keyString
                select p;
    

    【讨论】:

      【解决方案3】:

      只需将字符串保存到临时变量中,然后在您的表达式中使用它:

      var strItem = item.Key.ToString();
      
      IQueryable<entity> pages = from p in context.pages
                                 where  p.Serial == strItem
                                 select p;
      

      问题出现是因为ToString() 并没有真正执行,而是变成了MethodGroup,然后被解析并翻译成SQL。由于没有 ToString() 等效项,因此表达式失败。

      注意:

      请确保您还查看了 Alex's answer 关于稍后添加的 SqlFunctions 辅助类的信息。在许多情况下,它可以消除对临时变量的需要。

      【讨论】:

      • 如果我的 ToString() 被应用于相等的左侧怎么办?例如p.Serial.ToString() = item.
      • @dotNet 这仍然会失败,因为整个事情都变成了一个表达式,实体框架试图把它变成有效的 SQL。它知道如何处理一些方法,但ToString() 不是其中之一。
      • @Josh:我知道它会失败。我要的是那种场景的解决方案,因为上面的解决方案显然不能在那里应用。
      • @Josh:我正在为这样一种情况而苦苦挣扎。假设我的 OrderNumber 列是 int,但我的用户希望能够在输入时过滤 OrderNumbers 列表。如果他在搜索框中输入了 143,他只想要那些具有 OrderNumber LIKE '%143%' 的记录.我不需要在 OrderNumber 列上执行 ToString() 来实现它吗?
      • @dotNET 这是 ORM 倒下的场景之一。我认为在这些情况下可以通过ExecuteQuery 直接使用SQL 或通过ObjectQuery&lt;T&gt; 使用Entity SQL
      【解决方案4】:

      只要您需要在 LINQ 查询中使用方法调用,只需将 LINQ to Entity 查询转换为 LINQ to Objects 查询(例如调用 ToArray)即可。

      【讨论】:

      • “任何时候你需要使用方法调用”是一个糟糕的建议——对于很多记录,这可能是一个大问题。对于这种情况,公认的答案要好得多。
      【解决方案5】:

      遇到了类似的问题。 通过在实体集合上调用 ToList() 并查询列表来解决它。 如果集合很小,这是一个选项。

      IQueryable<entity> pages = context.pages.ToList().Where(p=>p.serial == item.Key.ToString())
      

      希望这会有所帮助。

      【讨论】:

      • 请注意,这将从数据库中检索 所有 页面实体,并在客户端而不是数据库中进行过滤.. 通常不是一件好事。
      • 确实,这种方法对于包含多条记录的任何表来说都是低效的,这意味着所有表都存在:-)。但是,这个答案今天确实对我有帮助,因为我正在做一个包含 toString() 的 .Select 投影,所以事先调用 .ToList() 对我没有性能损失,调用 .ToList() 允许我使用 .ToString()格式和我的 .Select 语句...
      【解决方案6】:

      正如其他人所回答的那样,这会中断,因为 .ToString 在进入数据库的过程中无法转换为相关的 SQL。

      但是,Microsoft 提供了SqlFunctions class,它是可在此类情况下使用的方法的集合。

      对于这种情况,您在这里寻找的是SqlFunctions.StringConvert

      from p in context.pages
      where  p.Serial == SqlFunctions.StringConvert((double)item.Key.Id)
      select p;
      

      当使用临时变量的解决方案由于某种原因不受欢迎时很好。

      与 SqlFunctions 类似,您还拥有 EntityFunctions(EF6 被 DbFunctions 淘汰),它提供了一组不同的函数,这些函数也与数据源无关(不限于例如 SQL)。

      【讨论】:

      • 他们在 .NET 4 中添加了 SqlFunctions 类,而我只是在学习它?很棒的发现。
      【解决方案7】:

      在 MVC 中,假设您正在根据您的要求或信息搜索记录。 它工作正常。

      [HttpPost]
      [ActionName("Index")]
      public ActionResult SearchRecord(FormCollection formcollection)
      {       
          EmployeeContext employeeContext = new EmployeeContext();
      
          string searchby=formcollection["SearchBy"];
          string value=formcollection["Value"];
      
          if (formcollection["SearchBy"] == "Gender")
          {
              List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Gender == value).ToList();
              return View("Index", emplist);
          }
          else
          {
              List<MvcApplication1.Models.Employee> emplist = employeeContext.Employees.Where(x => x.Name == value).ToList();
              return View("Index", emplist);
          }         
      }
      

      【讨论】:

      • 为了更好的实践,或者在生产类型的代码中,您应该始终将数据库事件放在服务层或数据层中,而不是直接在操作中。
      【解决方案8】:

      如果您真的想在查询中输入ToString,您可以编写一个表达式树访问器,用call to the appropriate StringConvert function 重写对ToString 的调用:

      using System.Linq;
      using System.Data.Entity.SqlServer;
      using System.Linq.Expressions;
      using static System.Linq.Expressions.Expression;
      using System;
      
      namespace ToStringRewriting {
          class ToStringRewriter : ExpressionVisitor {
              static MethodInfo stringConvertMethodInfo = typeof(SqlFunctions).GetMethods()
                       .Single(x => x.Name == "StringConvert" && x.GetParameters()[0].ParameterType == typeof(decimal?));
      
              protected override Expression VisitMethodCall(MethodCallExpression node) {
                  var method = node.Method;
                  if (method.Name=="ToString") {
                      if (node.Object.GetType() == typeof(string)) { return node.Object; }
                      node = Call(stringConvertMethodInfo, Convert(node.Object, typeof(decimal?));
                  }
                  return base.VisitMethodCall(node);
              }
          }
          class Person {
              string Name { get; set; }
              long SocialSecurityNumber { get; set; }
          }
          class Program {
              void Main() {
                  Expression<Func<Person, Boolean>> expr = x => x.ToString().Length > 1;
                  var rewriter = new ToStringRewriter();
                  var finalExpression = rewriter.Visit(expr);
                  var dcx = new MyDataContext();
                  var query = dcx.Persons.Where(finalExpression);
      
              }
          }
      }
      

      【讨论】:

      • 应该使用 FirstOrDefault 而不仅仅是 First...如果它是主键,那么使用 Find,因为这样会更好。
      • @TGarrett First 的唯一用法是GetMethods() 的结果,它返回MethodInfo[]。 AFAIK,MethodInfo[] 没有Find 方法,也没有这样的扩展方法。但是我真的应该使用Single,因为这个方法是通过反射找到的,如果不能解决合适的方法,就不会出现编译时错误。
      【解决方案9】:

      在这种情况下我遇到了同样的错误:

      var result = Db.SystemLog
      .Where(log =>
          eventTypeValues.Contains(log.EventType)
          && (
              search.Contains(log.Id.ToString())
              || log.Message.Contains(search)
              || log.PayLoad.Contains(search)
              || log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
          )
      )
      .OrderByDescending(log => log.Id)
      .Select(r => r);
      

      花了太多时间调试后,我发现逻辑表达式中出现了错误。

      第一行 search.Contains(log.Id.ToString()) 确实可以正常工作,但处理 DateTime 对象的最后一行让它惨遭失败:

      || log.Timestamp.ToString(CultureInfo.CurrentUICulture).Contains(search)
      

      删除有问题的行,问题就解决了。

      我不完全明白为什么,但似乎 ToString() 是字符串的 LINQ 表达式,而不是实体。 LINQ for Entities 处理 SQL 等数据库查询,而 SQL 没有 ToString() 的概念。因此,我们不能将 ToString() 放入 .Where() 子句中。

      但是第一行是如何工作的呢?而不是 ToString(),SQL 有 CASTCONVERT,所以到目前为止我最好的猜测是实体的 linq 在一些简单的情况下使用它。 DateTime 对象并不总是那么简单...

      【讨论】:

        【解决方案10】:

        将表转换为Enumerable,然后在内部使用ToString() 方法调用LINQ 方法:

            var example = contex.table_name.AsEnumerable()
        .Select(x => new {Date = x.date.ToString("M/d/yyyy")...)
        

        但是当你调用AsEnumerableToList 方法时要小心,因为你会在这个方法之前请求所有实体的所有数据。在我上面的例子中,我通过一个请求读取了所有 table_name 行。

        【讨论】:

        【解决方案11】:

        升级到 Entity Framework 版本 6.2.0 对我有用。

        我之前使用的是 6.0.0 版。

        希望这会有所帮助,

        【讨论】:

        • query.where(x => someGuidAsString.Contains(x.AGuid.ToString())) -> 不适用于 EF 6.0.0.0;但是,从 6.1.3 开始工作(已在 6.1.3 和 6.4.4 上测试)
        【解决方案12】:

        我的问题是我有一个 'text' 用于此列的数据类型(由于从 sqlite 迁移)。 解决方法:只需将数据类型改为'nvarchar()',重新生成表格即可。

        然后Linq接受字符串比较。

        【讨论】:

        • 这与ToString问题有什么关系?
        • 如果数据库字段具有“文本”格式,则不接受 ToString。如果将类型更改为 varchar 或 nvarchar,将接受 ToString 并转换成功。对我来说,这是解决方案
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-21
        相关资源
        最近更新 更多