运行时配置 LINQ for Objects 的 Where 条件
允许在响应用户输入时,运行时修改 LINQ for Objects 查询的 where 条件。
更新于 2015 年 4 月
对于 LINQ 查询中灵活的 where
条件的问题,有一个比本文中概述的更好的解决方案。 看看这里
引言
在使用 LINQ for Objects 时,我遇到的一个问题是创建易于处理从搜索对话框中获得的各种参数数量的查询。 这篇短文概述了该问题的一个部分解决方案。
背景
该解决方案基于用一个函数简单地替换硬编码的条件。 一个基本的 LINQ 查询看起来大致如下
var query = from TestClass t in TestData
where t.Alpha == 10
select {t.Name}
如果只想测试 TestClass
对象的 Alpha 属性,这是理想的选择。 我们可以使用一个或多个 Lambda 函数并引用满足我们需求的函数来获得更大的灵活性。
Func<TestClass, bool> filter = delegate(TestClass t)
{ return (t.Alpha >= 10 && t.Gamma >300); };
var query = from TestClass t in TestData
where (filter)
select {t.Name};
但是,我们定义的每个函数仍然硬编码了被测试的属性的名称和数量。 这不是我想要的。
Using the Code
只有三个类。
类 | 注释 |
Condition<T> |
定义一个 where 条件实例,该实例具有零个或多个 Condition.Test 定义,用于类型为 T 的一个或多个命名属性的对象。 |
Condition.Test |
将一个 string (text ) 或数字测试与一个命名属性相关联。 |
比较 |
提供用于应用为 [Condition] 类的实例定义的测试的方法 |
类:[Condition]
允许程序员将多个属性测试组合在一起,作为 AND
组或 OR
组。 这种测试集合可以添加到或完全重新初始化,而不会影响 LINQ 查询。 可以在查询定义中引用的 Condition
对象的不同配置下重新运行该查询。
属性 | 类型 | 注释 |
逻辑 |
枚举:TestLogic |
确定在条件内如何应用测试。 AND 或 OR |
测试 |
List<Condition.Test> |
要应用的测试 |
方法 | Returns | 注释 |
Evaluate<T> (T Instance)
|
bool |
使用默认的 TestLogic 评估 Condition 实例中所有定义的 Condition.Tests 。 从 LINQ 对象查询内部调用。 |
Evaluate<T> (T Instance, TestLogic)
|
bool |
使用指定的 TestLogic 评估所有定义的 Condition.Tests 。 从 LINQ 对象查询内部调用。 |
类:[Condition.Test]
这允许程序员将属性名称与适合该属性类型的测试以及要测试的值相关联。
属性 | 类型 | 注释 |
Compare |
对象 - 枚举
|
要应用于属性名称的测试:>, = < 等。 |
CompareTo |
数字对象或字符串 |
要测试属性的数字或字符串值。 数字值在测试时都转换为 double 。 |
类:[Comparison]
使用反射和一些简单的 switch{}
语句来执行由 Condition.Test
或对象实例定义的测试。
方法 | Returns | 注释 |
Test<T>
(T Instance,
string propertyName,
Comparison.Text comparison,
string compareTo)
|
bool |
将字符串测试应用于实例的命名属性。 |
Test<T>
(T Instance,
string propertyName,
Comparison.Number comparison,
object compareTo)
|
bool |
将数字测试应用于实例的命名属性。 |
设置一个条件
虽然该示例的属性名称和测试值是硬编码的,但我希望很明显,添加到每个条件的测试可以根据从搜索对话框中获取的用户输入或程序状态的某些其他变化的结果来定义。
测试可以作为 OR
组或 AND
组应用。 如果未指定,则默认行为是 AND
。
// Create a condition containing multiple tests that apply to an object
// of type TestClass
// Tests within the condition will be applied using logical OR.
Condition<TestClass> whereCondition = new Condition<TestClass>() {Logic = TestLogic.Or} ;
whereCondition.Tests.Add(new Condition<TestClass>.Test() {PropertyName="Alpha",
Compare = Comparison.Number.GreaterEqual,
CompareTo = 10});
whereCondition.Tests.Add(new Condition<TestClass>.Test() {PropertyName="Gamma",
Compare = Comparison.Number.Greater,
CompareTo = 300});
whereCondition.Tests.Add(new Condition<TestClass>.Test() {PropertyName="Name",
Compare = Comparison.Text.Contains,
CompareTo = "D"});
定义查询...
var query = from TestClass t in TestClass.testdata()
where (whereCondition.Evaluate<TestClass>(t))
select new {t.Name};
...并运行它
// Run the query with the where Condition in its initial state.
foreach (var t in query)
Console.WriteLine(t.Name);
将 where
逻辑设置与查询定义分开后,我们现在可以根据需要多次重新运行查询,更改 where
条件...
切换到逻辑 AND...
whereCondition.Logic = TestLogic.And;
foreach (var t in query)
Console.WriteLine(t.Name);
完全更改条件...
whereCondition.Tests.Clear();
whereCondition.Logic = TestLogic.Or;
whereCondition.Tests.Add(new Condition<TestClass>.Test() {PropertyName="Alpha",
Compare = Comparison.Number.GreaterEqual,
CompareTo = 100});
whereCondition.Tests.Add(new Condition<TestClass>.Test() {PropertyName="Beta",
Compare = Comparison.Number.LessEqual,
CompareTo = 20});
foreach (var t in query)
Console.WriteLine(t.Name);
或者根本没有任何测试...
whereCondition.Tests.Clear();
foreach (var t in query)
Console.WriteLine(t.Name);
组合多个条件
// Set up (t.Alpha >= 100 || t.Beta <= 20) && t.Gamma >= 30
whereCondition.Logic = TestLogic.Or;
whereCondition.Tests.Add(new Condition<TestClass>.Test() {PropertyName="Alpha",
Compare = Comparison.Number.GreaterEqual,
CompareTo = 100});
whereCondition.Tests.Add(new Condition<TestClass>.Test() {PropertyName="Beta",
Compare = Comparison.Number.LessEqual,
CompareTo = 20});
// ..define an additional condition.
Condition<TestClass> supplementary = new Condition<TestClass>();
supplementary.Tests.Add(new Condition<TestClass>.Test() {PropertyName="Gamma",
Compare = Comparison.Number.GreaterEqual,
CompareTo = 30});
// ...and redefine the query to use both conditions.
query = from TestClass t in TestClass.testdata()
where (whereCondition.Evaluate<TestClass>(t) &&
supplementary.Evaluate<TestClass>(t))
select new {t.Name};
foreach (var t in query)
Console.WriteLine(t.Name);
优点与缺点
优点
Where
条件定义独立于查询定义- 我们开启了持久化和检索
where
条件定义的可能性
缺点
- 需要更多打字才能设置
where
条件。 - 如编写所示,仅适用于表示字符串或数字类型的属性。 日期比较可能会很好。
- 代码不够透明,因此可能更难以维护。
- _一定_ 会有性能损失。
- 可能存在更优雅的解决方案,但我还没有遇到它。 至今。
历史
- 2012 年 2 月 - 首次(可能也是最终)发布