使用ExpressionVisitor更改' obj == value' to' obj.Equals(value)'

时间:2016-04-03 21:29:59

标签: linq expressionvisitor

我试图比较具有随机值的对象,可能是ID,ObjectKey甚至是同一个对象。简而言之,我想比较一个对象与任何东西,而不仅仅是相同的类型。

为此,我覆盖了对象的Equals()和GetHashCode(),它正在按预期工作。但我注意到当我通过' obj == value'搜索时,Linq不会调用这些方法。

如果我将查询更改为' obj.Equals(value)',则调用Equals()方法。但这不是我需要的。

此外,我试图超载' =='和'!='运算符,但是当我通过接口搜索时,这些重载都没有被调用。

最后,我无法手动更改所有查询,因为有人可能会使用' =='在未来的任何地方,打破代码。

所以我来到ExpressionVisitor。我注意到我可以为Linq查询重写表达式,但我有点无能为力。我已经尝试了一些我发现的例子,但是我遇到了一些错误。

最后,这是我通过ExpressionVisitor所需要的:

替换这个: var objects = ctx.Where(obj => obj == value);

到此: var objects = ctx.Where(obj => obj.Equals(value));

有可能吗?

2 个答案:

答案 0 :(得分:0)

这是可能的。您可以编写一个代理查询提供程序,在重写表达式后将查询传递给真实提供程序。

你也可以采用“LinqKit”使用它的AsExpandable重写器的方法。这种方法要容易得多,但需要将这些调用插入到每个查询中。

您还可以使用Roslyn执行源代码的一次性重构。这样做的缺点是,对于Equals次调用,源代码看起来不太好。

我没有足够的时间来草拟这些解决方案,因为它们需要做很多工作。对于AsExpandable,您可以在网上找到工作代码。我确信还有编写LINQ提供程序的教程。

答案 1 :(得分:0)

耶。发现它:

class Program
{
    static void Main(string[] args)
    {
        //the sample:
        Expression<Func<string, bool>> expr = name => name == "AA" || name.Length > 0 || name != "b";
        Console.WriteLine(expr);
        EqualsModifier treeModifier = new EqualsModifier();
        Expression modifiedExpr = treeModifier.Modify((Expression)expr);
        Console.WriteLine(modifiedExpr);
        Console.ReadLine();
    }
}
//the ExpressionVisitor
public class EqualsModifier : ExpressionVisitor
{
    public Expression Modify(Expression expression)
    {
        return Visit(expression);
    }
    protected override Expression VisitBinary(BinaryExpression b)
    {
        if (b.NodeType == ExpressionType.Equal)
        {
            Expression left = this.Visit(b.Left);
            Expression right = this.Visit(b.Right);
            MethodInfo equalsMethod = typeof(string).GetMethod("Equals", new[] { typeof(string) });
            return Expression.Call(left, equalsMethod, right);
        }
        else if (b.NodeType == ExpressionType.NotEqual)
        {
            Expression left = this.Visit(b.Left);
            Expression right = this.Visit(b.Right);
            MethodInfo equalsMethod = typeof(string).GetMethod("Equals", new[] { typeof(string) });
            return Expression.Not(Expression.Call(left, equalsMethod, right));
        }
        return base.VisitBinary(b);
    }
}

这些输出到:

原件: name =&gt; (((名称==&#34; AA&#34;)OrElse(name.Length&lt; 0))OrElse(名称!=&#34; b&#34;))

转换: name =&gt; ((name.Equals(&#34; AA&#34;)OrElse(name.Length&lt; 0))OrElse Not(name.Equals(&#34; b&#34;)))