0

I have ParentItems on which security trimming should be applied. So there is a SecurityTrimmingExpressionFactory which will build up an Expression<Func<ParentItem, bool>> for the parent Element:

SecurityTrimmingExpressionFactory

public static Expression<Func<ParentItem, bool>> CreateTrimming(currentUser)
{
   return parentItem => parentItem.OwnerId == currentUser.Id;    
}

In my WebAPI Controller I want to receive child items of a parent element. The normal way I would write it like this

WebAPI Controller (Get)

return Entities.ChildItems.Where(c => c.parentId == <some id>);

no problem at all but no security trimming.

If I want to apply my security trimming here (Currently I am using LinqKit to build the expression tree with the parent item)

WebAPI Controller (Get) with trimming

var expr = TrimmingExpressionsFactory.CreateParentTrimming(this.CurrentUser);
return Entities.ChildItems
                       .AsExpandable()
                       .Where(childItem => expr.Invoke(childItem.ParentItem)
                       .Where(c => c.parentId == <some id>)
                );

everything is working fine.

Now the problem is that external libraries (like LinqKit) are forbidden in the current environment (In my opinion: bad decision ;) ). Is there another way without an external library to expand or invoke query expression with dependend Entities?

Already tried to solve this problem by using .AsEnumerable() (often found this as a workaround in the web) but this is not an acceptable solution for large data sets.

Martin
  • 3,096
  • 1
  • 26
  • 46

1 Answers1

1

You can use the little helper utility from my answer to Define part of an Expression as a variable in c#:

public static class ExpressionUtils
{
    public static Expression<Func<TOuter, TResult>> Bind<TOuter, TInner, TResult>(this Expression<Func<TOuter, TInner>> source, Expression<Func<TInner, TResult>> resultSelector)
    {
        var body = new ParameterExpressionReplacer { source = resultSelector.Parameters[0], target = source.Body }.Visit(resultSelector.Body);
        var lambda = Expression.Lambda<Func<TOuter, TResult>>(body, source.Parameters);
        return lambda;
    }

    public static Expression<Func<TOuter, TResult>> ApplyTo<TInner, TResult, TOuter>(this Expression<Func<TInner, TResult>> source, Expression<Func<TOuter, TInner>> innerSelector)
    {
        return innerSelector.Bind(source);
    }

    class ParameterExpressionReplacer : ExpressionVisitor
    {
        public ParameterExpression source;
        public Expression target;
        protected override Expression VisitParameter(ParameterExpression node)
        {
            return node == source ? target : base.VisitParameter(node);
        }
    }
}

The usage in your sample case would be:

var filter = TrimmingExpressionsFactory.CreateParentTrimming(this.CurrentUser)
    .ApplyTo((ChildItem childItem) => childItem.ParentItem);
return Entities.ChildItems
    .Where(filter)
    .Where(c => c.parentId == <some id>)
);
Community
  • 1
  • 1
Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343