另一个过滤器库





4.00/5 (3投票s)
一个帮助在 SQL 语句中创建 WHERE 子句或为 System.Data.DataRow 选择操作创建过滤器表达式的类库。
引言
有时当我们学习一项新技术或一门新编程语言时,我们试图找到一种方法来做我们已经熟悉的技术所做的事情。 这就是五年前(或更早)我读到关于 C# 运算符重载时发生的事情。 运算符重载对于 VB 程序员来说不是常见的话题,并且重载运算符来处理复数并不在我的工作范围内。 在本文中,我想向您展示一小组类,这些类可以通过简单地组合表达式和逻辑运算符“&”、“|”和“!”来生成强大的WHERE
条件在 SQL 查询语句中。
问题所在
想象一下您有一个如上所示的用户界面,用户可以选择一个或多个搜索选项,然后您编写一个 SQL 语句来检索匹配的记录。
如果您通过组合 UI 上的值和数据库表上的字段来编写语句,则应小心添加正确的条件,平衡括号,并将参数添加到命令中。
解决方案:运算符重载
我定义了一个基类Expression
,它代表WHERE
子句中的一个通用术语。 然后,我派生了两个类:FieldExpression
,它代表一个字段条件,以及LiteralExpression
,它代表一个基于通用值的条件。 Expression
基类实现了Expression
比较所需的所有运算符。
public abstract class Expression
{
public static Predicate operator ==(Expression expr1, Expression expr2)
{
return new Predicate(expr1, expr2, CompareOperator.Equal);
}
public static Predicate operator !=(Expression expr1, Expression expr2)
{
return new Predicate(expr1, expr2, CompareOperator.NotEqual);
}
public static Predicate operator >=(Expression expr1, Expression expr2)
{
return new Predicate(expr1, expr2, CompareOperator.GreaterOrEqual);
}
public static Predicate operator <=(Expression expr1, Expression expr2)
{
return new Predicate(expr1, expr2, CompareOperator.LessOrEqual);
}
}
两个Expression
之间的每次操作都会产生一个Predicate
。 Predicate 可以使用逻辑运算符“&”、“|”和“!”与其他Predicate
组合,并且每个组合都可以用括号括起来,以便获得不同的评估优先级。 每个Predicate
都是一棵树,其中节点是一组作为 AND 或 OR 子句处理的条件,叶子是要评估的Expression
。 predicate 类能够递归地遍历其自身的层次结构并呈现基本的WHERE
语句。 当到达叶节点(即,属性类型为NodeKind.Expression
的Predicate
)时,它使用ExpressionRenderDelegate
调用回调函数。
private void Render(ExpressionRenderDelegate target,
StringBuilder filter, int nestedlevel)
{
if (this.kind == NodeKind.Expression)
{
target(filter, this.left, this.right, this.compare);
}
else
{
string comma = "";
if (this.kind == NodeKind.And)
{
foreach (Predicate p in this.siblings)
{
filter.Append(comma);
p.Render(target, filter, nestedlevel + 1);
comma = " and ";
}
}
if (this.kind == NodeKind.Or)
{
filter.Append("(");
foreach (Predicate p in this.siblings)
{
filter.Append(comma);
p.Render(target, filter, nestedlevel + 1);
comma = " or ";
}
filter.Append(")");
}
}
}
我使用了这种策略进行语句渲染,因为我想让Predicate
类相当抽象,并拥有用于生成数据库特定WHERE
子句的专用类(例如,请参阅两种实现:DataRowFilterRender
和SqlFilterRender
)。 仅此而已。
演示项目
您可以使用包含的 FiltersTest 应用程序测试 Filter 库。 它需要连接到著名的 Northwind SQL Server 数据库,因此请修改 *FiltersTest.exe.config* 文件中的连接字符串以适应您的数据库服务器。 然后,如果您进入 *Form1.cs*,您可以看到一个如何使用库在下面显示的button1_Click
中的示例。
private void button1_Click(object sender, EventArgs e)
{
Predicate p, p1 = null, p2 = null, p3 = null;
if(cmbcust.SelectedIndex >= 0) {
p1 = (FieldExpression)"CustomerID" ==
(LiteralExpression<string>)cmbcust.SelectedValue.ToString();
}
if (cmbemp.SelectedIndex >= 0)
{
p2 = (FieldExpression)"EmployeeID" ==
(LiteralExpression<int>)(int)cmbemp.SelectedValue;
}
if (cmbship.SelectedIndex >= 0)
{
p3 = (FieldExpression)"ShipVia" ==
(LiteralExpression<int>)(int)cmbship.SelectedValue;
}
if (optOr.Checked)
{
p = p1 | p2 | p3;
}
else
{
p = p1 & p2 & p3;
}
if (optExclude.Checked)
{
p = !p;
}
SqlFilterRender f = new SqlFilterRender("select * from Orders");
textBox1.Text = f.Render(p);
f.Command.Connection = this.ordersTableAdapter.Connection;
SqlDataAdapter da = new SqlDataAdapter(f.Command);
this.northwindDataSet.Orders.Clear();
da.Fill(this.northwindDataSet.Orders);
}
未来工作
Filter 库不完整,SQL 语言中还有很多运算符尚未实现(in
,between
,is null
等),并且并非所有基本数据类型都已处理。 我正在努力,但与此同时,请随时添加您的贡献...