程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> Linq To Sql進階系列(六)用object的動態查詢與保存log篇

Linq To Sql進階系列(六)用object的動態查詢與保存log篇

編輯:關於.NET

動態的生成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......
}

好,就先講到這裡。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved