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?
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();
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" :)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With