Click here to Skip to main content
15,881,204 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'm working with Entity Framework and need to create filters dynamically in run time.
I need to create an expression which should be able to access parameter of a parent call, e.g.:
x => x.Roles.Select(y => y.Actions.Where(z => !x.IsActive || z.IsActive))

so that this filter should select _all_ Actions for a root object, if that root object is _not_ active, or select all _active_ Actions, if that object is active.

I've code which creates an expression of such kind, but it is not clear how to get access to x parameter from within the Where filter.
Since Where filter is represented by a separate expression, which accepts only z parameter, it doesn't have x bound.


What should be done to get that parameter available on that level ?

Thanks,
Alex

What I have tried:

I can't put here an example of the code, because it is not simple, since it dynamically calculates types of objects ivolved into expressions.
It's just based on regular approach but working through reflection.
Posted
Updated 26-Jul-18 6:52am
Comments
Nathan Minier 25-Jul-18 14:41pm    
I think you've got a more fundamental issue if your design is allowing your role container to provide an "Active" override to a specific actions. Properties should have meaning; it might be of benefit to re-evaluate the structure.

At any rate, the functionality you're looking for is better served by nested loops rather than by an expression tree.
Alex-1978 25-Jul-18 15:37pm    
Thanks for your comment.
Could you please clarify, which nested loops are you talking about ?
[no name] 26-Jul-18 3:27am    
As far as I can tell, "x" is "in scope" (or that's what my tests tell me).

The inner lambda expression will have access to the parameters from the outer lambda expression:
C#
var x = Expression.Parameter(typeof(Widget), "x");
var y = Expression.Parameter(typeof(Role), "y");
var z = Expression.Parameter(typeof(RoleAction), "z");

// z => !x.IsActive || z.IsActive
var roleActionBody = Expression.OrElse(
    Expression.Not(Expression.Property(x, "IsActive")),
    Expression.Property(z, "IsActive"));

// y => y.Actions.Where(z => ...)
var roleBody = Expression.Call(typeof(Enumerable), "Where", new[] { typeof(RoleAction) },
    Expression.Property(y, "Actions"),
    Expression.Lambda(roleActionBody, z));

// x => x.Roles.Select(y => ...)
var body = Expression.Call(typeof(Enumerable), "Select", new[] { typeof(Role), typeof(IEnumerable<RoleAction>) },
    Expression.Property(x, "Roles"),
    Expression.Lambda(roleBody, y));

var result = Expression.Lambda<Func<Widget, IEnumerable<IEnumerable<RoleAction>>>>(body, x);

NB: The result is a sequence of sequences of RoleAction objects. If you want to flatten that to a sequence of RoleAction objects, you'll need to use SelectMany:
C#
...

// x => x.Roles.SelectMany(y => ...)
var body = Expression.Call(typeof(Enumerable), "SelectMany", new[] { typeof(Role), typeof(RoleAction) },
    Expression.Property(x, "Roles"),
    Expression.Lambda(roleBody, y));

var result = Expression.Lambda<Func<Widget, IEnumerable<RoleAction>>>(body, x);
 
Share this answer
 
Comments
Alex-1978 27-Jul-18 5:00am    
Thanks for your comment! It gave me a very important tip.

It was just necessary to add replacement of 'x' parameter in the body of roleActionBody.
I had only made replacement in the parameters of that expression.

I'm a newbie in expression tree, so it was not obvious for me :)

Thanks a lot!
"x" is in scope.

class RoleAction {
   public bool IsActive;
}

class Role {
   public List<RoleAction> Actions = new List<RoleAction>();
}

class Widget {
   public List<Role> Roles = new List<Role>();
   public bool IsActive;
}

class Program {
   static void Main( string[] args ) {
      List<Widget> widgets = new List<Widget>();
      var q = widgets.Select( x => x.Roles.Select( y => y.Actions.Where( z => !x.IsActive || z.IsActive ) ) );
   }
}
 
Share this answer
 
Comments
Alex-1978 26-Jul-18 3:44am    
Yeah, this is what I'm trying to do in run-time using Expression class.
I'm creating expressions for each step:

Expression<Func<Action, bool>> expr1 = z => !x.IsActive || z.IsActive;
Expression<Func<Role, IEnumerable<action>>> expr2 = y => y.Actions.Where(...)
Expression<Func<Widget, IEnumerable<action>>> expr3 => x => x.Roles ...

but this code is not written directly in C#, it is being created on the fly and the issue is that parameter 'x' from expr3 is not bound to the context of call of expr1.
I'm using ExpressionVisitor to replace parameters in the final expression, but since expr1 is not accepting 'x' as a parameter, it doesn't have access to it.

The question really is, which mechanism must be used to pass 'x' into expr1 ?
Is it a closure ? If yes, is it possible to create a clouse for a node in the expression tree ?
I've found no information about it till now.
[no name] 26-Jul-18 17:14pm    
Func<bool> GetX() { return !x.IsActive; // Or whatever. }

Expression<Func<Action, bool>> expr1 = z => GetX() || z.IsActive;

(Then again, you should reconsider evaluating a "parent" property at the child level if performance is a consideration since "select all" will sometimes be more appropriate).

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900