问题
I am trying to refactor some code for a generic repository, that passes in a filter object that will filter data, as well as page, sort etc.
Each inheriting Filter (Such as CustomerFilter) has the option of defining its own Expression Filter which will get applied by the base class in the repository.
So the customer filter will have properties like this:
public string CustomerId { get; set; }
public override Expression<Func<object, bool>> Predicate => c => ((Customer)c).Id == CustomerId;
Then the repository will run the filter in the repository, a bit like this (it's not generic yet!):
using (var context = new CustomerContext())
{
      return await Filter<Domain.Customer>.ApplyAsync(filter, context.Customers.AsQueryable()).ConfigureAwait(false);
}
This works ok, but I need a way to build the expression in a better way for more complex examples.
For example, the filter may allow to filter the customers on the state, but only if its set.
public string CustomerId { get; set; }
public State? CustomerState { get; set; }
public override Expression<Func<object, bool>> Predicate => c => (((Customer)c).Id == CustomerId) && (((Customer)c).State == CustomerState ?? (Customer)c).State);
This not only becomes a mess, but also there's a lot of unnecessary casting and parenthesis. So what I'd like to do is an expression builder in the getter, that would build the expression in a cleaner way, such as if(State != null) { CustomerState == State; }. But that's where I'm not sure how to proceed, so if anyone can help me out, I'd appreciate it. 
回答1:
If you want to combine multiple "Conditions" to apply to Where clause you can use PredicateBuilder from LinqKit library
Here is an example to combine two conditions with "Or" clause
System.Linq.Expressions.Expression<Func<Domain.Question, bool>> codition1 = e => e.CategoryId == 1;
System.Linq.Expressions.Expression<Func<Domain.Question, bool>> condition2 = e => e.CategoryId == 2;
var combinedCondition = LinqKit.PredicateBuilder.Or(codition1, condition2);
//using combined condition in where clause....
queryable = queryable.Where(combinedCondition);
You can use other methods of PredicateBuilder class such as "And" to get the combined condition you want...
回答2:
You can combine expressions with Linq Expressions API:
public Expression<Func<Customer, bool>> BuildExpression()
{
    Expression<Func<Customer, bool>> predicate = c => c.Id == CustomerId;
    if (State != null)
    {
        var parameter = predicate.Parameters.First();
        var newBody = Expression.AndAlso(
            predicate.Body,
            Expression.Equal(
                Expression.PropertyOrField(parameter, nameof(State)),
                Expression.Constant(State)
            ));
        predicate = Expression.Lambda<Func<Customer, bool>>(newBody, parameter);
    }
    return predicate;
}
In code above predicate is a base expression that will be used if State is null. But when State is set we extract expression parameter and add && c.State == State to predicate body
来源:https://stackoverflow.com/questions/56663177/expressionfuncobject-bool-as-property