[翻译]使用LINQ实现动态搜索(Implementing Dynamic Searching Using LINQ)
2008-08-29 03:11 G yc {Son of VB.NET} 阅读(1352) 评论(0) 编辑 收藏 举报原文链接:http://blogs.msdn.com/vbteam/archive/2007/08/29/implementing-dynamic-searching-using-linq.aspx
作者: Jonathan
在数据窗体应用程序中一个常见的需求是使用户能够按照由任意字段组合的动态查询搜索。例如,程序中的搜索功能允许用户查找所有满足在多个列上定义了不同规则的记录。
LINQ 使得编写跨越多种数据源的(强大)查询变得简单。例如,我们可以使用下面的查询来查找在用户指定的时间段内所有运往指定国家的所有订单(Orders) :
Dim query = From order In db.Orders _
Where order.ShipCountry = txtCountry.Text _
And order.ShippedDate >= dtpStartDate.Value _
And order.ShippedDate <= dtpEndDate.Value
这可以很容易的在编译时做到,但是如果我们希望检查订单日期而不是出货(Shipped)日期?这使得我们不得不编写另一个使用order.OrderDate的查询。如果你仅仅生成SQL字符串,那么运行时动态的生成不全是困难的,但LINQ如何做这个?LINQ 是否要求我在编译时指定条件为了建立查询?幸运的是,答案是肯定的。LINQ支持在运行时通过表达式 树 API 和表达式编译器(Expression Tree API and the Expression Compiler)来建立动态查询。
在Visual Studio 2008中,任何有效的VB表达式都被解释成表达式树(Expression Tree )。我们要做的是建立一个表达式树(Expression Tree )来表示用户条件,然后在传递它到LINQ to SQL,LINQ to SQL运行时将会把它转换成SQL语句。那么我们上面说的 Where语句第一个部分看起来像这个样子:
Dim p = Expression.Parameter(GetType(Order), "")Dim order = GetType(Order).GetProperty("ShipCountry")
Dim expr = Expression.Equal(Expression.PropertyOrField(p, order.Name), Expression.Constant("Germany"))
Dim predicate = Expression.Lambda(Of Func(Of Order, Boolean))(expr, New ParameterExpression() {p})
Yikes,这只是一小部分,我们目前只完成了1/3的Where语句!我可不想在写12行代码去来在运行时建立Where语句。我想做的是写一个CreateCondition 扩展方法,允许我用一行简单的代码建立表达式树(Expression Tree),像是这个样:
Dim condition1 = db.Orders.CreateCondition("ShipCountry", Compare.Equal, "Germany")
那么对于ShippedDate 我们可以建立2个条件:
Dim startDate? = #1/1/1997#Dim endDate? = #1/31/1997#
Dim condition2 = db.Orders.CreateCondition("ShippedDate", Compare.GreaterThanOrEqual, startDate)
Dim condition3 = db.Orders.CreateCondition("ShippedDate", Compare.LessThanOrEqual, endDate)
注意我们第一次传递一个字符串,之后又传入2个可为空的日期 (Literal) ,我们可以这么做是因为CreateCondition 方法是泛型的并可以根据传入的参数推断类型。我们现在仅需要组合条件到一个条件中:
Dim c = Condition.Combine(condition1, Compare.And, condition2, condition3)
或者我们也可以用操作符重载(Operator Overloading )来完成这个(等价于上面的代码):
Dim c = condition1 And condition2 And condition3
OK现在我们可以建立 我们的 Condition 对象来过滤数据:
'过滤所有不符合条件的Orders
'注意 这个查询还没有执行,他将延期执行
Dim filteredQuery = db.Orders.Where(c)
'我们还可以执行其他操作(像是排序)在 filteredQuery
Dim query = From row In filteredQuery _
Order By row.OrderDate, row.OrderID _
Select row
'执行查询并显示结构到DataGridView1
DataGridView1.DataSource = query
-
Condition 是一个抽象类用来构造一般版本(generic versions)通过构造这个,可以为一般版本参数推断带来好处,即我们不用担心传入的一般版本参数到方法,工厂方法将帮助我们解决。
-
Condition(Of T) 使用合并多个conditions 到一起。T是元素类型(即上面例子中的 Order )
-
Condition(Of T, S)是一种简单类型,它表示“object.propery <comparison> 值”表达式。类型参数 S将被推断为相应的值类型传入(即 String, Date, Boolean ...)
'编译lambda 表达式成委托del = DirectCast(LambdaExpr.Compile(), Func(Of T, Boolean))