0

I have built a predicate by the Address model (Expression<Func<Address, bool>> addressPred). Now i want to combine this with a predicate of the Person model (Expression<Func<Person, bool>> personPred). Example of the models are:

public class Person
{
    public string Name { get; set; }
    public Address Address { get; set; }
}
public class Address
{
    public string StreetName { get; set; }
}

If I do something like

addressPred = addressPred.And(a => a.StreetName == "Foo bar");

and then combine it with the personPred, the combined predicate will have a statement equalent to

combinedPred = combinedPred.And(a => a.Address.StreetName == "Foo bar");

The combinedPredicate is of type Person.
How can I achive this?

Edit: In reality the models are alot bigger and are shortened for simplicity's sake. What I ultimately want to achive is to build the Address predicate once, then build a predicate for Person with its own conditions and then combine it into a predicate where the Address-part of the Person predicate comes from the Address predicate (Address is a property of Person). The reason I want to do it this way is because Address may have alot of conditions, and I want to use it as a part of other predicates (Person, later also Company, Customer etc) before making a db-call (to Person, Company, Customer etc)
Also the combinedPred line of code was only to show what the equalent statement of the combinedPred would be like coming from Address predicate.

Sindre
  • 35
  • 6
  • 1
    It is not at all clear what you're asking. Where does the `personPred` fit into all of this? The `combinedPred` doesn't appear to use it, and has its own logic for getting an `Address` object. Please improve your question so it makes sense. – Peter Duniho Jan 03 '18 at 09:38
  • 3
    You would need to wrap up your address-related predicate in a person-related one, such as `p => addressPred(p.Address)`. Also, make sure you post the actual *types* of things, as well as an example of what you want to accomplish. Right now there's a lot of unknown types and handwaving so it's a bit unclear **exactly** what you're asking. – Lasse V. Karlsen Jan 03 '18 at 09:38
  • 1
    Note: the suggestion above by @Lasse is syntactically correct, but does not make use of any `Expression>`, i.e. a `Person` predicate. A predicate, by definition, returns a `bool` value. There is nothing in your question that explains what you mean by "combine" such that the final predicate only has a single Boolean expression and doesn't use any predicate that takes a `Person` at all. – Peter Duniho Jan 03 '18 at 09:41
  • I have edited the question. Hope it is now more clear what I am asking. – Sindre Jan 03 '18 at 10:19

1 Answers1

3

The situation is that the built Expression<Func<Address, bool>> addressPred has an address as ParameterExpression and it is requred to apply the expression to a MemberExpression. So I can suggest to replace the ParameterExpression with the MemberExpression like that:

var personParameter = Expression.Parameter(typeof(Person), "person");
var addressProperty = Expression.PropertyOrField(personParameter, "Address");        
var combined = new ReplaceVisitor<Address>(addressProperty).Visit(addressPred.Body);
var result = Expression.Lambda<Func<Person, bool>>(combined, personParameter);

Where:

public class ReplaceVisitor<T> : ExpressionVisitor
{
    private readonly MemberExpression _replacement;

    public ReplaceVisitor(MemberExpression replacement)
    {
        _replacement = replacement;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return node.Type.IsAssignableFrom(typeof(T))
                ? _replacement : base.VisitParameter(node);
    }
}
ASpirin
  • 3,601
  • 1
  • 23
  • 32