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

  • Post author:
  • Post category:其他


动态的生成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

思路出来了,先new出来一个对象,然后把对象传给这个函数,我们渴望它能返回与这个对象匹配的结果集。为了让它和DataContext有关系,我们把这个函数放到DataContext的partial类里。鼠标右击Linq To Sql文件,选择view code,这个时候,vs会为你创造一个DataContext的partial类,其扩展名比影射文件少了中间的desiger。大家要注意,你如果想自己修改影射文件,请放到这个文件里。这样当影射code被刷新时,才不会冲掉你自己的修改。先大体描述下我们的思路。



NorthwindDataContext db


=




new


NorthwindDataContext();




//


先new出一个对象






Customer c


=




new


Customer();




//


添入我们知道的最基本的信息,可以从ui获得






c.City


=







London





;


c.Phone


=







23236133





;




//


call函数find返回结果






var q


=


db.Find


<


Customer


>


(c);

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;


}





4,测试用例及反思

我们用下面的例子来测试下这个函数



Customer c


=




new


Customer();


c.City


=







London





;


c.Phone


=







23236133





;




var q


=


db.Find


<


Customer


>


(c).ToList();

其生成的sql语句为:



SELECT




[


t0


]


.


[


CustomerID


]


,


[


t0


]


.


[


CompanyName


]


,


[


t0


]


.


[


ContactName


]


,


[


t0


]


.


[


ContactTitle


]


,


[


t0


]


.


[


Address


]


,


[


t0


]


.


[


City


]


,


[


t0


]


.


[


Region


]


,


[


t0


]


.


[


PostalCode


]


,


[


t0


]


.


[


Country


]


,


[


t0


]


.


[


Phone


]


,


[


t0


]


.


[


Fax


]






FROM




[


dbo


]


.


[


Customers


]




AS




[


t0


]






WHERE


(


[


t0


]


.


[


Phone


]




=




@p0


)


AND


(


[


t0


]


.


[


City


]




=




@p1


)







@p0: Input NVarChar (Size = 8; Prec = 0; Scale = 0) [23236133]







@p1: Input NVarChar (Size = 6; Prec = 0; Scale = 0) [London]

我们可以看到,其sql语句中,只有city和phone两个条件。并且他们之间是and的关系。我们最开始的设想实现了,但是,它是完美的吗?如果是or条件该怎么办呢?更多的时候,我们是在用模糊查询,那又该怎么办呢?这个问题,就留于下篇。

最后,介绍一种写log的方法。stream流使用static为避免多个datacontext的同时在使用log.txt文件。



partial


class


DataMappingDataContext









{





private




static


StreamWriter sw


=




new


StreamWriter(Path.Combine(Directory.GetCurrentDirectory(),





log.txt





),


true


);







/**/



///




<summary>






///


Try to create DataContext with log.




///




</summary>






///




<param name=”withLog”></param>









public


DataMappingDataContext(


bool


withLog)


:


this


()









{



OnCreated();




if


(withLog)









{





if


(sw


==




null


)









{



sw


=




new


StreamWriter(Path.Combine(Directory.GetCurrentDirectory(),





log.txt





),


true


);


}







this


.Log


=


sw;


}





}










/**/



///




<summary>






///


try to close streamwriter




///




</summary>






///




<param name=”disposing”></param>







protected




override




void


Dispose(


bool


disposing)









{





base


.Dispose(disposing);


sw.Flush();




}





}

在dispose函数里,把输出流flush。使用时,如下

using(northwind db = new norhwind(true))

{


//do something……

}

好,就先讲到这里。

TrackBack:

http://www.cnblogs.com/126/archive/2007/09/09/887723.html

转载于:https://www.cnblogs.com/hdjjun/archive/2008/11/05/1327096.html