Skip to content

Instantly share code, notes, and snippets.

@mcintyre321
Last active October 4, 2019 00:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mcintyre321/6294588 to your computer and use it in GitHub Desktop.
Save mcintyre321/6294588 to your computer and use it in GitHub Desktop.
Replacing a case-sensitive methods in an Expression Tree with a case-insensitive equivalents using QueryInterceptor. Very cool! This is runnable in LinqPad if you add the QueryInterceptor Nuget package
void Main()
{
var words = new []{"HELLO"}.AsQueryable().SetComparer(StringComparison.CurrentCultureIgnoreCase);
words.Where (x => x.StartsWith("hel")).Dump();
words.Where (x => x.Contains("ell")).Dump();
words.Where (x => x.EndsWith("llo")).Dump();
words.Where (x => x.Equals("hello")).Dump();
words.Where (x => x == "hello").Dump();
words.Where (x => x != "hello").Dump(); //dont want to match this one...
}
public static class IQueryableCaseExtensions
{
public static IQueryable<T> SetComparer<T>(this IQueryable<T> q, StringComparison sc){
return q
.InterceptWith(new SetComparerExpressionVisitor(sc));
}
}
public class SetComparerExpressionVisitor : ExpressionVisitor
{
readonly StringComparison _comparer;
public SetComparerExpressionVisitor(StringComparison comparer)
{
_comparer = comparer;
}
protected override Expression VisitBinary(BinaryExpression node)
{
if (node.Left.Type == typeof (string) && node.Right.Type == typeof (string))
{
if (node.NodeType == ExpressionType.Equal)
{
var exp = ((LambdaExpression) MakeExpression(s => s.Equals("asdf", _comparer))).Body;
exp = new ReplacingVisitor(((dynamic) exp).Arguments[0], ((dynamic) node).Left).Visit(exp);
exp = new ReplacingVisitor(((dynamic) exp).Object, ((dynamic) node).Right).Visit(exp);
return exp;
}
if (node.NodeType == ExpressionType.NotEqual)
{
var exp = ((LambdaExpression) MakeExpression(s => !s.Equals("asdf", _comparer))).Body;
exp = new ReplacingVisitor(((dynamic) exp).Operand.Arguments[0], ((dynamic) node).Left).Visit(exp);
exp = new ReplacingVisitor(((dynamic) exp).Operand.Object, ((dynamic) node).Right).Visit(exp);
return exp;
}
}
return base.VisitBinary(node);
}
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.DeclaringType == typeof (string))
{
if (node.Method.Name == "Contains")
{
var exp = ((LambdaExpression) MakeExpression(s => s.IndexOf("asdf", _comparer) > -1)).Body;
exp =
new ReplacingVisitor(((dynamic) exp).Left.Arguments[0], ((dynamic) node).Arguments[0]).Visit(exp);
exp = new ReplacingVisitor(((dynamic) exp).Left.Object, ((dynamic) node).Object).Visit(exp);
return exp;
}
if (node.Method.Name == "StartsWith")
{
var exp = ((LambdaExpression) MakeExpression(s => s.IndexOf("asdf", _comparer) == 0)).Body;
exp =
new ReplacingVisitor(((dynamic) exp).Left.Arguments[0], ((dynamic) node).Arguments[0]).Visit(exp);
exp = new ReplacingVisitor(((dynamic) exp).Left.Object, ((dynamic) node).Object).Visit(exp);
return exp;
}
if (node.Method.Name == "EndsWith")
{
var exp = ((LambdaExpression) MakeExpression(s => s.EndsWith("asdf", _comparer))).Body;
exp = new ReplacingVisitor(((dynamic) exp).Arguments[0], ((dynamic) node).Arguments[0]).Visit(exp);
exp = new ReplacingVisitor(((dynamic) exp).Object, ((dynamic) node).Object).Visit(exp);
return exp;
}
if (node.Method.Name == "Equals")
{
var exp = ((LambdaExpression) MakeExpression(s => s.Equals("asdf", _comparer))).Body;
exp = new ReplacingVisitor(((dynamic) exp).Arguments[0], ((dynamic) node).Arguments[0]).Visit(exp);
exp = new ReplacingVisitor(((dynamic) exp).Object, ((dynamic) node).Object).Visit(exp);
return exp;
}
}
return base.VisitMethodCall(node);
}
public Expression MakeExpression(Expression<Func<string, bool>> exp)
{
return exp;
}
}
public class ReplacingVisitor : ExpressionVisitor {
Func<Expression, bool> match;
Func<Expression, Expression> createReplacement;
public ReplacingVisitor(Expression from, Expression to){
match = node => from == node;
createReplacement = node => to;
}
public override Expression Visit(Expression node){
if (match(node)) return createReplacement(node);
return base.Visit(node);
}
}
@mcintyre321
Copy link
Author

@davidfowl Thanks for your QueryInterceptor package!

@dotus-git
Copy link

thanks for this on many levels

@MrCsabaToth
Copy link

About how to use this to provide case insensitive search fr Mvc.Jquery.Datatables: mcintyre321/mvc.jquery.datatables#44

@StefH
Copy link

StefH commented Apr 9, 2016

I guess the string "asdf" is just a dummy string and can be anything?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment