I want to create a dynamic expression which then passed to Entity Framework like in the example:
EXAMPLE:
The user could select a dynamic list of columns.
Expression<Func<EntryLine,object>> groupingConcatenated
Func<EntryLine,object> grouping1 = x => new {x.Office.Name}
If(user selected office){
groupingConcatenated.Append(grouping1)
}
Func<EntryLine,object> grouping2 = x => new {x.Industry.Name}
If(user selected industry){
groupingConcatenated.Append(grouping2)
}
groupingConcatenated should be an anonimousObject with both navigations:
groupingConcatenated = {
new {x.Office.Name},
new {x.Industry.Name}
}
And now I want to call the context with groupingConcatenated which is build by user preference.
_context.Something.GroupBy(groupingConcatenated)
also the .Select after GroupBy I also want to build it dynamical.
I want to do that because the user can add columns, can choose only 2 of 10 and so on. I said that I want to append Func, but if it not possible I want to append Expressions.
The idea is to build the groupBy parameter conditionally.
We want to create a dynamic answer with EntityFramework, and I don't know also which is the input of the api endpoint. For example the UI will send me 4 columns which it want from the EntryLine class, and which are the columns after it want to call the groupBy. I want to build the GroupBy expression and also the after the to Build a Select expression dynamic, but if somebody help me to build the groupBy the select will work the same.
In Raw SQL this is very simple to build dynamic query, but it is very hard to maintain it. And I want to find a solution in .net with EF core. We use the latest tech.
Is it possible to merge 2 expressions or 2 functions then passed to a expression which will return an anonimous object? because all the examples are on fixed types like bool
I also have this code:
public class ExpressionMerger : ExpressionVisitor
{
Expression currentParameterExpression { get; set; }
public Expression<Func<Tin, Tout>> Merge<Tin, Ta, Tout>(Expression<Func<Tin, Ta>> inner, Expression<Func<Ta, Tout>> outer)
{
return Mergeall<Tin, Tout>(inner, outer);
}
public Expression<Func<Tin, Tout>> Merge<Tin, Ta, Tb, Tout>(Expression<Func<Tin, Ta>> inner, Expression<Func<Ta, Tb>> transition, Expression<Func<Tb, Tout>> outer)
{
return Mergeall<Tin, Tout>(inner, transition, outer);
}
protected Expression<Func<Tin, Tout>> Mergeall<Tin, Tout>(params LambdaExpression[] Expressions)
{
currentParameterExpression = Expressions[0].Body;
foreach (var Expression in Expressions.Skip(1))
{
currentParameterExpression = Visit(Expression.Body);
}
return Expression.Lambda<Func<Tin, Tout>>(currentParameterExpression, Expressions[0].Parameters[0]);
}
protected override Expression VisitParameter(ParameterExpression node)
{
//replace current lambda parameter with ~previous lambdas
return currentParameterExpression;
}
}
and also this code:
public static class PredicateBuilder
{
// Creates a predicate that evaluates to true.
public static Expression<Func<T, bool>> True<T>() { return param => true; }
// Creates a predicate that evaluates to false.
public static Expression<Func<T, bool>> False<T>() { return param => false; }
// Creates a predicate expression from the specified lambda expression.
public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }
// Combines the first predicate with the second using the logical "and".
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
// Combines the first predicate with the second using the logical "or".
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.OrElse);
}
// Negates the predicate.
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
{
var negated = Expression.Not(expression.Body);
return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
}
// Combine 2 expressions.
public static Expression<Func<T, object>> Merge<T>(this Expression<Func<T, object>> first, Expression<Func<T, object>> second)
{
return first.Compose(second, Expression.Add);
}
// Combines the first expression with the second using the specified merge function.
static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
// zip parameters (map from parameters of second to parameters of first)
var map = first.Parameters
.Select((f, i) => new { f, s = second.Parameters[i] })
.ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with the parameters in the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
// create a merged lambda expression with parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
class ParameterRebinder : ExpressionVisitor
{
readonly Dictionary<ParameterExpression, ParameterExpression> map;
ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
}
}
}
