我现在有一篇博客文章(可组合的存储库-嵌套扩展)和 nuget包来帮助在linq中嵌套扩展方法解决方案首先要记住的是,在解析节点时,我们实际上是向后运行的:entity => new ProfileModel{ SomethingElses = entity.SomethingElses.AsQueryable().ToViewModels()}在这里,我们处理ToViewModels(),然后处理AsQueryable(),然后处理SomethingElses,最后处理entity.由于我们发现entity从未被解析(VisitParameter),因此这意味着链中的某些内容阻止了树的遍历.我们这里有两个罪魁祸首: VisitMethodCall()(AsQueryable和ToViewModels)和VisitMemberAccess()(SomethingElses)我们没有覆盖VisitMemberAccess,因此问题必须出在VisitMethodCall 之内该方法有三个出口点:return ((IQueryable)node.Method.Invoke(null, args)).Expression;return Visit((replaceWith as LambdaExpression).Body);return base.VisitMethodCall(node);第一行逐字返回表达式,并停止进一步遍历树.这意味着后代节点将永远不会被访问-正如我们所说的,工作已基本完成.这种行为是否正确,实际上取决于您希望与访问者达成的目标.将代码更改为return Visit(((IQueryable)node.Method.Invoke(null, args)).Expression);意味着我们遍历这个(可能是新的!)表达式.这不能保证我们会访问正确的节点(例如,此表达式可能与原始节点完全独立)-但这确实表示如果此新表达式包含参数表达式,则参数表达式将被正确访问.I want to be able to used nested extension methods to do projection of entities in EF to corresponding view models. (see my previous question Projection of single entities in EF with extension methods for more details on what im doing).As per this question I built an attribute to replace an extension method in an expression tree with a lambda to be able to do this. It takes the method arguments from the extentsion method and replaces them on as VisitParameter is called (I dont know if there is a way to replace the parameters inline in the LambdaExpression).This works well for something like this:entity => new ProfileModel{ Name = entity.Name}And I can see the expression visitor replace the entity parameter on the LambdaExpression to the correct one from the extension method args.However when I change it to something more nested say,entity => new ProfileModel{ SomethingElses = entity.SomethingElses.AsQueryable().ToViewModels()}then I get:Additionally VisitParameter in my expression visitor doesn't seem to get called at all with the parameter 'entity'.Its like its not using my visitor at all for the second Lambda, but I dont know why It would for one and not the other?How can I correctly replace the parameter in the case of both types of lambda expressions?My Visitor below: protected override Expression VisitMethodCall(MethodCallExpression node) { bool expandNode = node.Method.GetCustomAttributes(typeof(ExpandableMethodAttribute), false).Any(); if (expandNode && node.Method.IsStatic) { object[] args = new object[node.Arguments.Count]; args[0] = _provider.CreateQuery(node.Arguments[0]); for (int i = 1; i < node.Arguments.Count; i++) { Expression arg = node.Arguments[i]; args[i] = (arg.NodeType == ExpressionType.Constant) ? ((ConstantExpression)arg).Value : arg; } return ((IQueryable)node.Method.Invoke(null, args)).Expression; } var replaceNodeAttributes = node.Method.GetCustomAttributes(typeof(ReplaceInExpressionTree), false).Cast<ReplaceInExpressionTree>(); if (replaceNodeAttributes.Any() && node.Method.IsStatic) { var replaceWith = node.Method.DeclaringType.GetMethod(replaceNodeAttributes.First().MethodName).Invoke(null, null); if (replaceWith is LambdaExpression) { RegisterReplacementParameters(node.Arguments.ToArray(), replaceWith as LambdaExpression); return Visit((replaceWith as LambdaExpression).Body); } } return base.VisitMethodCall(node); } protected override Expression VisitParameter(ParameterExpression node) { Expression replacement; if (_replacements.TryGetValue(node, out replacement)) return Visit(replacement); return base.VisitParameter(node); } private void RegisterReplacementParameters(Expression[] parameterValues, LambdaExpression expressionToVisit) { if (parameterValues.Length != expressionToVisit.Parameters.Count) throw new ArgumentException(string.Format("The parameter values count ({0}) does not match the expression parameter count ({1})", parameterValues.Length, expressionToVisit.Parameters.Count)); foreach (var x in expressionToVisit.Parameters.Select((p, idx) => new { Index = idx, Parameter = p })) { if (_replacements.ContainsKey(x.Parameter)) { throw new Exception("Parameter already registered, this shouldn't happen."); } _replacements.Add(x.Parameter, parameterValues[x.Index]); } }Full repro code example here: https://github.com/lukemcgregor/ExtensionMethodProjectionEdit:I now have a blog post (Composable Repositories - Nesting Extensions) and nuget package to help with nesting extension methods in linq 解决方案 First thing to remember is that when parsing nodes, we essentially run backwards:entity => new ProfileModel{ SomethingElses = entity.SomethingElses.AsQueryable().ToViewModels()}Here, we process ToViewModels(), then AsQueryable(), then SomethingElses, and finally entity. Since we're finding that entity is never parsed (VisitParameter), it means something in our chain stopped the traversal of the tree.We have two culprits here:VisitMethodCall() (AsQueryable and ToViewModels) and VisitMemberAccess() (SomethingElses)We're not overriding VisitMemberAccess, so the problem must lie within VisitMethodCallWe have three exit points for that method:return ((IQueryable)node.Method.Invoke(null, args)).Expression;return Visit((replaceWith as LambdaExpression).Body);return base.VisitMethodCall(node);The first line returns an expression verbatim, and stops further traversal of the tree. This means descendant nodes will never be visited - as we're saying the work is essentially done. Whether or not this is correct behavior really depends on what you're wanting to achieve with the visitor.Changing the code to return Visit(((IQueryable)node.Method.Invoke(null, args)).Expression);Means we traverse this (potentially new!) expression. This doesn't guarantee we'll visit the correct nodes (for example, this expression may be completely independent of the original) - but it does mean that if this new expression contained a parameter expression, that the parameter expression would be visited properly. 这篇关于表达式访问者仅对某些lambda表达式调用VisitParameter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! 1403页,肝出来的..
09-07 00:36