这个问题可以不用优化PredicateBuilder
,但需要仔细、“系统”的分析。
首先......考虑你的谓词制定决定因素
考虑一下你的情况,像这样把所有 14 个放在一起,你实际上可以看到你只有three决定因素,即:LocationId
, ActiveId
, and stats
:
No LocationId ActiveId stats result
1 not 0 1 don't care data.Where(x => x.LocationId == LocationId && x.IsActive == true && x.Removed == false)
2 not 0 2 don't care data.Where(x => x.LocationId == LocationId && x.IsActive == false && x.Removed == false));
3 not 0 not 0-2 don't care data.Where(x => x.LocationId == LocationId && x.Removed == false));
4 not 0 don't care all data.Where(x => x.LocationId == LocationId && x.IsActive == true && x.Removed == false)
5 not 0 don't care subscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.LocationId == LocationId));
6 don't care 1 all data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == true) || (x.Removed == false) || (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false)));
7 don't care 1 subscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == true));
8 don't care 1 unsubscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
9 don't care 2 all data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false) && (x.Removed == false)));
10 don't care 2 subscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
11 don't care 2 unsubscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
12 don't care don't care all data.Where(x => x.IsActive == true && x.Removed == false));
13 don't care don't care subscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == true));
14 don't care don't care unsubscribe data.Where(x => x.SubscribeDate >= DateTime.Now.AddDays(-7) && x.Removed == false && x.IsActive == false));
接下来,考虑结果的模式
我观察到你的结果是相当确定的,除了一些小例外。除了结果没有6
and no 9
,您的查询谓词实际上可以分为four基本组件(6
and 9
省略)。他们是:
comp1: x.LocationId == LocationId
comp2: x.IsRemoved == false
comp3: x.IsActive == true
comp4: x.SubscribeDate >= DateTime.Now.AddDays(-7)
查询逻辑很简单:
comp1 && comp2 && comp3 && comp4
将它们与12个案例放在一起(不包括案例)6
and 9
),你会得到:
Simplification:
DC = don't care
A = applied
NA = not applied
QueryComponents
No LocationId ActiveId stats comp1 comp2 comp3 comp4
1 not 0 1 DC A A Yes NA
2 not 0 2 DC A A No NA
3 not 0 not 0-2 DC A A NA NA
4 not 0 DC all A A Yes NA
5 not 0 DC subscribe A A NA A
7 DC 1 subscribe NA A Yes A
8 DC 1 unsubscribe NA A No A
10 DC 2 subscribe NA A No A
11 DC 2 unsubscribe NA A No A
12 DC DC all NA A Yes A
13 DC DC subscribe NA A Yes A
14 DC DC unsubscribe NA A No A
全部分解后,我们可以看到 Mapping
现在,可以看到查询组件可以与决定因素一起映射回:
comp1: Applied only when LocationId is not 0
comp2: Always applied //this is very good!
comp3: Yes = 1, 4, 7, 12, 13; NA = 3, 5; No = 2, 8, 10, 11, 14
comp4: Not Applied when LocationId is 0 except on case 5
您可以开始将概念转化为代码......
因此,我们可以做一些帮助flags(有 4 个)来确定是否应包含查询组件,如下所示:
bool LocationIdNotApplied = LocationId == 0; //for comp1
bool IsActiveNotApplied = LocationId != 0 && (ActiveId < 0 || ActiveId > 2 || stats = "subscribe"); //for comp3 to be applied or not
bool IsActiveFalse = (LocationId != 0 && ActiveId == 2) || stats == "unsubscribe" || (ActiveId == 2 && stats == "subscribe"); //for comp3 to be false
bool DateApplied = LocationId == 0 || (LocationId != 0 && stats == "subscribe");
那么你的data.Where
对于所有情况,除了6
and 9
可以这样简化:
data.Where(x => (x.LocationId == LocationId || LocationIdNotApplied) //comp1
&& x.IsRemoved == false //comp2
&& ((x.IsActive == !IsActiveFalse) || IsActiveNotApplied) //comp3
&& (x.SubscribeDate >= DateTime.Now.AddDays(-7) || !DateApplied)) //comp4
这是一个很大的简化,将 12 个案例变成了 1 个案例,您只需要添加额外的两个案例,总共 3 个案例,而不是原来的 14 个案例!
将它们组合成代码
public DataTable GetCustomers(int LocationId, int ActiveId, string stats)
{
using (var context = new MyContext())
{
var data = from c in context.Customers
where c.Removed == false
select new
{
FullName = c.FullName,
c.CustomerID,
c._Location.Name,
c.IsActive,
c.SubscribeDate,
c.Removed
};
bool LocationIdNotApplied = LocationId == 0; //for comp1
bool IsActiveNotApplied = LocationId != 0 && (ActiveId < 0 || ActiveId > 2 || stats = "subscribe"); //for comp3 to be applied or not
bool IsActiveFalse = (LocationId != 0 && ActiveId == 2) || stats == "unsubscribe" || (ActiveId == 2 && stats == "subscribe"); //for comp3 to be false
bool DateApplied = LocationId == 0 || (LocationId != 0 && stats == "subscribe");
if(LocationId == 0 && ActiveId == 1 && stats == "all"){ //case 6
return MyContext.CopyToDataTable(
data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == true) || (x.Removed == false) || (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false)));
} else if (LocationId == 0 && ActiveId == 2 && stats == "all"){ //case 9
return MyContext.CopyToDataTable(
data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false) && (x.Removed == false)));
} else { //other cases
return MyContext.CopyToDataTable(
data.Where(x => (x.LocationId == LocationId || LocationIdNotApplied) //comp1
&& x.IsRemoved == false //comp2
&& ((x.IsActive == !IsActiveFalse) || IsActiveNotApplied) //comp3
&& (x.SubscribeDate >= DateTime.Now.AddDays(-7) || !DateApplied))) //comp4
}
}
}
最后的笔记
你的案例6对我来说实际上很奇怪:
data.Where(x => (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == true) || (x.Removed == false) || (x.SubscribeDate >= DateTime.Now.AddDays(-7) || x.IsActive == false)));
请注意,您两者都有x.IsActive == true
and x.IsActive == false
for x.SubscribeDate >= DateTime.Now.AddDays(-7)
。然后你将它与||
。这就像说:
(A || true) || (A || false)
并将永远return true
无论。您可能想再次检查,甚至可以进一步简化/
最后的评论和道歉
因此,我对这种情况的解决方案没有PredicateBuilder
- 它需要对所有可能的情况进行仔细和“系统”(或者,我实际上的意思是一步一步)分析。
我必须向OP道歉,因为我无法完全测试我因缺乏完整的测试资源而提出的代码(与OP不同)。
但如果OP发现有一个我错过处理的案例orOP没有在原始问题中提出,至少,我上面的解决方案中提出的步骤应该还是有用的供OP根据自己的实际情况进行仔细分析。