在进行数据查询时,经常碰到需要动态构建查询条件。使用LINQ实现这个需求可能会比以前拼接SQL语句更麻烦一些。本文介绍了3种运行时动态构建查询条件的方法。 本文中的例子最终实现的都是同一个功能,从Northwind数据库Customers表中搜索出CompanyName列带有keywords中任意元素的项。keywords是个字符串数组,该数组长度在编译时是不确定的。思路及方法说明写在代码注释中。
1.表达式树
1 public static IEnumerable<Customers> GetCustomersFunc1(string[] keywords)
2 {
3 DataClassesDataContext dc = new DataClassesDataContext();
4
5 //创建一个静态类型为Customers的参数表达式
6 ParameterExpression c = Expression.Parameter(typeof(Customers), "c");
7
8 //创建一个恒等于false的表达式,用于与下面的表达式取并集
9 Expression condition = Expression.Constant(false);
10 foreach (string keyword in keywords)
11 {
12 //该表达式用于判断一个Customers类的CompanyName属性的值是否包含了关键字keyword
13 Expression con = Expression.Call(
14 Expression.Property(c, typeof(Customers).GetProperty("CompanyName")),
15 typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),
16 Expression.Constant(keyword));
17
18 //与之前的condition表达式进行逻辑或运算。
19 //如果要查找的项需要包含keywords中的所有关键字,则可使用Expression.And(con, condition)
20 //并且将Expression condition = Expression.Constant(false);
21 //改成Expression condition = Expression.Constant(true);
22 condition = Expression.Or(con, condition);
23 }
24
25 //创建一个以一个Customers类作为参数并返回bool类型的委托
26 Expression<Func<Customers, bool>> end = Expression.Lambda<Func<Customers, bool>>(condition, new ParameterExpression[] { c });
27
28 //使用刚才构建的条件进行查询
29 var result = dc.Customers.Where(end);
30 return result;
31 }
32
2.使用System.Linq.Dynamic
1 public static IEnumerable<Customers> GetCustomersFunc2(string[] keywords)
2 {
3 //需要引用System.Linq.Dynamic。Dynamic.cs文件可在LinqSamples中找到
4
5 DataClassesDataContext dc = new DataClassesDataContext();
6 string queryString = "";
7 foreach (string keyword in keywords)
8 {
9 //原形为(c=>c.CompanyName.Contains(keyword1)) || (c=>c.CompanyName.Contains(keyword2)) ||
10 queryString += "CompanyName.Contains("" + keyword + "") or ";
11 }
12
13 //与false进行逻辑或运算,为了避免queryString中最后的or出现语法错误
14 queryString += "1=0";
15 return dc.Customers.Where(queryString);
16 }
17
3.披着Linq的外衣拼接SQL语句
1 public static IEnumerable<Customers> GetCustomersFunc3(string[] keywords)
2 {
3 //这个方法其实是伪Linq,核心还是在拼接SQL语句,所以就不多解释了
4 DataClassesDataContext dc = new DataClassesDataContext();
5 string sqlQuery = "SELECT [CustomerID], [CompanyName], [ContactName], [ContactTitle], [Address], ";
6 sqlQuery += "[City], [Region], [PostalCode],[Country], [Phone], [Fax] FROM [dbo].[Customers] WHERE ";
7 foreach (string keyword in keywords)
8 {
9 sqlQuery += "([CompanyName] LIKE '%" + keyword + "%' ) OR ";
10 }
11 sqlQuery += "(1=0)";
12 return dc.ExecuteQuery<Customers>(sqlQuery);
13 }
14
15
=============================================
LINQ IN ACTION读书笔记:在运行时创建查询——表达式树
动态查询语句:之所以叫做动态查询语句是因为语句中使用的某些值,甚至之句将在运行时而不是编译时才能被最终决定。在编写查询语句时,还不能得到执行查询所需要的信息,因为这些信息可能会来自用户输入或者程序运行时的上下文,甚至在一些更高级的应用中,我们可能需要在程序运行时从无到有地完整创建出一个查询。比如,若应用程序需要查询来自多种地方的数据,例如XML文件、远程应用程序或用户等。我们可以使用最终的武器——表达式树。
例如:如下XML片段描述将要应用到某个图书集合上的过滤条件。
<and> <notEqual property="Title" value="Bonjour mon Amour"/> <greaterThan property="pageCount" value="200"/> </and>
如果直接编写这个查询,则如下:
var q = from book in SampleData.Books where (book.Title != "Bonjour mon Amour" && book.PageCount > 200) select book;
这个查询完全定义于编译期,不过若是该XML文件在运行时才交付给程序,那么就不可能按照这种写法书写,因为程序已经编译过了,此时的解决方案就是使用表达式树.
创建表达式树的最简单的方法就是让编译器将Expression<TDelegate>类型的Lambda表达式转换成一系列的工厂方法调用,然后借助这些工厂方法在运行时创建表达式树。不过若是为了生成动态查询语句,那么则要以另外的方法创建表达式树。我们完全可以根据需要自行调用这些工厂方法(即一系列定义于Expression<TDelegate>类中的静态方法)来创建出表达式树,并在运行时将其编译成Lambda表达式。
var book = Expression.Parameter(typeof (Book), "book"); var titleExpression = Expression.NotEqual(Expression.Property(book, "Title"), Expression.Constant("Bonjour mon Amour")); var pageCountExpression = Expression.GreaterThan(Expression.Property(book, "PageCount"), Expression.Constant(200)); var andExpression = Expression.And(titleExpression, pageCountExpression); var predicate = Expression.Lambda(andExpression, book); var query = Enumerable.Where(SampleData.Books, (Func<Book, bool>) predicate.Compile()); GridView1.DataSource = query.ToList(); GridView1.DataBind();
以上从无到有创建出了一个查询表达式,用来实现XML中描述的需求。对该表达式树的每个增强操作都是通过向其添加新的表达式来实现的。倒数第三四代码将该表达式树转换为能够执行的查询语句。其中query变量的使用方法与其他的LINQ查询语句相比没有什么区别。