如何使LINQ查询表达式动态化

时间:2011-10-05 20:22:16

标签: linq

我正在尝试使用以下LINQ查询表达式实现级联控件。

我的想法是,我有两个由表OptionA,OptionB和OptionC表示的选项列表和一个名为OptionIndex的视图,每个列有一个用于OptionA_ID,OptionB_ID,OptionC_ID的列,该表包含选项列表中所有标记的组合正在使用中。左外连接选项列表上的OptionIndex会为选项标记中的Disabled属性生成一个布尔值。

如何使on子句,即以下示例代码中的.Where(...)允许使用的控件的任意组合?

例如,假设用户最初在OptionA中选择选项值123。返回OptionC的值,标签和禁用布尔值的代码如下所示:

from t1 in OptionCs
from t2 in OptionIndexes.Where(x => t1.OptionC_ID == x.OptionC_ID && new List<int> { 123 }.Contains(x.OptionA_ID)).DefaultIfEmpty()
group new {t1, t2} by new { t1.OptionC_ID, t1.Label } into g
select new { g.Key.OptionC_ID, g.Key.Label, Disabled = g.Count(t => t.t2.OptionC_ID == null) > 0 }

然后假设用户在OptionB中选择选项值456和789。返回OptionC的值,标签和禁用布尔值的代码更改为:

from t1 in OptionCs
from t2 in OptionIndexes.Where(x => t1.OptionC_ID == x.OptionC_ID && new List<int> { 123 }.Contains(x.OptionA_ID) && new List<int> { 456, 789 }.Contains(x.OptionB_ID)).DefaultIfEmpty()
group new {t1, t2} by new { t1.OptionC_ID, t1.Label } into g
select new { g.Key.OptionC_ID, g.Key.Label, Disabled = g.Count(t => t.t2.OptionC_ID == null) > 0 }

为了使示例代码更易于理解,我使用了new List<int>。在实际项目中,我将从控件本身作为整数数组传递选项列表中的整数。

技巧以某种方式使查询表达式动态化,以便它可以表示正在使用的0到N个多选控件的任意组合,或者传递一些东西来告诉连接接受任何给定控件的任何值,例如

{x.OptionB_ID.Any}.Contains(x.OptionB_ID)

处理此问题的最佳方法是什么?

谢谢!

1 个答案:

答案 0 :(得分:2)

将问题提炼为一个简单的例子,请考虑这个整数列表:

List<int> l = new List<int> { 1, 25, 3, 99, -23, 0, 15, 75 };

假设您要根据外部条件有条件地过滤此列表。有时您想要正数,有时您想要小于50的数字,有时您希望数字可被5整除,或者这些数字的任意组合。使用静态表达式应用所有过滤器将如下所示:

l.Where(n => n > 0).Where(n => n < 50).Where(n => n % 5 == 0);

要动态应用任何或所有这些,只需构建LINQ查询:

// These switches simulate your external conditions.
bool conditionA = true;
bool conditionB = false;
bool conditionC = true;

IEnumerable<int> myList = l;
if (conditionA) { myList = myList.Where(n => n > 0     ); }
if (conditionB) { myList = myList.Where(n => n < 50    ); }
if (conditionC) { myList = myList.Where(n => n % 5 == 0); }

在我的示例中设置了开关,输出为25,15,75。


旁注:如果您不了解,请使用LINQPad来尝试这样的事情。对于基本上以交互方式执行代码而言,它是一个很棒的工具,无论是否为LINQ代码。当我构建上面的示例时,我在最后4行的每一行之后插入myList.Dump();次调用,这样我就可以看到每个过滤器的应用方式。这是输出:

LINQ filtering progression