LINQ 到实体不识别方法的系统。字符串 ToString()方法,此方法不能转换为存储表达式

我正在把一些东西从 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)时,它抛出一个异常:

LINQtoEntity 不识别该方法的 System.String 方法,并且此方法不能被转换为存储 表情。

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

222854 次浏览

像这样改变它,它应该起作用:

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

异常不在 LINQ 查询声明的行中抛出,而在 foreach行中抛出的原因是延迟执行特性,也就是说,在尝试访问结果之前,LINQ 查询不会执行。这发生在 foreach而不是更早的时候。

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

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

var keyString = item.Key.ToString();


var pages = from p in context.entities
where p.Serial == keyString
select p;

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

var strItem = item.Key.ToString();


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

问题出现是因为 ToString()并没有真正执行,它被转换成 方法组,然后被解析并转换成 SQL。由于没有等效的 ToString(),表达式将失败。

注:

请确保您还签出了关于后来添加的 SqlFunctions助手类的 Alex 的回答。在许多情况下,它可以消除对临时变量的需要。

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

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

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

希望这个能帮上忙。

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

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

对于这种情况,您在这里寻找的是 字符串转换:

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

当带有临时变量的解决方案由于某种原因而不受欢迎时,这种方法很好。

与 Sqlfunction 类似,您还可以使用 实体函数(其中的 EF6被 数据库函数取代) ,它提供了一组不同的函数,这些函数也是数据源不可知的(不仅限于 SQL)。

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

如果你真的想在查询中键入 ToString,你可以编写一个表达式树访问器,用 调用适当的 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);

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

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

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

删除问题行,解决问题。

我不完全理解为什么,但似乎 ToString ()是字符串的 LINQ 表达式,而不是实体的。LINQforEntities 处理 SQL 这样的数据库查询,而 SQL 没有 ToString ()的概念。因此,我们不能将 ToString ()抛入。凡()条款。

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

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

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

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

升级到 实体框架6.2.0版对我来说很有用。

我之前在6.0.0版本。

希望这对你有帮助,

我的问题是这个专栏使用了 'text'数据类型(由于从 sqlite 迁移)。 解决方案: 只需将数据类型更改为 'nvarchar()'并重新生成表。

然后 Linq 接受字符串比较。

我正在致力于淘汰 Telerik 开放存取并用实体框架4.0取而代之。我遇到了相同的问题,telerik: 网格边界柱过滤停止工作。

我发现它不仅仅在 System.String DataTypes上工作。因此,我找到了这个线程,并通过在 Linq 查询的末尾使用 .List()解决了这个问题,如下所示:

var x = (from y in  db.Tables
orderby y.ColumnId descending
select new
{
y.FileName,
y.FileSource,
y.FileType,
FileDepartment = "Claims"
}).ToList();