Last active
April 25, 2018 21:45
-
-
Save forgetaboutit/469aeadd5ba69f6e10026a08f4049e58 to your computer and use it in GitHub Desktop.
LINQKit.Core vs. Custom Splice
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
// Code from the post: https://balefrost.github.io/expression_splicing.html | |
public static class ExpressionSplicer | |
{ | |
public static TResult Inline<T1, TResult>(this Expression<Func<T1, TResult>> substitutionExpr, T1 value1) | |
{ | |
throw new Exception( | |
"This method isn't meant to be called; it's meant to appear inside a template expression passed to Splice"); | |
} | |
} | |
class ParameterReplacer : System.Linq.Expressions.ExpressionVisitor | |
{ | |
private readonly Dictionary<ParameterExpression, Expression> _formalParameterSubstitutions; | |
public ParameterReplacer(Dictionary<ParameterExpression, Expression> formalParameterSubstitutions) | |
{ | |
_formalParameterSubstitutions = formalParameterSubstitutions; | |
} | |
protected override Expression VisitParameter(ParameterExpression node) | |
{ | |
if (_formalParameterSubstitutions.TryGetValue(node, out var actualValue)) | |
{ | |
return actualValue; | |
} | |
return base.VisitParameter(node); | |
} | |
} | |
public static Expression<Func<T1, TResult>> Splice<T1, TResult>( | |
Expression<Func<T1, TResult>> templateExpr) | |
{ | |
return new SpliceRewriter().VisitAndConvert(templateExpr, "Splice"); | |
} | |
class SpliceRewriter : System.Linq.Expressions.ExpressionVisitor | |
{ | |
protected override Expression VisitMethodCall(MethodCallExpression node) | |
{ | |
if (node.Method.DeclaringType == typeof(ExpressionSplicer) && node.Method.Name == "Inline") | |
{ | |
// `node.Arguments[0]` (i.e. the `this` parameter to `Inline` will be the substitution expression | |
// and the remainder of `node.Arguments` will be the arguments we want to provide to that | |
// substitution expression. | |
LambdaExpression expressionToInsert = | |
Expression.Lambda<Func<LambdaExpression>>(node.Arguments[0]).Compile()(); | |
var substitutionArguments = node.Arguments.Skip(1); | |
var parameterArgumentPairs = expressionToInsert.Parameters | |
.Zip(substitutionArguments, (parameter, argument) => new { parameter, argument }); | |
var substitutions = parameterArgumentPairs.ToDictionary(x => x.parameter, x => x.argument); | |
var parameterReplacer = new ParameterReplacer(substitutions); | |
return parameterReplacer.Visit(expressionToInsert.Body); | |
} | |
return base.VisitMethodCall(node); | |
} | |
} | |
Expression<Func<T, bool>> MakeContainsExpression<T>( | |
IQueryable<int> intermediateResults, | |
Expression<Func<T, int>> substitutionExpr) | |
{ | |
return Splice((T x) => intermediateResults.Contains(substitutionExpr.Inline(x))); | |
} | |
// End of code from the post | |
// Expand-Equivalent: | |
// Please kindly include LinqKit.Core in your LINQPad-Script or paste | |
// the code of its ExpressionVisitor from Github here | |
Expression<Func<T, bool>> MakeContainsExpressionExpand<T>( | |
IQueryable<int> intermediateResults, | |
Expression<Func<T, int>> substitutionExpr) | |
{ | |
Expression<Func<T, bool>> expr = (T x) => intermediateResults.Contains(substitutionExpr.Invoke(x)); | |
return expr.Expand(); | |
} | |
void Main() | |
{ | |
var queryable = | |
Enumerable.Range(1, 10). | |
AsQueryable(); | |
var containsExprSplice = | |
MakeContainsExpression<int>( | |
queryable, | |
i => i * 5); | |
var containsExprExpand = | |
MakeContainsExpressionExpand<int>( | |
queryable, | |
i => i * 5); | |
// In my LINQPad output, those result in the same expression | |
containsExprSplice.Dump(); | |
containsExprExpand.Dump(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment