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
.