У меня есть сложный вопрос о преобразовании выражений Linq. У меня был хороший поиск, но я не смог найти ничего, что могло бы покрыть это дело. Я неплохо знаком с Linq, по крайней мере, с точки зрения создания и передачи лямбда-выражений в методы, но я несколько слабее в работе с Expression.
Во-первых, некоторый контекст: у меня есть универсальное решение для сохраняемости, основанное на NHibernate, которое используется в ряде проектов, похожих на DDD. Для особых случаев, когда данный набор дочерних элементов в агрегате может быть по существу бесконечным (т.е. действительно очень большим), я не могу просто отобразить набор или пакет, так как никогда не будет приемлемо загружать всю коллекцию в память. В этой архитектуре код, использующий API, не может напрямую обращаться к репозиторию, чтобы выполнить запрос или таким образом ограничить результаты. Конечно, я мог бы вообще не иметь коллекции в API и вместо этого предоставлять методы для извлечения соответствующих подмножеств дочерних объектов (и если это не сработает, я так и сделаю), но я пытаюсь сделать что-то немного другое. и у меня почти все получилось...
Эти проекты сопоставляются с помощью Fluent (не с автоматическим сопоставлением), поэтому я добавил метод в свой базовый класс карт в форме
HasManyQueryable<TCollection>(Expression<Func<T, IQueryable<TCollection>>> memberExpression, Expression<Func<T, TCollection, bool>> selector)
Этот метод извлекает соответствующий PropertyInfo
из первого Expression
(который указывает элемент для сопоставления). Селектор Expression
содержит отношение между родительским и дочерним объектами в качестве замены обычного отображения NHibernate.
Итак, предположим, у меня есть селектор на карте для типа домена User
(который выше T
):
HasManyQueryable<Transaction>(x => x.Transactions, (u, t) => t.User == u);
Это задает сопоставление между подмножеством всех Transactions
, где Transaction.User
— это предоставленный User
u, и свойством User.Transactions
, которое равно IQueryable<Transaction>
. Когда реальный объект User
создан, мне нужно превратить его в
Expression<Func<Transaction, bool>> expression = (t => t.User == this)
где this
— строящийся объект User
. Другими словами, я хочу взять общее правило, которое говорит, как сопоставить Users
с Transactions
, и превратить его в правило о сопоставлении этого User
с Transactions
. Затем я могу использовать это выражение для создания IQueryable<Transaction>
из репозитория, выполнив запрос Linq, таким образом:
return Repository.For<Transaction>().Where(selector);
Это может работать только тогда, когда селектор равен Func<Transaction, bool>
, поэтому мне нужно превратить исходное выражение, которое сгенерирует Func<User, Transaction, bool>
в Func<Transaction, bool>
.
Это дает мне коллекцию IQueryable
, в которой все операции запросов выполняются как запросы Linq-to-NHibernate, и, таким образом, вся коллекция никогда не загружается в память (да, я знаю, что вы можете сформулировать запрос, который действительно заставит это сделать, но Я могу поймать их во время проверки кода).
Фу. Надеюсь, это имеет смысл.
Кто-нибудь, у кого есть навыки переписывания Expression, может указать мне правильное направление?