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


126

我正在将一些东西从一台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()',该方法无法转换为商店表达式。

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


Answers:


134

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

var strItem = item.Key.ToString();

IQueryable<entity> pages = from p in context.pages
                           where  p.Serial == strItem
                           select p;

出现问题是因为ToString()未真正执行,而是将其转换为MethodGroup,然后将其解析并转换为SQL。由于没有ToString()等效项,因此表达式失败。

注意:

确保您还检查了有关以后添加的帮助程序类的Alex的答案SqlFunctions。在许多情况下,它可以消除对临时变量的需要。


14
如果将我的ToString()应用于等式的左侧怎么办?egpSerial.ToString()=项目。
dotNET 2013年

3
@dotNet这仍然会失败,因为整个过程都变成了一个表达式,实体框架试图将其变成有效的SQL。它知道一些方法,但ToString()不是其中一种。
2013年

7
@Josh:我知道它会失败。我要的是该方案的解决方案,因为上述解决方案显然无法在此处应用。
dotNET 2013年

3
@Josh:我正在为一个这样的场景而苦苦挣扎。假设我的OrderNumber列为int,但我的用户希望在输入时能够过滤OrderNumbers列表。如果他在搜索框中输入143,则他只希望具有OrderNumber LIKE'%143%'的记录。 。我不需要在OrderNumber列上执行ToString()吗?
dotNET 2013年

5
@dotNET这是ORM掉在脸上的那些场景之一。我认为在这种情况下,可以ExecuteQuery通过使用实体SQL或将实体SQL与ObjectQuery<T>
Josh 2013年

69

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

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

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

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

当出于任何原因都不希望使用临时变量的解决方案时,该方法很好。

与SqlFunction相似,您还具有EntityFunctionsEF6DbFunctions淘汰)提供了一组不同的功能,这些功能也与数据源无关(例如,不限于SQL)。


4
他们在.NET 4中添加了SqlFunctions类,而我只是在学习它?很好的发现。
James Skemp

24

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

9

遇到类似的问题。通过在实体集合上调用ToList()并查询列表来解决此问题。如果集合很小,则可以选择。

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

希望这可以帮助。


42
请注意,这将从数据库中检索所有 Page实体,并在客户端而不是db上进行筛选。通常不是一件好事。
lambinator

3
的确,此方法对于包含多个记录的任何表都是无效的,这意味着存在所有表:-)。但是,这个答案确实对我有帮助,因为我正在做一个包含toString()的.Select投影,因此事先调用.ToList()对我来说没有性能损失,而调用.ToList()使我可以使用.ToString()格式和我的.Select语句...
Nathan Prather 2013年

6

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

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

声明不在LINQ查询行中而是在行中抛出异常的原因foreach是延迟执行功能,即直到尝试访问结果后才执行LINQ查询。而且这种情况发生在foreach而不是更早。


6

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

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

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


5
通常,这不是一个好选择。.AsEnumerable()将所有数据存储在内存中,您可以在此处查看更多信息:stackoverflow.com/questions/3311244/…–
kavain

5

升级到Entity Framework 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);
    }         
}

2
为了更好的实践或在代码的生产类型中,应该始终将数据库事件放在服务层或数据层中,而不是直接放在操作中。
TGarrett '16

0

如果您确实想ToString在查询中键入内容,则可以编写一个表达式树访问者,ToString对适当StringConvert函数调用重写对的调用:

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 '16

@TGarrett这里的唯一用法FirstGetMethods()返回结果MethodInfo[]。AFAIK,MethodInfo[]没有Find方法,也没有这样的扩展方法。但是我确实应该使用,Single因为这种方法是通过反射找到的,并且如果无法解决适当的方法,则不会出现编译时错误。
Zev Spitz

0

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

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表达式,而不是Entities。LINQ for Entities处理诸如SQL之类的数据库查询,而SQL没有ToString()的概念。因此,我们不能将ToString()放入.Where()子句中。

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


-8

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


3
“任何时候都需要使用方法调用”都是不好的建议-记录很多,这可能是个大问题。对于这种情况,可接受的答案要好得多。
PeteGO
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.