У меня есть этот запрос 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
};
}
Однако у него есть проблемы. Пытаюсь создавать задачи. Для каждой новой задачи, когда я устанавливаю текст ссылки в виде постоянной строки, такой как «Привет», это нормально. Однако выше я пытаюсь создать текст ссылки свойства, используя свойства счета-фактуры.
Я получаю такую ошибку:
base {System.SystemException} = {"LINQ to Entities не распознает метод 'System.String Format (System.String, System.Object, System.Object)', и этот метод не может быть преобразован в выражение хранилища." }
Кто-нибудь знает почему? Кто-нибудь знает альтернативный способ сделать это, чтобы он работал?
linq
entity-framework
linq-to-entities
AnonyMouse
источник
источник
Ответы:
Entity Framework пытается выполнить вашу проекцию на стороне SQL, где нет эквивалента
string.Format
. ИспользуйтеAsEnumerable()
для принудительной оценки этой части с помощью Linq to Objects.Основываясь на предыдущем ответе, который я вам дал, я бы реструктурировал ваш запрос следующим образом:
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 });
Также я вижу, что вы используете связанные сущности в query (
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 });
источник
IQueryable
происходит изIEnumerable
, основное сходство заключается в том, что когда вы делаете свой запрос, он отправляется в движок базы данных на этом языке, тонкий момент - это когда вы указываете C # обрабатывать данные на сервере (а не на стороне клиента) или указывать SQL для обработки данные.Итак, когда вы говорите
IEnumerable.ToString()
, C # получает сбор данных и вызываетToString()
объект. Но когда вы говорите, чтоIQueryable.ToString()
C # указывает SQL вызватьToString()
объект, в SQL нет такого метода.Недостатком является то, что когда вы обрабатываете данные на C #, вся просматриваемая коллекция должна быть создана в памяти, прежде чем C # применит фильтры.
Самый эффективный способ сделать это - сделать запрос, как
IQueryable
и все фильтры, которые вы можете применить.А затем накопите его в памяти и выполните форматирование данных на C #.
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") });
источник
Хотя 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
(по крайней мере, то, что я нашел в моем собственном коде после той же исходной ошибки, что и вы). Если вы делаете что-то более сложное с C #, вам все равно придется использоватьAsEnumerable
.источник
AsEnumerable()
может быть гораздо более эффективным. ИзбегайтеAsEnumerable()
иToList()
до тех пор, пока вы действительно не захотите запоминать все результаты.