gpt4 book ai didi

c# - LinqKit 的扩展器无法从字段中获取表达式是否有特殊原因?

转载 作者:IT王子 更新时间:2023-10-29 04:19:06 26 4
gpt4 key购买 nike

我正在使用 LinqKit允许动态组合表达式的库。

对于编写 Entity Framewok 数据访问层来说,这是一种纯粹的幸福,因为可以选择性地重用和组合多个表达式,从而实现代码的可读性和高效性。

考虑以下代码:

private static readonly Expression<Func<Message, int, MessageView>> _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
};

我们声明了一个将 Message 转换到 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() 调用时崩溃
错误说:

Unable to cast object of type System.Linq.Expressions.FieldExpression to type 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()
)
);

代码按预期工作。

我的问题是:

Is there any specific reason why LinqKit doesn't recognize Invoke on expressions stored in fields? Is it just an omission by developer, or is there some important reason why expressions need to be stored in local variables first?

这个问题可能可以通过查看生成的代码和检查 LinqKit 源来回答,但是我认为也许与 LinqKit 开发相关的人可以回答这个问题。

谢谢。

最佳答案

我下载了源代码并尝试对其进行分析。 ExpressionExpander 不允许引用存储在常量之外的变量中的表达式。它期望调用 Invoke 方法的表达式引用由 ConstantExpression 表示的对象,而不是另一个 MemberExpression

因此我们不能提供我们的可重用表达式作为对类的任何成员的引用(即使是公共(public)字段,而不是属性)。也不支持嵌套成员访问(如 object.member1.member2 ... 等)。

但这可以通过遍历初始表达式并递归地提取子字段值来解决。

我已经将 ExpressionExpander 类的 TransformExpr 方法代码替换为

var lambda = Expression.Lambda(input);
object value = lambda.Compile().DynamicInvoke();

if (value is Expression)
return Visit((Expression)value);
else
return input;

现在可以使用了。

在这个解决方案中,我之前提到的所有内容(递归遍历树)都是由 ExpressionTree 编译器为我们完成的:)

关于c# - LinqKit 的扩展器无法从字段中获取表达式是否有特殊原因?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6226129/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com