Когда вы работаете с Linq To Entities, вам часто нужно знать границу между тем, что выполняется на сервере (базы данных), и тем, что выполняется на клиенте.Полезной отправной точкой (хотя отнюдь не всей историей!) Для работы с этим является наблюдение, когда вы работаете с IQueryable
(сервер) и когда вы работаете с IEnumerable
(клиент).
Если вы посмотрите на имеющуюся у вас статистику и наведете курсор на var
, который объявляет groups
, вы увидите, что компилятор выводит тип IQueryable<GroupWithCount>
.Это означает, что при выполнении итерации groups
код будет выполняться на сервере .Чтобы это работало, все в коде должно быть переведено Entity Framework в SQL.
Entity Framework хорош, но не совершенен, и одна из вещей, которые он не знает, как это сделать, - переводвызов Int32.ToString()
в SQL.Таким образом, все это отвергается.
В этом случае исправить легко.Вся необходимая информация содержится в каждом GroupWithCount
, поэтому мы можем просто добавить .AsEnumerable()
для принудительной оценки запроса на стороне сервера и затем проецировать в требуемую форму:
var groups = (from g in DB.Groups
where g.user_id == user_id
let mC=(from c in DB.Contacts where c.group_id == g.group_id select c).Count()
select new GroupWithCount
{
members = mC,
group_id = g.group_id,
group_name = g.group_name
})
.AsEnumerable() // come to the client
// next Select is evaluated on the client
.Select(gwc => new GroupWithCount
{
members = gwc.members,
group_id = gwc.group_id,
group_name = gwc.group_name + gwc.members.ToString()
};
Все, что после AsEnumerable
будет оцениваться на клиенте, поэтому может включать произвольный код C #, включая требуемый вызов ToString
.Изучение var
теперь покажет, что компилятор определил тип IEnumerable<GroupWithCount>
.
Часто бывает, что мы хотим выполнить, скажем, сложную фильтрацию на сервере.так что было бы неплохо вернуть все обратно с AsEnumerable
- но здесь все в порядке.