Linq to Sql : 动态构造Expression进行动态查询

原文:Linq to Sql : 动态构造Expression进行动态查询

前一篇在介绍动态查询时,提到一个问题:如何根据用户的输入条件,动态构造这个过滤条件表达式呢?
Expression<Func<ProductExt, bool>> predicate t => t.ProductName.Contains("che") && t.UnitPrice >= 22;

理想情况下,我希望可以像下面这样来构造predicate,这样,我们就可以使用&、| 、&=、|=来任意拼接过滤条件了:

   1: Expression<Func<ProductExt, bool>> predicate = null;
   2: predicate &= (t => t.ProductName.Contains("che")) | (t => t.UnitPrice >= 22);

但是理想与现实之间,似乎总有不可逾越的鸿沟……

前面的代码中,我们总是要写一常串Expression<Func<T, bool>>,写得都有点儿烦了,我妄想自定义一个类型,这样就不用每次都写这么长了,然后再针对这个自定义类型再进行运算符重载……然后我发现,Expression<Delegate>是密封类型(sealed),不给重载,没办法,只好老老实实的写,运算符重载也泡汤了。

无奈之下,只好实现了下面的这种方式,勉强凑合用着:

   1: Expression<Func<ProductExt, bool>> predicate = null;
   2: predicate = predicate.AndAlso(p => p.CompanyName.Length > 2)
   3:      .OrElse((Products p) => p.ProductName.Contains("che"))
   4:      .AndAlso((Products p) => p.UnitPrice >= 22);

运算得到的结果如下:p => (((p.CompanyName.Length > 2) || p.ProductName.Contains("che")) && (p.UnitPrice >= 22))

有了这个OrElse和AndAlso扩展,我们就可以对Expression<Func<T, bool>>为所欲为了……

不过跟之前文章中提到的一样,这里有个限制:虽然我们的Expression中,第一个可以凡泛型参数可以传任意值(譬如传ProductExt或Products或其他的,可以不必要求是同一种类型),但条件中用到的对象属性(譬如CompanyName、ProductName、UnitPrice ),必须在T中存在同名属性。

2010-12-31更新:

更新了ParameterConverter类,之前对二元运算符的右侧和方法参数直接求值的,所以不能处理Expression中含有&& 和 || 等二元运算符、及方法参数中包含Expression参数(例如list.Contains(p.CompanyName))的情况;现在增加了参数访问计数,如果二元表达式&&和||、及方法的参数中不包含Expression参数,则进行求值,否则不求值。代码如下:

1     public class ParameterConverter : ExpressionVisitor
 2     {
 3         protected LambdaExpression SourceExpression { get; set; }
 4         protected ParameterExpression Parameter { get; set; }
 5         protected bool UseOuterParameter = false;
 6 
 7         public ParameterConverter(LambdaExpression expression)
 8         {
 9             this.SourceExpression = (LambdaExpression)expression;
10         }
11 
12         public ParameterConverter(LambdaExpression expression, ParameterExpression parameter) : this(expression)
13         {
14             this.Parameter = parameter;
15             this.UseOuterParameter = true;
16         }
17 
18         public LambdaExpression Replace(Type targetType)
19         {
20             if (this.SourceExpression == null)
21                 return null;
22 
23             if (!this.UseOuterParameter)
24                 this.Parameter = Expression.Parameter(targetType, this.SourceExpression.Parameters[0].Name);
25 
26             Expression body = this.Visit(this.SourceExpression.Body);
27             this.SourceExpression = Expression.Lambda(body, this.Parameter);
28             return this.SourceExpression;
29         }
30 
31         protected override Expression VisitParameter(ParameterExpression p)
32         {
33             return this.Parameter;
34         }
35 
36         protected override Expression VisitMemberAccess(MemberExpression m)
37         {
38             Expression exp = this.Visit(m.Expression);
39             PropertyInfo propertyInfo = m.Member as PropertyInfo;
40             return propertyInfo == null ? m : Expression.Property(exp, propertyInfo.Name);
41         }
42 
43         protected override Expression VisitBinary(BinaryExpression b)
44         {
45             Expression left = this.Visit(b.Left);
46             Expression right = Calc(b.Right);//对二元运算符右边的表达式进行求值
47             Expression conversion = this.Visit(b.Conversion);
48             if (b.NodeType == ExpressionType.Coalesce && b.Conversion != null)
49                 return Expression.Coalesce(left, right, conversion as LambdaExpression);
50             else
51                 return Expression.MakeBinary(b.NodeType, left, right, b.IsLiftedToNull, b.Method);
52         }
53 
54         protected override ReadOnlyCollection<Expression> VisitExpressionList(
55                 ReadOnlyCollection<Expression> original)
56         {
57             if (original == null || original.Count == 0)
58                 return original;
59 
60             //对参数进行求值运算
61             List<Expression> list = new List<Expression>();
62             for (int i = 0, n = original.Count; i < n; i++)
63             {
64                 list.Add(Calc(original[i])); //对调用函数的输入参数进行求值
65             }
66             return list.AsReadOnly();
67         }
68 
69         private static Expression Calc(Expression e)
70         {
71             LambdaExpression lambda = Expression.Lambda(e);
72             return Expression.Constant(lambda.Compile().DynamicInvoke(null), e.Type);
73         }
74     }

最后,上代码:LinqToSqlExtension

博客园的文件貌似有缓存,删掉重新上传,总是覆盖不了,只好单独上传了 ParameterConverter.rar

Linq to Sql : 动态构造Expression进行动态查询

时间: 2024-12-22 23:27:12

Linq to Sql : 动态构造Expression进行动态查询的相关文章

Linq to sql 实现多条件的动态查询(方法一)

/// <summary> /// Linq to sql 多字段动态查询 /// </summary> /// <returns></returns> private List<TVacant> ViewBinding(ModelDataContext db,string fyno,string brd,string area,string city,string pos) { Expression<Func<TVacant, bo

Linq to sql 结合Entity Framework 的连接查询总结

最近在做项目使用linq结合EntityFramework来处理数据库的操作.想来也用了快一年了,发现有些使用技巧是需要注意下,特做下总结,希望对刚入门的朋友们有所帮助.刚开始用的时候各总循环查询子查询,结果性能差得不行,现在看看都觉得好笑.也只有写出过很烂的代码才知道怎么样的代码才是优雅的吧,哈哈.我先总结下连接查询吧,发现很多刚入门的朋友和我一样,不知道怎么用linq写类似与sql的连接查询(left join.inner join等等). 连接查询 内连接查询 linq语法如下: 1 va

Linq to Sql:N层应用中的查询(下) : 根据条件进行动态查询

原文:Linq to Sql:N层应用中的查询(下) : 根据条件进行动态查询 如果允许在UI层直接访问Linq to Sql的DataContext,可以省去很多问题,譬如在处理多表join的时候,我们使用var来定义L2S查询,让编译器自动推断变量的具体类型(IQueryable<匿名类型>),并提供友好的智能提示:而且可以充分应用L2S的延迟加载特性,来进行动态查询.但如果我们希望将业务逻辑放在一个独立的层中(譬如封装在远程的WCF应用中),又希望在逻辑层应用Linq to sql,则情

Entity Framework 与 LINQ to SQL

Entity Framework和LINQ to SQL到底有什么区别?这是一个很常见的问题.下面的表中简要罗列了两种技术的主要区别. LINQ to SQL Entity Framework 复杂度 相对不复杂 相对比较复杂 模型 域模型(Domain model) 概念数据模型(Conceptual data model) 数据库服务器 SQL Server 多种数据库产品 开发时间 快速应用开发 需要较长时间,但支持更多特性 继承 困难 容易 文件类型 DBML文件 EDMX,CDSL,M

LINQ to SQL和Entity Framework对比

LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术.他们之间的本质区别在于EF对数据库架构和我们查询的类型实行了更好的解耦.使用EF,我们查询的对象不再是完全对应数据库架构的C#类,而是更高层的抽象:Entity Data Model.这为我们提供了额外的灵活性,但是在性能和简单性上面也会有所损失. LINQ to SQL由C#团队开发并在.NET Framework 3.5中发布,而Entity Framework由ADO.NET团队开发并作为.

zz entity framework vs linq to sql

http://blog.csdn.net/jfchef/article/details/7632872 Entity Framework和LINQ to SQL到底有什么区别?这是一个很常见的问题.下面的表中简要罗列了两种技术的主要区别. LINQ to SQL Entity Framework 复杂度 相对不复杂 相对比较复杂 模型 域模型(Domain model) 概念数据模型(Conceptual data model) 数据库服务器 SQL Server 多种数据库产品 开发时间 快速

LINQ TO SQL和Entity Framework 的关系 你了解多少?

1. LINQ  TO SQL 和EF 特点:  LINQ TO SQL和Entity Framework都是一种包含LINQ功能的ORM 也就是所谓的关系对象的映射.其中包括的有DBFrist  Code Frist    ModeL  Frist   三种中方式 来进行和数据库之间的访问.只是形式不同而已本质没有什么区别.都是ORM  之间的转化.最终都是来进行访问底层的数据库的对象 和服务. 简单来说LINQ是为了满足不知道怎么操作数据库的程序员开发设计的,LinQ 可以让他们以一种面向数

LINQ to SQL和Entity Framework对比与关联

LINQ to SQL和Entity Framework都是一种包含LINQ功能的对象关系映射技术.他们之间的本质区别在于EF对数据库架构和我们查询的类型实行了更好的解耦.使用EF,我们查询的对象不再是完全对应数据库架构的C#类,而是更高层的抽象:Entity Data Model.这为我们提供了额外的灵活性,但是在性能和简单性上面也会有所损失. LINQ to SQL由C#团队开发并在.NET Framework 3.5中发布,而Entity Framework由ADO.NET团队开发并作为.

LINQ TO SQL 中的join(转帖)

http://www.cnblogs.com/ASPNET2008/archive/2008/12/21/1358152.html join对于喜欢写SQL的朋友来说还是比较实用,也比较容易接受的东西.在LINQ TO SQL中,写多表查询,同样可以写join,只是它有它自己的语法要求而已,语义都是一样的,下面我来讲下LINQ TO SQL中的join最基本的形式:都是最简单的,当然还有其它方面的内容,如:怎样加上过滤条件,如何分组,如何排序等等,为了单纯说join的用法,这里就简化下. Cod