LINQ to Entities не распознает метод 'System.String Format (System.String, System.Object, System.Object)'

У меня есть этот запрос linq:

private void GetReceivedInvoiceTasks(User user, List<Task> tasks)
{
    var areaIds = user.Areas.Select(x => x.AreaId).ToArray();

    var taskList = from i in _db.Invoices
                   join a in _db.Areas on i.AreaId equals a.AreaId
                   where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
                   select new Task {
                       LinkText = string.Format(Invoice {0} has been received from {1}, i.InvoiceNumber, i.Organisation.Name),
                       Link = Views.Edit
                   };
}

У этого есть проблемы, хотя. Я пытаюсь создать задачи. Для каждой новой задачи, когда я устанавливаю текст ссылки на константную строку типа "Привет", это нормально. Однако выше я пытаюсь создать свойство linktext, используя свойства счета-фактуры.

Я получаю эту ошибку:

base {System.SystemException} = { "LINQ to Entities не распознает метод" System.String Format (System.String, System.Object, System.Object) ", и этот метод не может быть переведен в хранилище выражение".}

Кто-нибудь знает почему? Кто-нибудь знает альтернативный способ сделать это, чтобы заставить его работать?

Ответ 1

Entity Framework пытается выполнить вашу проекцию на стороне SQL, где нет эквивалента string.Format. Используйте AsEnumerable(), чтобы принудительно оценить эту часть с Linq для объектов.

На основе в предыдущем ответе, который я вам дал, я бы реструктурировал ваш запрос следующим образом:

int statusReceived = (int)InvoiceStatuses.Received;
var areaIds = user.Areas.Select(x=> x.AreaId).ToArray();

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select i)
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.Organisation.Name),
                  Link = Views.Edit
                });

Также я вижу, что вы используете связанные объекты в запросе (Organisation.Name), убедитесь, что вы добавили правильный Include к вашему запросу или конкретно материализуете эти свойства для последующего использования, то есть:

var taskList = (from i in _db.Invoices
               where i.Status == statusReceived && areaIds.Contains(i.AreaId)
               select new { i.InvoiceNumber, OrganisationName = i.Organisation.Name})
               .AsEnumerable()
               .Select( x => new Task()
               {
                  LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.OrganisationName),
                  Link = Views.Edit
                });

Ответ 2

IQueryable происходит от IEnumerable, главное сходство в том, что когда вы делаете запрос, он публикуется в ядро базы данных на его языке, то тонкий момент - когда вы говорите С# обрабатывать данные на сервере (не на стороне клиента) или SQL обрабатывать данные.

Так что, в основном, когда вы говорите IEnumerable.ToString(), С# получает сбор данных и вызывает ToString() для объекта. Но когда вы говорите, что IQueryable.ToString() С# указывает SQL вызвать ToString() для объекта, но в SQL такого метода нет.

Недостатком является то, что при обработке данных в С# вся коллекция, которую вы просматриваете, должна быть встроена в память, прежде чем С# применяет фильтры.

Наиболее эффективный способ сделать это - сделать запрос как IQueryable со всеми фильтрами, которые вы можете применить.

А затем собрать его в памяти и выполнить форматирование данных в С#.

IQueryable<Customer> dataQuery = Customers.Where(c => c.ID < 100 && c.ZIP == 12345 && c.Name == "John Doe");

 var inMemCollection = dataQuery.AsEnumerable().Select(c => new
                                                  {
                                                     c.ID
                                                     c.Name,
                                                     c.ZIP,
                                                     c.DateRegisterred.ToString("dd,MMM,yyyy")
                                                   });

Ответ 3

SQL не знает, что делать со string.Format может выполнять конкатенацию строк.

Если вы запустите следующий код, вы должны получить данные, которые вам нужны.

var taskList = from i in _db.Invoices
               join a in _db.Areas on i.AreaId equals a.AreaId
               where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
               select new Task {
                   LinkText = "Invoice " + i.InvoiceNumber + "has been received from " + i.Organisation.Name),
                   Link = Views.Edit
               };

Как только вы действительно выполните запрос, это должно быть немного быстрее, чем с помощью AsEnumerable (по крайней мере, то, что я нашел в своем собственном коде после того, как у вас была та же самая исходная ошибка). Если вы делаете что-то более сложное с С#, вам все равно придется использовать AsEnumerable.