为啥实体框架不能在 LINQ 语句中使用 ToString()?

Posted

技术标签:

【中文标题】为啥实体框架不能在 LINQ 语句中使用 ToString()?【英文标题】:Why would Entity Framework not be able to use ToString() in a LINQ statement?为什么实体框架不能在 LINQ 语句中使用 ToString()? 【发布时间】:2010-12-27 14:32:40 【问题描述】:

在 LINQ-to-SQL 中工作

var customersTest = from c in db.Customers
                select new
                
                    Id = c.Id,
                    Addresses = from a in db.Addresses where c.Id.ToString() == 
                        a.ReferenzId select a
                ;

foreach (var item in customersTest)

    Console.WriteLine(item.Id);

但 Entity Framework 中的一个类似示例收到一条错误消息,基本上说它无法“将其转换为 SQL”,这是德语的原始错误消息:

"'LINQ to Entities' erkennt die 方法 'System.String ToString()' nicht, und diese Methode kann nicht in einen Speicherausdruck übersetzt 韦尔登。”

翻译:

"'LINQ to Entities' 无法识别 方法'System.String ToString()', 这个方法不能翻译成 记忆表达。

谁能阐明我们如何让这种语句在实体框架中工作或解释为什么会出现此错误?

【问题讨论】:

Id 已经是字符串了吗?不知道为什么这会导致 EF 出现问题,但错误消息似乎表明这是绊脚石。你会认为 string.ToString() 会很快工作:P Id 在 L2S 示例中是一个整数。在我们真实的 EF 示例中,它是一个 GUID。有趣的是,ToString() 是 intellisense 的,它可以编译但会出现运行时错误。 @Edward Tanguay:我刚刚看到你的问题并添加了一个答案,我希望即使过了这么长时间它仍然有用...... 【参考方案1】:

简单地说:LINQ to Entities 不知道从您的 ID 类型到字符串的转换。

c.ID 的类型是什么?有什么理由为什么它是 ID 的一种类型,而 ReferenzId 的另一种类型?如果可能的话,使它们成为相同的类型,此时您将不再有问题。我不知道是否有其他方法可以在 LINQ to Entities 中执行转换 - 可能有 - 但对齐类型会更简洁。

顺便说一句,这看起来真的是一个连接:

var query = from c in db.Customers
            join a in db.Addresses on c.Id equals a.ReferenzId into addresses
            select new  Id = c.Id, Addresses = addresses ;

编辑:回复您的评论 - ToString 出现在 IntelliSense 中,因为编译器不知道您的查询的含义或翻译方式。它是完全有效的 C#,并且可以生成有效的表达式树 - 只是 EF 不知道如何将该表达式树转换为 SQL。

您可以尝试使用Convert.ToString(c.Id) 而不是仅仅调用c.Id.ToString()...

【讨论】:

这是一个我们正在使用的历史数据库,其中 a.ReferenzID 确实是一个字符串,原因是它可以连接到一个表中的 int 而另一个表中的 GUID。 尝试反向转换?改为致电new Guid(a.ReferenzId) 我怀疑参数化构造函数会起作用,但我认为String.EqualsGuid.Equals 可能。 AFAIR 从一开始就不支持 ToString 的主要原因是我们找不到一种方法来实现一个规范函数,该函数可以从所有可能的不同输入类型转换为字符串相关的目标数据库引擎。无论如何,我们可以在未来重新审视它。【参考方案2】:

据我所知(对于 v1)LINQ to Entities 是非常原始的。换句话说,它不知道如何获取扩展方法“ToString()”并为其生成SQL。

在 LINQ to SQL 中,它在生成 SQL 之前执行扩展方法“ToString()”。不同之处在于 LINQ to Entities 使用 IQueryable 而不是 IEnumerable。

但是,据我所知,强制转换应该可以工作(因为强制转换是一种数据类型,SQL 知道 CAST())。

所以

c.Id.ToString() 应该是 (string)c.Id

(另外,请确保它是(字符串)而不是(字符串))。

关于使用 Lambda(在实体框架中)生成 SQL 表达式而不是纯 LINQ,我想说的其中一个缺点。

请记住,在 SQL 中使用等号左侧的 CAST 性能有点差 :-)

【讨论】:

对晚来的 DV 感到抱歉:(string)c.Id 无法使用“无法将类型 'int' 转换为 'string'”进行编译。 从您写的内容来看,可以认为 Linq2SQL 使用的是 IEnumerable 而不是 IQueryable,这当然不是真的。 @Wiktor Zychla:我已经验证过了,您不能在 EF 查询中使用 (string) 作为转换运算符简单地转换为字符串。但是看看我的回答,我现在有两个选择(发布here)对我来说很好。【参考方案3】:

更新答案:

如果您点击了我在回答开头给您的链接,那么这个缺失的功能同时获得了 75 票,现在(终于!)implemented by Microsoft in EF 6.1。致所有参与的人:感谢您的投票!你的声音被听到了。

例如:

var query = from e in context.Employees where e.EmployeeID.ToString() == "1" select e;

现在将被翻译成:

DECLARE @p0 NVarChar(1000) = '1'
SELECT [t0].[EmployeeID], [t0].[LastName], [t0].[FirstName], [t0].[Title], 
    [t0].[TitleOfCourtesy], [t0].[BirthDate], [t0].[HireDate], [t0].[Address],[t0].[City],
    [t0].[Region], [t0].[PostalCode], [t0].[Country], [t0].[HomePhone], [t0].[Extension], 
    [t0].[Photo], [t0].[Notes], [t0].[ReportsTo], [t0].[PhotoPath]
FROM [Employees] AS [t0]
WHERE (CONVERT(NVarChar,[t0].[EmployeeID])) = @p0

e.EmployeeID.ToString() 转换为 (CONVERT(NVarChar,[t0].[EmployeeID]))


原答案:

Linq2EF 为什么不将.ToString() 转换为正确的 SQL 语句,这对我来说毫无意义,就像 Linq2SQL 所做的那样,只有 Microsoft 开发团队 知道他们尚未实现它的原因。 :-(

但如果您投票支持此功能,则可以通过关注this link.

来提高实施它的优先级

幸运的是,还有 2 种可用的解决方法,我最近在 EF 查询中使用了这两种方法:


I) 帮助我绕过这个限制的是将查询更改为列表,如下所示:

var customersList = (from c in db.Customers
             select c).ToList(); // converts to IEnumerable<T> ...
var customersTest = (from c in customersList 
             select new Id=c.ID.ToString()); // ... which allows to use .ToString()

语句.ToList() 转换为IEnumerable&lt;T&gt;,其中.ToString() 可用。 注意,根据要求,您也可以使用.AsEnumerable(),它的优点是支持延迟执行,如果您有多个相互依赖的 linq 查询,或者如果您重新使用不同的参数值(非常感谢Divega 的提示!)。

之后,您可以根据需要使用此查询,例如:

var customersTest2 = from c in customersTest                 
    select new
            
                Id = c.Id,
                Addresses = from a in db.Addresses where c.Id == a.ReferenzId select a
            ;

当然,如果您需要,您可以根据需要为customersTest 的对象添加更多属性。你也可以优化上面的查询,为了这个例子的可读性,我只使用了 3 个步骤。


II) 对于简单的转换,如果您必须在进一步的子查询中重用生成的查询(并且需要保留IQueryable),请使用System.Data.Objects.SqlClient 中的SqlFunctions,它们将被正确地翻译成 SQL 查询。

示例1:日期转换(您必须使用如下所示的日期部分)

var customersTest = from c in db.Customers
    select new 
        strDate=SqlFunctions.DateName("dd", c.EndDate)
            +"."+SqlFunctions.DateName("mm", c.EndDate)
            +"."+SqlFunctions.DateName("yyyy", c.EndDate) 
    

示例 2:数字到字符串的转换

var customersTest = from c in db.Customers
    select new 
        strID=SqlFunctions.StringConvert((double)c.ID)
    

这应该可以帮助您摆脱大多数需要转换为字符串的情况。


【讨论】:

您也可以使用 AsEnnumerable() 来代替 ToList()。如果您想保留延迟执行以及使用不同参数值多次执行查询的能力,这可能是一个更好的选择。 @divega:谢谢你的提示,我试过了,效果很好!我也更新了我的答案。 另一个提示:如果您使用的是SqlFunctions.StringConvert(...),那么在某些情况下您需要.Trim() 来删除转换函数创建的空格。令人惊讶的是,您可以在查询中使用SqlFunctions.StringConvert(...).Trim()! :-) 如果您想了解如何升级,我在 Visual Studio 杂志上找到了一篇不错的文章:How to upgrade and new features of EF 6。本文来自 2014 年 4 月 1 日,因此它不包括 EF 6.1 的所有新功能,但它是一个好的开始……如果有人知道更好的文章,请告诉我! 顺便说一下,如果您要查找 .NET framework 4.5 中的所有 SqlFunction,here 是一个链接。您需要引用System.Data.Entity.dll 并按照说明导入命名空间System.Data.Objects.SqlClient【参考方案4】:

刚刚发布的Entity Framework 6.1 RTM现在支持.ToString()

【讨论】:

是的,我偶然注意到它支持它。但这在任何参考文献中都没有提到。能否请您提供有关此问题的参考资料? 已在data.uservoice.com/forums/…上方的答案中发布

以上是关于为啥实体框架不能在 LINQ 语句中使用 ToString()?的主要内容,如果未能解决你的问题,请参考以下文章

创建有效的通用实体框架 linq 语句

如果不支持包含,您如何在 LINQ to Entities(实体框架)中执行 SQL 样式的“IN”语句?

为啥实体框架在直接选择语句中的执行速度比 Dapper 快 [关闭]

在实体框架代码优先中,为啥我不能在连接字符串中使用 SQL Server Express?

c# ef框架怎么使用linq语句多表查询?

LINQ 查询到 LINQ 方法;为啥我需要 CBool​​?