Last active
October 4, 2019 00:02
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
thanks for this on many levels
About how to use this to provide case insensitive search fr Mvc.Jquery.Datatables: mcintyre321/mvc.jquery.datatables#44
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
@davidfowl Thanks for your QueryInterceptor package!