ridermansb
ridermansb

Reputation: 11059

Dynamic expression for filter IQueryable

I have a situation where I have just the property name (string) and the value by which desire filter. I need to filter the list to return true if any name repeated.

Remembering that the filter should exclude it yourself. With expression like: p => p.Id != currentId

Code

As the property is a string, I decided to create a Dynamic Expression to solve this problem.

[TestMethod]
public void TestingExpression()
{
    // Arrange
    var fix = new Fixture();
    var list = fix.Build<User>()
                    .With(p => p.Name)
                    .OmitAutoProperties()
                    .CreateMany(20).AsQueryable(); // Create IQueryable<User> with 20 users

    // Act 
    var currentId = 2;
    var uniquePropName = "Name";
    var uniqueValue = "{NotFoundValue}";

    // Expression: p => p.Id != currentId
    ParameterExpression pId = Expression.Parameter(typeof(int), "Id");
    ConstantExpression cId = Expression.Constant(currentId, typeof(int));
    BinaryExpression notCurrent = Expression.NotEqual(pId, cId);
    Expression<Func<int, bool>> NotCurrentExpr =
        Expression.Lambda<Func<int, bool>>(
            notCurrent,
            new ParameterExpression[] { pId });

    // Expression: p.{uniquePropName} == {uniqueValue}
    ParameterExpression pUnique = Expression.Parameter(typeof(string), uniquePropName);
    ConstantExpression cUnique = Expression.Constant(uniqueValue, typeof(string));
    BinaryExpression checkUnique = Expression.Equal(pUnique, cUnique);
    Expression<Func<string, bool>> CheckUniqueExp =
        Expression.Lambda<Func<string, bool>>(
            checkUnique,
            new ParameterExpression[] { pUnique });


    var exp = Expression.And(NotCurrentExpr, CheckUniqueExp);

    // Asset
    list.Provider.CreateQuery<User>(exp).ToList()
            .Should().HaveCount(19);

}

Question

The goal is to create a dynamic expression as: query.Any(p => p.Id != id && p.{Dynamic} == nome);

But I do not know how to continue ..

Thanks

Upvotes: 2

Views: 4577

Answers (1)

user1914530
user1914530

Reputation:

A nice tutorial can be found here.

You seem to be confusing the parameters and properties. Here is a further example:

//user => user.SomeProperty == someValue

//the parameter of the predicate, a User object in your case 
ParameterExpression parameter = Expression.Parameter(typeof(User), "user");

//the property of the user object to use in expression
Expression property = Expression.Property(parameter, myPropertyNameString);

//the value to compare to the user property
Expression val = Expression.Constant(myValueToCompare);

//the binary expression using the above expressions
BinaryExpression expEquals = Expression.Equal(property, val);

//create the Expression<Func<T, Boolean>>
var predicate = Expression.Lambda<Func<User, Boolean>>(expEquals, parameter);

Furthermore in your code you try and create two lambdas whereas your aim is to produce

p => p.Id != id && p.{Dynamic} == nome

This is a single lambda that takes 1 parameter with the body consisting of two binary expressions and a conditional AND operator producing a Boolean result. You should instead use Expression.AndAlso() to apply the && operator to your binary expressions and then use Expression.Lambda<Func<User, Boolean>>() to get your final lambda expression.

Upvotes: 1

Related Questions