LINQ to Entities не распознает метод "System.String 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), он выдает исключение:

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

Кто-нибудь знает, почему это происходит?

Ответ 1

Просто сохраните строку в переменной temp, а затем используйте ее в своем выражении:

var strItem = item.Key.ToString();

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

Проблема возникает из-за того, что ToString() на самом деле не выполняется, она превращается в MethodGroup, а затем анализируется и переводится на SQL. Поскольку эквивалент ToString() отсутствует, выражение терпит неудачу.

Примечание:

Убедитесь, что вы также проверили ответ Alex относительно вспомогательного класса SqlFunctions, который был добавлен позже. Во многих случаях это может устранить необходимость в временной переменной.

Ответ 2

Как и другие ответили, это ломается, потому что .ToString не может перевести на соответствующий SQL по пути в базу данных.

Однако Microsoft предоставляет класс SqlFunctions, который представляет собой набор методов, которые можно использовать в таких ситуациях.

В этом случае вы здесь SqlFunctions.StringConvert:

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

Хорошо, когда решение с временными переменными нежелательно по каким-либо причинам.

Подобно SqlFunctions, вы также имеете EntityFunctions (с EF6, устаревшим DbFunctions), который предоставляет другой набор функций, которые также являются агностиками источника данных (не ограничиваясь, например, SQL).

Ответ 3

Проблема заключается в том, что вы вызываете ToString в запросе LINQ to Entities. Это означает, что синтаксический анализатор пытается преобразовать вызов ToString в его эквивалентный SQL (что невозможно... отсюда исключение).

Все, что вам нужно сделать, - переместить вызов ToString в отдельную строку:

var keyString = item.Key.ToString();

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

Ответ 4

Была аналогичная проблема. Решил его, вызвав ToList() в коллекции сущностей и запросив список. Если коллекция небольшая, это вариант.

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

Надеюсь, что это поможет.

Ответ 5

Измените его так, и он должен работать:

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

Причина, по которой исключение не выбрано в строке, объявленной запросом LINQ, но в строке foreach есть функция отложенного выполнения, то есть запрос LINQ не выполняется до тех пор, пока вы не попытаетесь получить доступ к результату. И это происходит в foreach и не раньше.

Ответ 6

Переведите таблицу в Enumerable, затем вызовите методы LINQ с помощью метода ToString() внутри:

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

Но будьте осторожны, когда вы вызываете методы AsEnumerable или ToList, потому что вы будете запрашивать все данные из всей сущности перед этим методом. В моем случае выше я прочитал все строки table_name по одному запросу.

Ответ 7

Обновление до Entity Framework версии 6.2.0 сработало для меня.

Я был ранее на версии 6.0.0.

Надеюсь это поможет,

Ответ 8

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

Ответ 9

Если вы действительно хотите ввести ToString внутри своего запроса, вы можете написать посетителю дерева выражений, который переписывает вызов ToString с вызовом к соответствующему StringConvert функция:

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

        }
    }
}

Ответ 10

В этом случае я получил ту же ошибку:

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 для строк, но не для Entities. LINQ для Entities имеет дело с запросами базы данных, такими как SQL, и SQL не имеет понятия ToString(). Таким образом, мы не можем перетащить ToString() в предложение .Where().

Но как же работает первая строка? Вместо ToString() SQL имеет CAST и CONVERT, поэтому я догадываюсь, что linq для сущностей использует это в некоторых простых случаях. Объекты DateTime не всегда оказываются настолько простыми...

Ответ 11

Просто поверните запрос LINQ to Entity в запрос LINQ to Objects (например, вызовите ToArray) в любое время, когда вам нужно использовать вызов метода в запросе LINQ.