Skip to content

Instantly share code, notes, and snippets.

@holyqqwqqasd
Last active January 29, 2021 12:08
Show Gist options
  • Save holyqqwqqasd/7ace632986e32e1114c9739ce086659d to your computer and use it in GitHub Desktop.
Save holyqqwqqasd/7ace632986e32e1114c9739ce086659d to your computer and use it in GitHub Desktop.
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