Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logical Inverse of a Func<T, bool>

I have some fairly complex Entity Framework queries throughout my codebase and I decided to centralize the logic into the models. Basically, picture a bunch of controllers with large queries and lots of repeated code in their expression trees. So I took out parts of those expression trees and moved them to the models, allowing for less repetition.

For example, let's say I often need to fetch models called Entity which are in a state of Not Deleted. On my Entity model I have:

public static Func<Entity, bool> IsNotDeleted = e =>
    e.Versions != null ?
        e.Versions.OrderByDescending(v => v.VersionDate).FirstOrDefault() != null ?
            e.Versions.OrderByDescending(v => v.VersionDate).First().ActivityType != VersionActivityType.Delete :
            false :
       false;

(This is one of the smaller examples, mostly just checking for valid data before trying to examine that data.)

And using it would look like:

var entities = EntityRepository.Entities.Where(Entity.IsNotDeleted).Where(...

I'm finding, however, that while sometimes I want records which are not deleted, other times I want records which are deleted. To do that, is there a way to invert the logic from the consuming code? Something conceptually akin to this (which obviously doesn't work):

var entities = EntityRepository.Entities.Where(!Entity.IsDeleted).Where(...

I'd prefer not to have two Func<>s on the object, one for IsDeleted and one for IsNotDeleted which are nearly identical. The Func<> returns a bool, is there a syntax to call the inverse of it when putting it in a .Where() clause?

like image 327
David Avatar asked Jun 19 '14 21:06

David


2 Answers

Consider the following extension methods.

public static class Functional
{
    public static Func<T, bool> Not<T>(this Func<T, bool> f)
    {
        return x => !f(x);
    }

    public static Expression<Func<T, bool>> Not<T>(
        this Expression<Func<T, bool>> f)
    {
        // 1. Break the lambda f apart into its parameters and body.
        // 2. Wrap the body expression with a unary not expression (!).
        // 3. Construct a new lambda with the modified body.
        return Expression.Lambda<Func<T, bool>>(
            Expression.Not(f.Body), f.Parameters);
    }
}

Entity.IsDeleted.Not() is the same as Entity.IsNotDeleted().

Note you probably want to be using Expression<Func<T, bool>> - not Func<T, bool> - so that your lambda logic can be used database-side rather than client-side.

You can use it like this:

Expression<Func<int, bool>> isNegative = x => x < 0;
Expression<Func<int, bool>> isNonNegative = isNegative.Not();
like image 67
Timothy Shields Avatar answered Sep 25 '22 10:09

Timothy Shields


You don't need full blown functional lambda decalration. Go the Church-Turing way - reccursion for the IsNotDeleted definition:

public static Func<Entity, bool> IsNotDeleted = e => !IsDeleted(e);

Answer above is even more "Church-Turing-y" :)

like image 30
Wanted by FBI.... Sexually LOL Avatar answered Sep 24 '22 10:09

Wanted by FBI.... Sexually LOL