Last active
January 29, 2021 12:08
-
-
Save holyqqwqqasd/7ace632986e32e1114c9739ce086659d to your computer and use it in GitHub Desktop.
This file contains hidden or 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() | |
{ | |
Expression<Func<Test, Test>> f = x => x; | |
var r = new Test[0].AsQueryable().Append(new Test(444)).Select(q => new { A = q.X.ToString(), F = f.Apply(q).Double() }).Expand(); | |
r.Expression.ToString().Dump(); | |
r.ToArray().Dump(); | |
var query2 = new[] { new { A = 10, B = new[] { "Hello", "Worlddd" } } }.AsQueryable(); | |
Expression<Func<int, int>> fa1 = x => x * 1; | |
var r2 = query2.Select(x => new | |
{ | |
fal = fa1.Apply(x.A), | |
fas = Foo.fa2.Apply(x.A), | |
fae = Foo.fa3.Apply(x.A) | |
}).Expand(); | |
r2.Expression.ToString().Dump(); | |
r2.ToArray().Dump(); | |
} | |
class Foo | |
{ | |
public static Expression<Func<int, int>> fa2 = x => x * 2; // static field | |
public static Expression<Func<int, int>> fa3 => x => x * 3; // static property | |
} | |
struct Test | |
{ | |
public int X; | |
public int Double() => X * X; | |
public Test(int x) => X = x; | |
} | |
public static class Expr | |
{ | |
public static TResult Apply<TSource, TResult>(this Expression<Func<TSource, TResult>> expr, TSource source) | |
{ | |
// Replaced by ExpandVisitor | |
throw new NotImplementedException(); | |
} | |
public static Expression<Func<TSource, TResult>> Expand<TSource, TResult>( | |
Expression<Func<TSource, TResult>> expr) | |
{ | |
var newExpr = ExpandVisitor.Instance.Visit(expr) ?? throw new InvalidOperationException(); | |
return (Expression<Func<TSource, TResult>>)newExpr; | |
} | |
public static IQueryable<T> Expand<T>(this IQueryable<T> query) | |
{ | |
var result = ExpandVisitor.Instance.Visit(query.Expression) ?? throw new InvalidOperationException(); | |
return (IQueryable<T>)query.Provider.CreateQuery(result); | |
} | |
} | |
public class ExpandVisitor : ExpressionVisitor | |
{ | |
public static readonly ExpandVisitor Instance = new ExpandVisitor(); | |
private ExpandVisitor() | |
{ | |
} | |
protected override Expression VisitMethodCall(MethodCallExpression node) | |
{ | |
if (node.Method.DeclaringType == typeof(Expr) && | |
node.Method.Name == nameof(Expr.Apply)) | |
{ | |
var memberNode = (MemberExpression)node.Arguments[0]; | |
var expr = (memberNode.Expression as ConstantExpression)?.Value; | |
var func = memberNode.Member switch | |
{ | |
FieldInfo field => (LambdaExpression)field.GetValue(expr), | |
PropertyInfo property => (LambdaExpression)property.GetValue(expr), | |
_ => throw new InvalidOperationException() | |
}; | |
var allParams = func.Parameters | |
.Zip(node.Arguments.Skip(1), (a, b) => (a, b)) | |
.ToArray(); | |
return new CollectionParameterReplaceVisitor(allParams).Visit(func.Body) ?? | |
throw new InvalidOperationException(); | |
} | |
var args = node.Arguments.Select(Instance.Visit); | |
return (node, node.Object) switch | |
{ | |
({ }, null) => Expression.Call(node.Method, args), | |
(_, { }) => Expression.Call(Instance.Visit(node.Object), node.Method, args), | |
_ => throw new InvalidOperationException() | |
}; | |
} | |
} | |
private class CollectionParameterReplaceVisitor : ExpressionVisitor | |
{ | |
private readonly (ParameterExpression, Expression)[] _parameters; | |
public CollectionParameterReplaceVisitor((ParameterExpression, Expression)[] parameters) | |
{ | |
_parameters = parameters; | |
} | |
protected override Expression VisitParameter(ParameterExpression node) => | |
_parameters.Any(x => x.Item1 == node) | |
? _parameters.First(x => x.Item1 == node).Item2 | |
: node; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment