为什么Entity Framework无法在LINQ语句中使用ToString()?

Edw*_*uay 13 c# linq entity-framework linq-to-sql

适用于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);
}
Run Code Online (Sandbox Code Playgroud)

但实体框架中的类似示例会收到一条错误消息,表示它无法"将其转换为SQL",这是德语中的原始错误消息:

"'LINQ to Entities'erkennt die Methode'System.String ToString()'nicht,und diese Methode kann nicht in einenSpeicherausdruckübersetztwerden."

翻译:

"'LINQ to Entities'无法识别Method'System.String ToString()',此方法无法转换为内存表达式.

任何人都可以阐明我们如何在Entity Framework中使用这种语句或解释为什么会出现这个错误?

Jon*_*eet 9

简单地说: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 };
Run Code Online (Sandbox Code Playgroud)

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

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

  • AFAIR从一开始就不支持ToString的主要原因是我们找不到一种方法来实现一个规范函数,该函数将从所有相关目标数据库引擎中的所有可能的不同输入类型执行转换为字符串.无论如何,这是我们将来可以重访的东西. (2认同)

Mat*_*att 8

对我来说没有任何意义,因为Linq2EF没有转换e.EmployeeID.ToString()为正确的SQL语句,正如Linq2SQL所做的那样,只有Microsoft开发团队知道他们尚未实现它的原因.:-(

但是,如果您通过此链接投票支持此功能,则可以提高实施优先级.

幸运的是,还有2个可用的解决方法,我最近在EF查询中使用它们:


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

var query = from e in context.Employees where e.EmployeeID.ToString() == "1" select e;
Run Code Online (Sandbox Code Playgroud)

声明(CONVERT(NVarChar,[t0].[EmployeeID]))转换为.ToString(),其中.ToList()是可用的.请注意,根据要求,您也可以使用IEnumerable<T>,这样做的优点是支持延迟执行,如果您有多个linq查询相互依赖或者您使用的是不同的参数值,那就更好了(非常感谢Divega这个暗示!).

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

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
Run Code Online (Sandbox Code Playgroud)

当然,如果需要,可以.ToString()根据需要为对象添加更多属性.您还可以优化上面的查询,我只使用了3个步骤来实现此示例的可读性.


II)对于简单的转换,如果你必须在更多的子查询中重用生成的查询(并且它需要保留.AsEnumerable()),使用customersTestfrom IQueryable,它们将被正确地转换为SQL查询.

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

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()
Run Code Online (Sandbox Code Playgroud)

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

var customersTest2 = from c in customersTest                 
    select new
            {
                Id = c.Id,
                Addresses = from a in db.Addresses where c.Id == a.ReferenzId select a
            };
Run Code Online (Sandbox Code Playgroud)

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


更新:如果您按照我在回答开始时给您的链接,这个缺失的功能同时获得了75票,现在(最终!)由Microsoft在EF 6.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) 
    }
Run Code Online (Sandbox Code Playgroud)

现在将翻译为:

var customersTest = from c in db.Customers
    select new {
        strID=SqlFunctions.StringConvert((double)c.ID)
    }
Run Code Online (Sandbox Code Playgroud)

SqlFunctions转换为System.Data.Objects.SqlClient.


Jam*_*ock 6

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