2009年12月22日 星期二

LINQ Deferred VS Immediate Data Loading

使用LINQ To SQL的時候,預設的行為是Deferred Loading,意思是說當你下了一段LINQ時,程式並不會馬上對後端的資料庫進行讀取的行為,而是等到你對LINQ的結果集進行存取的時候才會對後端資料庫進行存取。範例如下

       var q = from c in context.Employees
                select new { c.FirstName, c.EmployeeID, c.City, c.Country};

        
        GridView1.DataSource = q;
        GridView1.DataBind();  //執行DataBind的時候才會真的對後端資料庫進行讀取的動作



這樣子是為了避免不必要的資料庫Loading,但某些情況之下,你卻必須要馬上獲得LINQ To SQL的結果,這個時候最簡單的方法就是使用ToList()的功能,因為這個方法就是把結果及轉換為一個List的集合物件,所以勢必會在使用此方法的時候就馬上對資料庫進行讀取,這就是Immediate data Lodaing的功能,目前我所知道LINQ To SQL好像也只有這樣的方式是比較方便的,也比較彈性。

今天遇到一個問題剛好需要使用這樣的功能,問題如下
我的資料庫資料如下(註:使用上面的程式碼結果)

該資料中最後一筆資料是我所新增,而Conutry其實是null,不過我要新增一個欄位叫做Code,這個欄位是經過一個額外定義的Method經過運算而成,該方法之程式碼如下

public class Class1
{
    public static string GetCheckCode(string str)
    {
        if (str == null) return "null";

        if (str.Length == 2)
            return "A";
        else
            return "B";
    }
}


而利用LINQ To SQL的功能再透過底下程式碼我可以得到Code的欄位

 
var q = from c in context.Employees
                select new { c.FirstName, c.EmployeeID, c.City, c.Country, Code = Class1.GetCheckCode(c.Country) };
GridView1.DataSource = q;
        GridView1.DataBind();


可獲得結果如下

而現在我想利用這個多餘的欄位來進行過濾,例如,我想取得Code == "A"的結果集,直覺上你會很自然的使用底下的程式碼

  
var q = from c in context.Employees
                where Class1.GetCheckCode(c.Country) == "A"
                select new { c.FirstName, c.EmployeeID, c.City, c.Country, Code = Class1.GetCheckCode(c.Country) };

 GridView1.DataSource = q;
        GridView1.DataBind();    

但是這樣卻會發生Exception,錯誤訊息如下

這是因為LINQ To SQL並沒有支援使用客製化方法來進行條件的過濾(有興趣的可以參考LINQ To SQL的相關轉譯介紹),此時解決的方式就把腦筋動到LINQ To Object了,因為針對一個記憶體中結果集去執行這樣的過濾條件是很稀鬆平常的事情,所以我們再把程式修改如下

  
//先進行LINQ To SQL查詢
var q = from c in context.Employees
                select new { c.FirstName, c.EmployeeID, c.City, c.Country, Code = Class1.GetCheckCode(c.Country) };

//利用ToList()方法,進行Immediate Data Loading
var q2 = from c in q.ToList()
                 where c.Code == "A"
                 select c;

 GridView1.DataSource = q2;
        GridView1.DataBind();    


其結果如下

這就是我們要的結果集。

結論:
LINQ真的是一個必學的技能,原本我以為只是微軟從Java那抄Hibernet的一個solution,不過我大大的錯了,正確的來說Hibernet只不過符合LINQ一部分的功能而已,雖然這這個範例中使用的是LINQ To SQL,也就是MS SQL only,不過想要使用其他資料庫的話,其實只要去找相關的Provider還是可以使用(當然,這樣的方便性就降低了,不像Hibernet以一擋百),但微軟再後面其實還有一個更強的殺手級應用Entity Framework,這就可以解決掉不同資料庫的問題,而且功能更強大。但是這還只是針對資料庫而言,以LINQ的技術來說,主要需要知道的技能應該如下
  1. LINQ To SQL
  2. LINQ To Object
  3. LINQ To XML
其他LINQ的相關應用,都是架構再這三個上面,以本文章的例子就需要知道LINQ To SQL以及LINQ To Object方能解決,這在Hibernet上面好像沒辦法如此方便,總之LINQ贏了。

沒有留言: