Search code examples
c#expression-trees

Extending an expression<func<t, bool>>


It has been a long while since I have delved into expression trees and I am struggling with the following problem.

Basically I want to take the following Expression<Func<T, TIdType>>:

(a) => EF.Property<TIdType>(a, "TenantId")

and extend it to become this Expression<Func<T, bool>:

(a) => EF.Property<TIdType>(a, "TenantId").Equals(TenantId)

So basically I want to take the original expression and add on .Equals(TenantId)

For the background to this issue, its all due to me attempting to workaround an issue in EF Core 2.0 as raised here:

https://github.com/aspnet/EntityFrameworkCore/issues/11456

The following hopefully shows what I am attempting in more detail:

public class FooEntity
{
    public Guid TenantId { get; set; }
}

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestMethod1()
    {

        var adaptor = new TenantFilterExpressionAdaptor<FooEntity, Guid>();
        var tenantIdFilter = adaptor.Adapt((a) => EF.Property<Guid>(a, "TenantId"));

    }
}

public class TenantFilterExpressionAdaptor<T, TIdType>
{

    public TIdType TenantId { get; set; }

    public Expression<Func<T, bool>> Adapt(Expression<Func<T, TIdType>> idExpression)
    {

        // Tack on .Equals(TenantId) to the expression!?

        // assume I have to add nodes to the body?
        // idExpression.Body?

    }

}

Solution

  • You have to build new expression, because expressions are immutable. In this case you can reuse body and parameters of existing expression for the new one:

    public class TenantFilterExpressionAdaptor<T, TIdType> {
    
        public TIdType TenantId { get; set; }
    
        public Expression<Func<T, bool>> Adapt(Expression<Func<T, TIdType>> idExpression) {
            return Expression.Lambda<Func<T, bool>>(
                Expression.Equal(idExpression.Body, Expression.Constant(TenantId)),
                idExpression.Parameters);
        }
    }
    

    You mentioned in comments that constant is problematic for you. In this case you can reference property itself and not its value:

    return Expression.Lambda<Func<T, bool>>(
        Expression.Equal(idExpression.Body, Expression.Property(Expression.Constant(this), nameof(TenantId))),
        idExpression.Parameters);
    

    This will be expression like:

    x => x.TenantId == this.TenantId
    

    where this is instance of your adapter.