转换表达式<func <xclass,object =“”>&gt;到表达式<func <yclass,object =“”>&gt;

时间:2017-10-05 17:01:13

标签: c# linq parsing reflection casting

我想创建一个通过查询获取项目的存储库模式类。不幸的是,我需要将此查询从一个类解析为另一个类(Picture to ListItem)以将其发送到服务器(api)。所以我的代码应该如下所示:

    public static void ConvertQuery(Expression<Func<Picture, object>> oldQuery)
    {
        Expression<Func<ListItem, object>> newQuery = convert(oldQuery);
    }

并且,例如,我想通过如下所示的强制转换属性转换旧查询:

  • SomePicture.Id =&gt; SomeListItem.Id
  • SomePicture.FileName =&gt; SomeListItem [&#34;文件名&#34;]

我找到了一些我可以投射属性的解决方案。但最大的问题是将一个属性转换为字典字段(item1.Filename到item2。[&#34; Filename&#34;])

更新

@nejcs

我已尝试使用您的解决方案,但不幸的是我有例外:

  

System.ArgumentException:&#39; ParameterExpression类型   &#39; Microsoft.SharePoint.Client.ListItem&#39;不能用于代表   类型&#39; CastExpression.Picture&#39;&#39;

的参数

财产&#34;项目&#34;负责字典值,但我认为转换存在问题。下面是stackTrace:

  

at System.Linq.Expressions.Expression.ValidateLambdaArgs(Type   delegateType,Expression&amp; body,ReadOnlyCollection 1参数)at   System.Linq.Expressions.Expression.Lambda [TDelegate](表达体,   String name,Boolean tailCall,IEnumerable 1 parameters)at   System.Linq.Expressions.Expression 1.Update(表达体,   IEnumerable`1参数)at   System.Linq.Expressions.ExpressionVisitor.VisitLambda [T](表达式1   节点)   System.Linq.Expressions.Expression 1.Accept(ExpressionVisitor visitor)   在System.Linq.Expressions.ExpressionVisitor.Visit(表达式节点)
  在CastExpression.Program.Main(String [] args)

我也知道表达式主体是怎样的

对于oldClassQuery:

Expression<Func<Picture, object>> oldQuery = x => x.FileName == "AS";
  

{x =&gt;转换((x.FileName ==&#34; AS&#34;))}

对于newClassQuery:

Expression<Func<ListItem, object>> newQuery = x => x["FileName"] == "AS";
  

{x =&gt;转换((x.get_Item(&#34; FileName&#34;)==&#34; AS&#34;)}}

1 个答案:

答案 0 :(得分:0)

您正在寻找ExpressionVisitor。只需通过扩展此类并覆盖适当的方法来创建自定义方法,这会将子表达式从一种形式转换为另一种形式。

例如,对于转换成员访问权限,您可以执行以下操作(绝不完整):

public class RewritingVisitor : ExpressionVisitor
{
    private readonly ParameterExpression p = Expression.Parameter(typeof(ListItem)); // create new parameter which will be referenced later

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (node.Type == typeof(Picture))
        {
            return p;
        }
        return node;
    }

    protected override Expression VisitMember(MemberExpression node)
    {
        var rewritten = Visit(node.Expression);
        if (rewritten == node.Expression) return node;

        if (node.Expression != null &&
            node.Expression.Type == typeof(Picture) &&
            rewritten.Type == typeof(ListItem))
        {
            if (node.Member.Name == "Id")
            {
                return Expression.MakeMemberAccess(
                    rewritten,
                    typeof(ListItem).GetProperty("Id"));
            }
            else if (node.Member.Name == "FileName")
            {
                return Expression.MakeIndex(
                    rewritten,
                    typeof(ListItem).GetProperty("Item"), // default indexer name
                    new[] { Expression.Constant("FileName") });
            }
        }
    }
}

然后您可以通过简单地实例化它并使用lambda表达式作为参数调用Visit方法来使用它:

var visitor = new RewritingVisitor();
var newQuery = visitor.Visit(oldQuery);

编辑:

我忘记了一个小但相当重要的部分:如果更新了子表达式,默认情况下,访问者将在传递新值的表达式上调用update(或类似方法)。在lambda表达式的情况下,验证逻辑需要与原来相同类型的表达式,当然这些表达式不是真的。您必须从访问过的部分手动构造新的lambda表达式:

protected override Expression VisitLambda<T>(Expression<T> node)
{
    var lambdaExpr = (LambdaExpression)node;
    var rewrittenParameters = lambdaExpr.Parameters.Select(x => (ParameterExpression)Visit(x)).ToArray();
    var rewrittenBody = Visit(lambdaExpr.Body);

    return Expression.Lambda(rewrittenBody, rewrittenParameters);
}

这在访问者中缺少覆盖,它负责从重写的参数和lambda body创建新的lambda。