Я использую библиотеку LinqKit , которая позволяет комбинировать выражения на лету.
Это чистое счастье для написания уровня доступа к данным Entity Framewok, потому что несколько выражений можно по желанию повторно использовать и комбинировать, что позволяет получить как читаемый, так и эффективный код.
Рассмотрим следующий фрагмент кода:
private static readonly Expression> _selectMessageViewExpr =
( Message msg, int requestingUserId ) =>
new MessageView
{
MessageID = msg.ID,
RequestingUserID = requestingUserId,
Body = ( msg.RootMessage == null ) ? msg.Body : msg.RootMessage.Body,
Title = ( ( msg.RootMessage == null ) ? msg.Title : msg.RootMessage.Title ) ?? string.Empty
};
Мы объявляем выражение который проецирует сообщение
на MessageView
(я удалил детали для ясности).
Теперь код доступа к данным может использовать это выражение для получения отдельного сообщения:
var query = CompiledQueryCache.Instance.GetCompiledQuery(
"GetMessageView",
() => CompiledQuery.Compile(
_getMessagesExpr
.Select( msg => _selectMessageViewExpr.Invoke( msg, userId ) ) // re-use the expression
.FirstOrDefault( ( MessageView mv, int id ) => mv.MessageID == id )
.Expand()
)
);
Это красиво, потому что то же самое выражение можно повторно использовать и для получения списка сообщений:
var query = CompiledQueryCache.Instance.GetCompiledQuery(
"GetMessageViewList",
() => CompiledQuery.Compile(
BuildFolderExpr( folder )
.Select( msg => _selectMessageViewExpr.Invoke( msg, userId ) )
.OrderBy( mv => mv.DateCreated, SortDirection.Descending )
.Paging()
.Expand()
),
folder
);
Как видите, выражение проекции хранится в _selectMessageViewExpr
и используется для построения нескольких различных запросов.
Однако я потратил много времени на отслеживание странной ошибки, когда этот код разбился в Expand ()
позвоните по номеру .
В сообщении об ошибке говорилось:
Невозможно преобразовать объект типа
System.Linq.Expressions.FieldExpression
в типSystem.Linq.Expressions.LambdaExpression
.
Это только через некоторое время что я понял, что все работает, когда на выражение ссылается в локальной переменной до его вызова Invoke
на :
var selector = _selectMessageViewExpr; // reference the field
var query = CompiledQueryCache.Instance.GetCompiledQuery(
"GetMessageView",
() => CompiledQuery.Compile(
_getMessagesExpr
.Select( msg => selector.Invoke( msg, userId ) ) // use the variable
.FirstOrDefault( ( MessageView mv, int id ) => mv.MessageID == id )
.Expand()
)
);
Этот код работает, как ожидалось.
Мой вопрос: :
Есть ли какая-то конкретная причина, по которой LinqKit не распознает
Invoke
в выражениях, хранящихся в полях? Это просто упущение разработчика, или есть какая-то важная причина, по которой выражения должны быть сначала хранятся в локальных переменных?
На этот вопрос, вероятно, можно ответить, посмотрев сгенерированный код и проверив исходные коды LinqKit, однако я подумал, что, возможно, кто-то, имеющий отношение к разработке LinqKit, сможет ответить на этот вопрос.
Спасибо.