LINQ to Entities不能识别方法System.String ToString()方法,并且这个方法不能被转换成存储expression式

我正在从一个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)它会抛出一个exception说:

LINQ to Entities不能识别方法System.String ToString()方法,并且此方法不能被转换成存储expression式。

任何人都知道为什么会这样

只需将string保存到一个临时variables,然后在expression式中使用它:

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

问题的出现是因为ToString()没有被真正执行,它被转换成一个MethodGroup ,然后被parsing并转换成SQL。 由于没有相同的ToString() ,所以expression式失败。

注意:

确保你也检查了Alex关于SqlFunctions添加的SqlFunctions辅助类的答案 。 在许多情况下,它可以消除临时variables的需要。

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

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

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

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

很好,当有临时variables的解决scheme由于任何原因不可取时。

与SqlFunctions类似,您也有EntityFunctions ( EF6被DbFunctions废弃),它提供了一组不同的函数,这些函数也是数据源不可知的(不限于例如SQL)。

问题是,你在一个LINQ to Entities查询中调用ToString。 这意味着parsing器试图将ToString调用转换为其等效的SQL(这是不可能的…因此例外)。

你所要做的就是将ToString调用移动到一个单独的行:

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

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

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

希望这可以帮助。

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

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

在LINQ查询被声明的行中没有抛出exception的原因是在foreach的行中是延迟执行特性,即LINQ查询不会被执行,直到你试图访问结果为止。 这发生在foreach而不是早些时候。

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

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

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

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

 [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); } } 

如果你真的想在你的查询中键入ToString ,你可以写一个expression式树访问器,通过调用相应的StringConvert函数来重写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); } } } 

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

 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); 

花费太多时间debugging之后,我发现逻辑expression式中出现了错误。

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

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

删除有问题的线路和问题解决。

我不完全理解为什么,但它似乎ToString()是一个LINQexpression式的string,但不是实体。 实体的LINQ处理SQL等数据库查询,而SQL没有ToString()的概念。 因此,我们不能将ToString()引入.Where()子句中。

但是第一线如何运作呢? 而不是ToString(),SQL有CASTCONVERT ,所以我现在最好的猜测是linq for实体在一些简单的情况下使用它。 DateTime对象并不总是被认为是如此简单…

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