0

I started to use expressions to encapsulate business logic and be able to pass it to EF directly. However I can't figure out how to "nicely" combine and/or reuse expressions in another expressions. For example, I have the following 2 business rules for valid products and clearance products:

internal static Expression<Func<Product, bool>> fnValidProduct = (p) =>
        p.DoNotDisplayProductsOnSite == false &&
        p.CategoryId != null
        
internal static Expression<Func<Product, bool>> fnClearanceProduct = (p) => 
        (p.ProductFlags & ProductFlags.Clearance) > 0 && p.Qty > 3;

However, I cannot figure out how to make a rule for "valid clearance products" using these building block. Code below won't compile for expressions:

internal static Expression<Func<Product, bool>> fnValidClearanceProduct = (p) =>
        fnValidProduct(p) &&
        fnClearanceProduct(p);

I've looked into this sometime ago, and I even recovered some codes which I can't really understand now - which really defies the purpose of combining expressions:

internal static Expression<Func<ProductContainer, bool>> fnValidProduct
{
    get
    {
        var parameterExpression = Expression.Parameter(typeof(ProductContainer));
        var propertyOrField = Expression.PropertyOrField(parameterExpression, nameof(ProductContainer.product));
        var combined = Expression.Lambda<Func<ProductContainer, bool>>(Expression.Invoke(fnValidProduct, propertyOrField),
            parameterExpression);
        return combined.Expand();
    }
}

Question is then - is there a way or NuGet libraries or something - to achieve that "code reuse"?

avs099
  • 10,937
  • 6
  • 60
  • 110
  • 1
    Well, `fnValidProduct` takes a `Product` as the parameter, and `fnClearanceProduct` is expecting to be passed a `ModelsPrice`, which is why they can't be combined. – stuartd Sep 30 '20 at 22:28
  • Just speculating, but perhaps `internal static Expression> fnValidClearanceProduct = (p, mp) => fnValidProduct(p) && fnClearanceProduct(mp);`? – stuartd Sep 30 '20 at 22:30
  • Have you looked at *Predicate Builder*? http://www.albahari.com/nutshell/predicatebuilder.aspx – Flydog57 Sep 30 '20 at 22:31
  • @stuartd that was a typo, i was simplifying real code. It's all Products – avs099 Sep 30 '20 at 22:40
  • 1
    Have you tried `Expression.And(expr1.Body, expr2.Body)` ? – John Wu Sep 30 '20 at 22:47
  • Expressions are not delegates. `fnValidProduct(p) && fnClearanceProduct(p)` can work only if you compile the original expressions into delegates. That said, the Expression API has all the compositional features needed to fully support all possible language expressions. The terms _"easy"_ and _"visual"_ aren't precise, but it seems to me that calling `Expression.And()` or similar is both easy and expressive (which is what I take _"visual"_ to mean). – Peter Duniho Sep 30 '20 at 23:11
  • @JohnWu As is explained in (some of) the duplicates, it's not quite that easy. You need to replace all instances of the parameters with the new parameter for the lambda you're creating, else it just fails at runtime. – Servy Oct 01 '20 at 00:35

0 Answers0