linq to sql之组装where条件下的'或'语句

之前遇到过类似的需求,即前台传入几个过滤条件,后台动态组装where。

例如,前台传入name='张三',age=10, 其余的字段,类似email,QQ之类的本次查询时不做过滤。

用linq to sql来组装where语句非常方便。

var f= db.Users.Where(x => true);
if(!string.IsNullOrWhiteSpace(name))
    f= f.Where(x=>x.Name == name);
if(age != null)
    f= f.Where(x=>x.Age == age);
var data = f.ToList();

今天碰到另一种情况。状态的多种可能性。用户的注册邮箱有多个状态(用int表示):未验证null,无效-1,有效0,退订-2,查看(>0的值)等。前台过滤时,可能勾选多个状态,例如未验证 或 有效 或 查看。用后台的where表示,就是:

Where(x => x.EmailState == null || x.EmailState == 0 || x.EmailState > 0)

问题出现了。这里有五种状态,每个状态有前台选中和未选中两种情况,组合的情况最多有2^5 = 32种。用32个if来解决这里的问题,不是不可以,只是代码会显得很笨重,而且扩展性很差,如果以后出现第六个、第七个状态......T_T...T_T

首先想到的,能不能像上面的where组装那样实现。答案是不行。上面的where很明显是逻辑and运算。而这里应该是单个where情况下的or运算

解决思路:用Func<Users,bool>来表示每一个邮箱状态对应的过滤语句。对勾选的每一个状态,对它们的过滤语句执行'||'运算。

filter1(x) || filter2(x) || filter3(x)...

代码方案一:

Func<Users, bool> filter = x => false;
if (chkEmailUnverify.Checked) filter = x => filter(x) || x.SendMail == null;
if (chkEmailUnsubscribe.Checked) filter = x => filter(x) || x.SendMail == -2;
if (chkEmailInvalid.Checked) filter = x => filter(x) || x.SendMail == -1;
if (chkEmailValid.Checked) filter = x => filter(x) || x.SendMail == 0;
if (chkEmailOpen.Checked) filter = x => filter(x) || (x.SendMail.HasValue && x.SendMail.Value > 0);

对勾选的每一个状态,在之前的Func<Users,bool>基础上执行||运算。理论上来讲应该行得通,但是实际上调试的时候发现,只要超过两个勾选,就会报错:

提示堆栈溢出。呃...对这类问题一直比较怵,对clr不了解。只能做猜测:因为filter本身就是代理,而代理的执行有延迟的特性,所以这里filter = x => filter(x)...可能会出现无限向下发展的情况。寻求曲线解决。

代码方案二:

Func<Users, bool> filter = x => true;
List<Func<int?, bool>> list = new List<Func<int?, bool>>() { x => false };
if (chkEmailUnverify.Checked) list.Add(x => x == null);
if (chkEmailValid.Checked) list.Add(x => x == 0);
if (chkEmailOpen.Checked) list.Add(x => x > 0);
if (chkEmailUnsubscribe.Checked) list.Add(x => x == -2);
if (chkEmailInvalid.Checked) list.Add(x => x == -1);

filter = x => list.Select(c => c(x.SendMail)).Aggregate((a, b) => a || b);

好吧,我承认只是在之前的那个思路基础上加了一个外壳。壳的主要思想是避免出现filter = x => filter(x) || x.Shield == 'value'。

可能上面的考虑有些道理,这次的方案通过了编译,也取得了我要的结果。大功告成~o(∩_∩)o 

调用时的代码:

var f= db.Users.Where(filter);

如有错误,欢迎留言指导。

为MH370祈福~

posted @ 2014-03-24 15:34  脸谱匠  阅读(937)  评论(4编辑  收藏  举报