我的代码库中有一些相当复杂的实体框架查询,我决定将逻辑集中到模型中。基本上,描绘了一堆控制器,它们在表达式树中有大量查询和大量重复代码。因此,我取出了部分表达树并将它们移动到模型中,从而减少了重复次数。
例如,假设我经常需要获取名为Entity的模型,这些模型处于Not Deleted状态。在我的实体模型上,我有:
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;
(这是一个较小的例子,主要是在尝试检查数据之前检查有效数据。)
使用它看起来像:
var entities = EntityRepository.Entities.Where(Entity.IsNotDeleted).Where(...
然而,我发现虽然有时候我想要的是没有删除的记录,但有时我想要删除 的记录。要做到这一点,有没有办法从消费代码中反转逻辑?概念上类似于此的东西(显然不起作用):
var entities = EntityRepository.Entities.Where(!Entity.IsDeleted).Where(...
我不希望在对象上有两个Func<>
,一个用于IsDeleted
,一个用于IsNotDeleted
几乎相同。 Func<>
返回bool
,是否有一种语法可以在将其放入.Where()
子句时调用它的反转?
答案 0 :(得分:12)
考虑以下扩展方法。
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()
与Entity.IsNotDeleted()
相同。
请注意,您可能希望使用Expression<Func<T, bool>>
- 而不是Func<T, bool>
- 这样您的lambda逻辑可以用于数据库端而不是客户端。
你可以像这样使用它:
Expression<Func<int, bool>> isNegative = x => x < 0;
Expression<Func<int, bool>> isNonNegative = isNegative.Not();
答案 1 :(得分:1)
您不需要完全功能性的lambda decalration。去Church-Turing方式 - Is Not Deleted定义的递归:
public static Func<Entity, bool> IsNotDeleted = e => !IsDeleted(e);
上面的回答更像是“Church-Turing-y”:)