學(xué)習(xí)筆記 Linq to sql動(dòng)態(tài)查詢(xún)
本文從四個(gè)方面對(duì)Linq to sql動(dòng)態(tài)查詢(xún)進(jìn)行了闡述,這其中既有原理,又有實(shí)例,是大家休學(xué)習(xí)Linq to sql動(dòng)態(tài)查詢(xún)的好資料。
動(dòng)態(tài)的生成sql語(yǔ)句,根據(jù)不同的條件構(gòu)造不同的where字句,是拼接sql 字符串的好處。而Linq的推出,是為了彌補(bǔ)編程中的 Data != Object 的問(wèn)題。我們又該如何實(shí)現(xiàn)用object的Linq to sql動(dòng)態(tài)查詢(xún)呢?
1,Linq to sql動(dòng)態(tài)查詢(xún)之用object的查詢(xún)是什么?
我們可以簡(jiǎn)單的舉這么一個(gè)例子。我們到公安局查找一個(gè)人。首先,我們會(huì)給出他的一些特征,比如,身高多少,年齡多少,性別,民族等。那么,我們把這個(gè)人的一些特征輸入電腦。我們希望,電腦能給我們返回這個(gè)人的信息。
而實(shí)際上,有相同特征的人太多了,常常返回一個(gè)集合。那讓我們把這個(gè)過(guò)程抽象到程式里。我們需要new出來(lái)一個(gè)對(duì)象。這個(gè)對(duì)象包含了我們能知道的基本信息。而后,把這個(gè)對(duì)象傳給Linq To Sql,等待返回結(jié)果。
根據(jù)這些基本的需求,我們來(lái)定義下面的函數(shù),為了實(shí)現(xiàn)這個(gè)函數(shù)對(duì)任何實(shí)體都是有用的,我們把它定義為generic的。為了不破壞Linq To Sql延遲加載的規(guī)矩,我們把它的返回類(lèi)型定義為IQueryable。如下:
public IQueryable
思路出來(lái)了,先new出來(lái)一個(gè)對(duì)象,然后把對(duì)象傳給這個(gè)函數(shù),我們渴望它能返回與這個(gè)對(duì)象匹配的結(jié)果集。為了讓它和DataContext有關(guān)系,我們把這個(gè)函數(shù)放到DataContext的partial類(lèi)里。
鼠標(biāo)右擊Linq To Sql文件,選擇view code,這個(gè)時(shí)候,vs會(huì)為你創(chuàng)造一個(gè)DataContext的partial類(lèi),其擴(kuò)展名比影射文件少了中間的desiger。大家要注意,你如果想自己修改影射文件,請(qǐng)放到這個(gè)文件里。這樣當(dāng)影射code被刷新時(shí),才不會(huì)沖掉你自己的修改。先大體描述下我們的思路。
- NorthwindDataContext db = new NorthwindDataContext();
- //先new出一個(gè)對(duì)象
- Customer c = new Customer();
- //添入我們知道的最基本的信息,可以從ui獲得
- c.City = "London";
- c.Phone = "23236133";
- //call函數(shù)find返回結(jié)果
- var q = db.Find<Customer>(c);
2,Linq to sql動(dòng)態(tài)查詢(xún)之原理
Linq To Sql動(dòng)態(tài)查詢(xún)支持用戶(hù)動(dòng)態(tài)生成lambda表達(dá)式。本文中所實(shí)現(xiàn)的方法,正是反射加lambda動(dòng)態(tài)表達(dá)式。我們先來(lái)看如何動(dòng)態(tài)生成lambda表達(dá)式。
在 Linq 中,lambda表達(dá)式會(huì)首先轉(zhuǎn)化為Expression Tree,本文并不詳解Expression Tree。Expression Tree是lambda表達(dá)式從code的形式轉(zhuǎn)化為data的結(jié)果,是一種更高效的在內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)。比如:
- Func<int,int> f = x => x + 1; // Code
- Expression<Func<int,int>> e = x => x + 1; // Data
第二個(gè),其實(shí)也就是***個(gè)轉(zhuǎn)化后的形式。那好了,有了這個(gè)前提,我們就可以動(dòng)態(tài)構(gòu)造這個(gè)Expression Tree了。
- // 先構(gòu)造了一個(gè)ParameterExpression對(duì)象,這里的c,就是Lambda表達(dá)中的參數(shù)。(c=>)
- ParameterExpression param = Expression.Parameter(typeof(TEntity), "c");
- //構(gòu)造表達(dá)式的右邊,值的一邊
- Expression right = Expression.Constant(p.GetValue(obj, null));
- //構(gòu)造表達(dá)式的左邊,property一端。
- Expression left = Expression.Property(param, p.Name);
- //生成篩選表達(dá)式。即c.CustomerID == "Tom"
- Expression filter = Expression.Equal(left, right);
- //生成完整的Lambda表達(dá)式。
- Expression<Func<TEntity, bool>> pred =
- Expression.Lambda<Func<TEntity, bool>>(filter, param);
- //在這里,我們使用的是and條件。
- queryquery = query.Where(pred);
3,Linq to sql動(dòng)態(tài)查詢(xún)之反射在本方法中的作用
因?yàn)槲覀儾捎昧四0?,也就是說(shuō),我們并不知道傳進(jìn)來(lái)的對(duì)象會(huì)有那些property,那反射在這里就提供一個(gè)很好的方法。我們可以通過(guò)反射去遍歷每一個(gè)property,只有判斷出該property的值不為null時(shí),才將其視為條件。該函數(shù)完整的代碼如下:
- public IQueryable<TEntity> Find<TEntity>(TEntity obj) where
- TEntity : class
- {
- //獲得所有property的信息
- PropertyInfo[] properties =
- obj.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
- //構(gòu)造初始的query
- IQueryable<TEntity> query =
- this.GetTable<TEntity>().AsQueryable<TEntity>();
- //遍歷每個(gè)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);
- queryquery = query.Where(pred);
- }
- }
- }
- }
- return query;
- }
4,Linq to sql動(dòng)態(tài)查詢(xún)之測(cè)試用例及反思
我們用下面的例子來(lái)測(cè)試下這個(gè)函數(shù)
- Customer c = new Customer();
- c.City = "London";
- c.Phone = "23236133";
- var q = db.Find<Customer>(c).ToList();
其生成的sql語(yǔ)句為:
- 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]
我們可以看到,其Linq to sql動(dòng)態(tài)查詢(xún)語(yǔ)句中,只有city和phone兩個(gè)條件。并且他們之間是and的關(guān)系。我們最開(kāi)始的設(shè)想實(shí)現(xiàn)了,但是,它是***的嗎?如果是or條件該怎么辦呢?更多的時(shí)候,我們是在用模糊查詢(xún),那又該怎么辦呢?這個(gè)問(wèn)題,就留于下篇。
***,介紹一種寫(xiě)log的方法。stream流使用static為避免多個(gè)datacontext的同時(shí)在使用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函數(shù)里,把輸出流flush。使用時(shí),如下
- using(northwind db = new norhwind(true))
- {
- //do something......
- }
以上就是筆者和大家分享的關(guān)于Linq to sql動(dòng)態(tài)查詢(xún)的知識(shí)。
【編輯推薦】