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")...)
但要小心,当你调用AsEnumerable
或ToList
方法时,因为你会在这个方法之前请求来自所有实体的所有数据。 在我上面的例子中,我通过一个请求读取所有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有CAST
和CONVERT
,所以我现在最好的猜测是linq for实体在一些简单的情况下使用它。 DateTime对象并不总是被认为是如此简单…
只要将LINQ to Entity查询转换为LINQ to Objects查询(例如调用ToArray),只要您需要在您的LINQ查询中使用方法调用。