Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

every Parameter object property that is not null, to be added to expression predicate as a condition

I am looking for a way to dynamically build an expression, based upon the method parameters.

This is the code snippet from my service method, where I would like to build a predicate expression.

    public async Task<Account> GetCustomerAccountsAsync(Parameters params)
    {
        var items = await _unitOfWork.Accounts.GetWhereAsync(a => a.CustomerId == params.CustomerId && ... );
        ...
    }

GetWhereAsync is a method from the generic repository that looks like:

    public async Task<IEnumerable<TEntity>> GetWhereAsync(Expression<Func<TEntity, bool>> predicate)
    {
        return await Context.Set<TEntity>().Where(predicate).ToListAsync();
    }

And Parameters class:

    public class Parameters
    {
        public string CustomerId { get; set; }
        public string AccountId { get; set; }
        public string ProductId { get; set; }
        public string CurrencyId { get; set; }
    }

What I would like to implement, is that every Parameter object property that is not null, to be added to expression predicate as a condition.

For example, if CustomerId and AccountId have some values, I would like to dynamically build predicate expression that would have functionality same as the following predicate:

    var items = await _unitOfWork.Accounts.GetWhereAsync(a => a.CustomerId == params.CustomerId && a.AccountId == params.AccountId);

Appreciate any help.

like image 647
Vladimir Avatar asked Mar 10 '20 15:03

Vladimir


2 Answers

You don't need to use Expressions to build something dynamically here. You can do something like this:

_unitOfWork.Accounts.Where(a =>
    (params.CustomerId == null || a.CustomerId == params.CustomerId) &&
    (params.AccountId == null || a.AccountId == params.AccountId) &&
    (params.ProductId == null || a.ProductId == params.ProductId) &&
    (params.CurrencyId == null || a.CurrencyId == params.CurrencyId)
);

This is how I've done queries before for a search form with multiple optional search parameters.

like image 126
Valuator Avatar answered Oct 24 '22 10:10

Valuator


I'm currently working a lot with Expressions so I think I can help you.

I just built a custom code for you.

The code accepts that you add Properties to your filtered class (Account) without having to change the filter building code.

The code filters string Properties and use it to create the Predicate.

public Func<Account, bool> GetPredicate(Parameters parameters)
{
    var stringProperties = typeof(Parameters)
    .GetProperties(BindingFlags.Public | BindingFlags.Instance)
    .Where(x => x.PropertyType == typeof(string));

    var parameterExpression = Expression.Parameter(typeof(Account));

    var notNullPropertyNameToValue = new Dictionary<string, string>();

    BinaryExpression conditionExpression = null;

    foreach (var stringProperty in stringProperties)
    {
        var propertyValue = (string)stringProperty.GetValue(parameters);
        if (propertyValue != null)
        {
            var propertyAccessExpression = Expression.PropertyOrField(parameterExpression, stringProperty.Name);
            var propertyValueExpression = Expression.Constant(propertyValue, typeof(string));
            var propertyTestExpression = Expression.Equal(propertyAccessExpression, propertyValueExpression);

            if (conditionExpression == null)
            {
                conditionExpression = propertyTestExpression;
            }
            else
            {
                conditionExpression = Expression.And(conditionExpression, propertyTestExpression);
            }
        }
    }

    //Just return a function that includes all members if no parameter is defined.
    if (conditionExpression == null)
    {
        return (x) => true;
    }

    var lambdaExpression = Expression.Lambda<Func<Account, bool>>(conditionExpression, parameterExpression);
    return lambdaExpression.Compile();
}

It returns a typed predicate that you can use in Linq for example.

See this example :

void Main()
{
    var customers = new List<Account>()
    {
        new Account()
        {
            CustomerId = "1",
        },
        new Account()
        {
            CustomerId = "2",
        }
    };
    var predicate = GetPredicate(new Parameters() { CustomerId = "1" });

    customers.Where(predicate);
}

If any help is needed, feel free to ask !

like image 4
Vasilievski Avatar answered Oct 24 '22 11:10

Vasilievski