Я немного поиграл с вашей проблемой, но без окончательного удовлетворительного результата.Я перечислю только несколько моментов, которые смог найти и понять.
1)
Я переписываю ваш последний фрагмент кода (в упрощенной форме без проекции на анонимный тип)) ...
var query = from c in Countries
select FindSeverity(u => u.Building.Country == c).Max();
... а затем в синтаксисе метода расширения:
var query = Countries
.Select(c => FindSeverity(u => u.Building.Country == c).Max());
Теперь мы лучше видим, что FindSeverity(u => u.Building.Country == c).Max()
является телом изExpression<Func<Country, T>>
(T
в данном случае int
).(Я не уверен, что «тело» - это правильный терминус, но вы понимаете, что я имею в виду: часть справа от лямбда-стрелки =>).Когда весь запрос переводится в дерево выражений, это тело транслируется как вызов метода для функции FindSeverity
.(Это можно увидеть в отладчике, когда вы наблюдаете, что свойство Expression
query
: FindSeverity
является непосредственно узлом в дереве выражений, а не телом этого метода.) Это приводит к сбою при выполнении, поскольку LINQ to Entitiesне знает этот метод.В теле такого лямбда-выражения вы можете использовать только известные функции, например, канонические функции из статического System.Data.Objects.EntityFunctions
класса.
2)
ВозможноОбщий способ создания повторно используемых частей запроса состоит в написании пользовательских методов расширения IQueryable<T>
, например:
public static class MyExtensions
{
public static IQueryable<int?> FindSeverity(this IQueryable<User> query,
Expression<Func<User, bool>> predicate)
{
var last30Days = DateTime.Today.AddDays(-30);
return from u in query.Where(predicate)
from i in u.Issues
where i.Date > last30Days
select i.Severity;
}
}
Затем вы можете писать запросы, такие как:
var max1 = Users.FindSeverity(u => u.Building.ID == 1).Max();
var max2 = Users.FindSeverity(u => u.Building.Country == "Wonderland").Max();
Как вывидите, вы вынуждены писать свои запросы в синтаксисе метода расширения.Я не вижу способа использовать такие пользовательские методы расширения запроса в синтаксисе запроса.
Приведенный выше пример является лишь общим шаблоном для создания фрагментов запроса многократного использования, но он не очень помогает для конкретных запросов в вашемвопрос.По крайней мере, я не знаю, как переформулировать ваш метод FindSeverity
, чтобы он соответствовал этому шаблону.
3)
Я считаю, что ваши исходные запросы не могутработать в LINQ to Entities.Запрос, подобный этому ...
from b in Building
let issueSeverity = (from u in Users
where u.Building == b
from i in u.Issues
where i.Date > last30Days
select i.Severity).Max()
select new
{
Building = b,
IssueSeverity = issueSeverity
}
... подпадает под категорию "Ссылка на нескалярную переменную" внутри запроса, который не поддерживается в LINQ to Entities.(В LINQ to Objects это работает.) Нескалярная переменная в запросе выше - Users
.Если таблица Building
не пуста, ожидается исключение: "Невозможно создать постоянное значение типа EntityType. В этом контексте поддерживаются только примитивные типы (такие как Int32, String и Guid ')."
Похоже, у вас есть отношение один-ко-многим между User
и Building
в базе данных, но эта связь не полностью смоделирована в ваших сущностях: User
имеет навигациюсвойство Building
, но Building
не имеет коллекции Users
.В этом случае я бы ожидал Join
в запросе, что-то вроде:
from b in Building
join u in Users
on u.Building.ID equals b.ID
let issueSeverity = (i in u.Issues
where i.Date > last30Days
select i.Severity).Max()
select new
{
Building = b,
IssueSeverity = issueSeverity
}
Это не создаст упомянутое исключение ссылки на нескалярную переменную.Но, возможно, я неправильно понял вашу модель.