Linq To Sql进阶系列(六)用object的动态查询与保存log篇

系列文章导航:

Linq To Sql进阶系列(一)从映射讲起

Linq To Sql进阶系列(二)M:M关系

Linq To Sql进阶系列(三)CUD和Log

Linq To Sql进阶系列(四)User Define Function篇

Linq To Sql进阶系列(五)Store Procedure篇

Linq To Sql进阶系列(六)用object的动态查询与保存log篇

Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别


动态的生成sql语句,根据不同的条件构造不同的where字句,是拼接sql 字符串的好处。而Linq的推出,是为了弥补编程中的 Data != Object 的问题。我们又该如何实现用object的动态查询呢?

1,用object的查询是什么?
我们可以简单的举这么一个例子。我们到公安局查找一个人。首先,我们会给出他的一些特征,比如,身高多少,年龄多少,性别,民族等。那么,我们把这个人的一些特征输入电脑。我们希望,电脑能给我们返回这个人的信息。而实际上,有相同特征的人太多了,常常返回一个集合。那让我们把这个过程抽象到程式里。我们需要new出来一个对象。这个对象包含了我们能知道的基本信息。而后,把这个对象传给Linq To Sql,等待返回结果。

根据这些基本的需求,我们来定义下面的函数,为了实现这个函数对任何实体都是有用的,我们把它定义为generic的。为了不破坏Linq To Sql延迟加载的规矩,我们把它的返回类型定义为IQueryable。如下:

public IQueryable<TEntity> Find<TEntity>(TEntity obj) where TEntity : class

系列文章导航:

Linq To Sql进阶系列(一)从映射讲起

Linq To Sql进阶系列(二)M:M关系

Linq To Sql进阶系列(三)CUD和Log

Linq To Sql进阶系列(四)User Define Function篇

Linq To Sql进阶系列(五)Store Procedure篇

Linq To Sql进阶系列(六)用object的动态查询与保存log篇

Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别


2,原理
Linq To Sql支持用户动态生成lambda表达式。本文中所实现的方法,正是反射加lambda动态表达式。我们先来看如何动态生成lambda表达式。在Linq 中,lambda表达式会首先转化为Expression Tree,本文并不详解Expression Tree。Expression Tree是lambda表达式从code的形式转化为data的结果,是一种更高效的在内存中的数据结构。比如: 
Func<int,int> f = x => x + 1;                               // Code

Expression<Func<int,int>> e = x => x + 1;       // Data

第二个,其实也就是第一个转化后的形式。那好了,有了这个前提,我们就可以动态构造这个Expression Tree了。
// 先构造了一个ParameterExpression对象,这里的c,就是Lambda表达中的参数。(c=>)                           
ParameterExpression param = Expression.Parameter(typeof(TEntity), "c"); 
//构造表达式的右边,值的一边
Expression right = Expression.Constant(p.GetValue(obj, null));
//构造表达式的左边,property一端。
Expression left = Expression.Property(param, p.Name);
//生成筛选表达式。即c.CustomerID == "Tom"
Expression filter = Expression.Equal(left, right);
//生成完整的Lambda表达式。
Expression<Func<TEntity, bool>> pred = Expression.Lambda<Func<TEntity, bool>>(filter, param);
//在这里,我们使用的是and条件。
query = query.Where(pred);

3,反射在本方法中的作用
因为我们采用了模板,也就是说,我们并不知道传进来的对象会有那些property,那反射在这里就提供一个很好的方法。我们可以通过反射去遍历每一个property,只有判断出该property的值不为null时,才将其视为条件。该函数完整的代码如下:

        public IQueryable<TEntity> Find<TEntity>(TEntity obj) where TEntity : class
        
{
            
//获得所有property的信息
            PropertyInfo[] properties = obj.GetType().GetProperties(BindingFlags.Public |
 BindingFlags.Instance);
            
//构造初始的query
            IQueryable<TEntity> query = this.GetTable<TEntity>().AsQueryable<TEntity>();
            
//遍历每个property
            foreach (PropertyInfo p in properties)
            
{
                
if (p != null)
                
{
                    Type t 
= p.PropertyType;
                    
//加入object,Binary,和XDocument, 支持sql_variant,imager 和xml等的影射。
                    if (t.IsValueType || t == typeof(string|| t == typeof(System.Byte[])
                        
|| t == typeof(object|| t == typeof(System.Xml.Linq.XDocument)
                        
|| t == typeof(System.Data.Linq.Binary))
                    
{
                        
//如果不为null才算做条件
                        if ( p.GetValue(obj, null!= null)
                        
{
                            ParameterExpression param 
= Expression.Parameter(typeof(TEntity), "c");
                            Expression right 
= Expression.Constant(p.GetValue(obj, null));
                            Expression left 
= Expression.Property(param, p.Name);
                            Expression filter 
= Expression.Equal(left,right);

                            Expression
<Func<TEntity, bool>> pred = Expression.Lambda<Func
<
TEntity, bool>>(filter, param);
                            query 
= query.Where(pred);
                        }

                    }

                }

            }

            
return query;
        }

系列文章导航:

Linq To Sql进阶系列(一)从映射讲起

Linq To Sql进阶系列(二)M:M关系

Linq To Sql进阶系列(三)CUD和Log

Linq To Sql进阶系列(四)User Define Function篇

Linq To Sql进阶系列(五)Store Procedure篇

Linq To Sql进阶系列(六)用object的动态查询与保存log篇

Linq To Sql进阶系列(七)动态查询续及CLR与SQL在某些细节上的差别


4,测试用例及反思
我们用下面的例子来测试下这个函数

            Customer c = new Customer();
            c.City 
= "London";
            c.Phone 
= "23236133";

            var q 
= db.Find<Customer>(c).ToList();

it知识库Linq To Sql进阶系列(六)用object的动态查询与保存log篇,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。