首页 文章

LINQ to Entities无法识别方法'System.String ToString()'方法,并且此方法无法转换为存储表达式

提问于
浏览
123

我正在将一些东西从一个mysql服务器迁移到一个sql server但我无法弄清楚如何使这个代码工作:

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()'方法,并且此方法无法转换为存储表达式 .

谁知道为什么会这样?

11 回答

  • 131

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

    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 帮助程序类 . 在许多情况下,它可以消除对临时变量的需求 .

  • 0

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

    但是,Microsoft提供了SqlFunctions class,它是可以在这种情况下使用的方法集合 .

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

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

    当具有临时变量的解决方案因任何原因而不可取时是好的 .

    与SqlFunction类似,您还有EntityFunctions(EF6已被DbFunctions淘汰),它提供了一组不同的函数,这些函数也是数据源不可知的(不限于例如SQL) .

  • 10

    问题是您在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;
    
  • -7

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

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

    希望这可以帮助 .

  • 3

    像这样改变它应该工作:

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

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

  • 0

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

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

    但是在调用 AsEnumerableToList 方法时要小心,因为在此方法之前您将请求来自所有实体的所有数据 . 在上面的例子中,我通过一个请求读取了所有 table_name 行 .

  • 2

    升级到 Entity Framework Version 6.2.0 为我工作 .

    我以前的版本是6.0.0 .

    希望这可以帮助,

  • 1

    在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);
        }         
    }
    
  • 64

    如果你真的想在查询中输入 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);
    
            }
        }
    }
    
  • 23

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

    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)
    

    Remove the problematic line and problem solved.

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

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

  • 6

    只需在LINQ查询中使用方法调用时,只需将LINQ to Entity查询转换为LINQ to Objects查询(例如,调用ToArray) .

相关问题