Почему Entity Framework не сможет использовать ToString () в операторе LINQ? - PullRequest
13 голосов
/ 17 декабря 2009

Это работает в 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", вот оригинальное сообщение об ошибке на немецком языке:

"erkennt die" LINQ to Entities " Метод 'System.String ToString ()' Nicht, Und Diese Methode Канн Нихт в Einen Speicherausdruck übersetzt Верден. "

Перевод:

"LINQ to Entities" не распознает Метод 'System.String ToString ()', этот метод не может быть переведен на выражение памяти.

Может кто-нибудь пролить свет на то, как мы можем заставить такого рода оператор работать в Entity Framework, или объяснить, почему он получает эту ошибку?

Ответы [ 4 ]

9 голосов
/ 17 декабря 2009

Проще говоря: LINQ to Entities не знает о преобразовании типа идентификатора в строку.

Какой тип c.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 };

РЕДАКТИРОВАТЬ: Чтобы ответить на ваш комментарий - в IntelliSense появляется ToString, потому что компилятор не имеет реального представления о том, что будет означать ваш запрос или как он будет переведен. Это совершенно правильный C # и может генерировать правильное дерево выражений - просто EF не знает, как преобразовать это дерево выражений в SQL.

Вы можете попробовать , используя Convert.ToString(c.Id) вместо простого вызова c.Id.ToString() ...

8 голосов
/ 03 июля 2013

Обновленный ответ:

Если вы перешли по ссылке, которую я дал вам в начале моего ответа, эта недостающая функция тем временем получила 75 голосов и теперь (наконец-то!) реализована Microsoft в 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 знает причину, по которой они еще не реализовали это. : - (

Но вы можете повысить приоритет для его реализации, если вы проголосуете за эту функцию , перейдя по этой ссылке.

К счастью, есть также 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<T>, где доступно .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), используйте SqlFunctions из System.Data.Objects.SqlClient, они будут правильно переведены в запросы 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)
    }

Это должно помочь вам в большинстве ситуаций, когда требуется преобразование в строки.


6 голосов
/ 18 марта 2014

Entity Framework 6.1 RTM, который был только что выпущен, теперь поддерживает .ToString ()

0 голосов
/ 20 декабря 2009

LINQ to Entities, насколько я понимаю (для v1), очень примитивен. Другими словами, он не знает, как взять метод расширения "ToString ()" и сгенерировать для него SQL.

В LINQ to SQL он выполняет метод расширения "ToString ()" перед генерацией SQL. Разница в том, что LINQ to Entities использует IQueryable вместо IEnumerable.

НО, насколько я помню, приведение должно работать (потому что приведение типов данных и SQL знает о CAST ()).

Итак

c.Id.ToString () должен быть (строка) c.Id

(также убедитесь, что это (строка), а не (строка)).

Один из недостатков, который я бы сказал, об использовании Lambda (в Entity Framework) для генерации выражения SQL вместо чистого LINQ.

Имейте также в виду, что использование CAST в левой части знака равенства в SQL немного неэффективно: -)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...