使用 ExpressionVisitor 将 'obj == value' 更改为 'obj.Equals(value)'
Use ExpressionVisitor to change 'obj == value' to 'obj.Equals(value)'
我正在尝试将一个对象与一个随机值进行比较,该随机值可以是一个 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));
可能吗?
这是可能的。您可以编写一个代理查询提供程序,在重写表达式后将查询传递给真正的提供程序。
您还可以采用 "LinqKit" 与其 AsExpandable
重写器一起使用的方法。这种方法要简单得多,但需要将这些调用插入到每个查询中。
您还可以使用 Roslyn 对源代码执行一次性重构。这样做的缺点是那些 Equals
调用的源代码看起来不太好。
我没有必要的时间来草拟这些解决方案,因为它们需要大量工作。对于 AsExpandable
,您可以在网上找到工作代码。我敢肯定也有编写 LINQ 提供程序的教程。
耶。找到了:
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 => (((name == "AA") OrElse (name.Length < 0)) OrElse (name != "b"))
已转换:
name => ((name.Equals("AA") OrElse (name.Length < 0)) OrElse Not(name.Equals("b")))
我正在尝试将一个对象与一个随机值进行比较,该随机值可以是一个 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));
可能吗?
这是可能的。您可以编写一个代理查询提供程序,在重写表达式后将查询传递给真正的提供程序。
您还可以采用 "LinqKit" 与其 AsExpandable
重写器一起使用的方法。这种方法要简单得多,但需要将这些调用插入到每个查询中。
您还可以使用 Roslyn 对源代码执行一次性重构。这样做的缺点是那些 Equals
调用的源代码看起来不太好。
我没有必要的时间来草拟这些解决方案,因为它们需要大量工作。对于 AsExpandable
,您可以在网上找到工作代码。我敢肯定也有编写 LINQ 提供程序的教程。
耶。找到了:
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 => (((name == "AA") OrElse (name.Length < 0)) OrElse (name != "b"))
已转换: name => ((name.Equals("AA") OrElse (name.Length < 0)) OrElse Not(name.Equals("b")))