本文中舉例用到的資料模型如下:
Student和Class之間是多對一關聯性,Student和Course之間是多對多關係。
DataContext的DeferredLoadingEnabled屬性指定是否需要延時載入,其預設值為true。以Student為例,其延時載入的對象是指Class和對應的Course。設定延時載入為true時,當訪問到Student執行個體的Class屬性或者StudentCourse屬性時會自動載入Class表和StudentCourse表中的資料,如下範例程式碼:
static void Main(string[] args){ using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8)) { using (DbAppDataContext db = new DbAppDataContext()) { db.Log = writer; //設定延時載入屬性為true db.DeferredLoadingEnabled = true; //獲得一個Student var aStudent = db.Students.First(); //直接存取Student的Class屬性ClassName Console.WriteLine("{0}屬於{1}",aStudent.Name ,aStudent.Class.ClassName); } } Console.ReadLine();}
當設定DataContext的DeferredLoadingEnabled屬性為true時,可以直接存取關係表中的資料,我們可以看下以上代碼使用到的SQL語句:
SELECT TOP (1) [t0].[StudentID], [t0].[Name], [t0].[Hometown], [t0].[Gender], [t0].[Birthday], [t0].[ClassID], [t0].[WeightInKg], [t0].[HeightInCm], [t0].[Desc] AS [Desc]FROM [dbo].[Student] AS [t0]-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1SELECT [t0].[ClassID], [t0].[ClassName]FROM [dbo].[Class] AS [t0]WHERE [t0].[ClassID] = @p0-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [1]-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1
用到了兩個sql語句,在我們訪問到Student的Class屬性時,DataContext自動去載入了Class的資料。
在有些時候可以將DeferredLoadingEnabled屬性設定為false,設定為false時再直接存取Student的Class屬性時將拋出Null 參考的異常。
有了延時載入,LoadWith方法是做什麼用的呢?
MSDN上對LoadWith的解釋是:通過使用 lambda 運算式檢索與主目標相關的指定資料。
LoadWith不是DataContext的方法,而是DataLoadOptions的方法,可以給DataContext設定LoadOptions屬性來改變DataContext載入資料的方式;換句話說是LoadOptions設定是否在select主表資料時同時用join載入關聯表的資料。
假定情境:我希望在select Student表的資料時同時用一個sql將其class的屬性select出來,如下代碼:
static void Main(string[] args){ using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8)) { using (DbAppDataContext db = new DbAppDataContext()) { db.Log = writer; //聲明DataLoadOptions變數 var loadOptions = new DataLoadOptions(); //設定需要在load Student時需要同時load什麼 loadOptions.LoadWith<Student>(s=>s.Class); //將DataLoadOptions執行個體賦值給db.LoadOptions db.LoadOptions = loadOptions; //載入一個Student var student = db.Students.First(); Console.WriteLine("{0}屬於{1}",student.Name,student.Class.ClassName); } } Console.ReadLine();}
其效果和上面例子完全一樣,但是執行的SQL卻是不一樣的,我們看下SQL:
SELECT TOP (1) [t0].[StudentID], [t0].[Name], [t0].[Hometown], [t0].[Gender], [t0].[Birthday], [t0].[ClassID], [t0].[WeightInKg], [t0].[HeightInCm], [t0].[Desc] AS [Desc], [t1].[ClassID] AS [ClassID2], [t1].[ClassName]FROM [dbo].[Student] AS [t0]INNER JOIN [dbo].[Class] AS [t1] ON [t1].[ClassID] = [t0].[ClassID]-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1
這次執行只用了一個SQL語句,Student表inner join Class表,這就是LoadWith的意義所在。
LoadWith可以用一個sql語句載入相關表的資料,那麼AssociateWith 方法又是做什麼用的呢?
還是假定一個情境,我們想知道某個班級的資訊和這個班級中體重大於30公斤的學生的資訊,也就是說在取得關聯表資料時附加了條件,請看下面的代碼及注釋:
static void Main(string[] args){ using (var writer = new StreamWriter(WatchSqlPath, false, Encoding.UTF8)) { using (DbAppDataContext db = new DbAppDataContext()) { db.Log = writer; var loadOptions = new DataLoadOptions(); //在載入Class的Students屬性時附加上體重大於30的條件 loadOptions.AssociateWith<Class>(c => c.Students.Where(s => s.WeightInKg > 30)); db.LoadOptions = loadOptions; //將DataLoadOptions執行個體賦值給db.LoadOptions db.LoadOptions = loadOptions; //取得id為1的班級 var aClass = db.Classes.Where(c=>c.ClassID == 1).Single(); Console.WriteLine("{0}體重大於30kg的同學有:",aClass.ClassName); foreach (var item in aClass.Students) { Console.WriteLine("\t{0}",item.Name); } } } Console.ReadLine();}
同樣看下代碼執行的真實SQL語句:
SELECT [t0].[ClassID], [t0].[ClassName]FROM [dbo].[Class] AS [t0]WHERE [t0].[ClassID] = @p0-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [1]-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1SELECT [t0].[StudentID], [t0].[Name], [t0].[Hometown], [t0].[Gender], [t0].[Birthday], [t0].[ClassID], [t0].[WeightInKg], [t0].[HeightInCm], [t0].[Desc] AS [Desc]FROM [dbo].[Student] AS [t0]WHERE ([t0].[WeightInKg] > @p0) AND ([t0].[ClassID] = (( SELECT [t2].[ClassID] FROM ( SELECT TOP (1) [t1].[ClassID] FROM [dbo].[Class] AS [t1] WHERE [t1].[ClassID] = @p1 ) AS [t2] )))-- @p0: Input Float (Size = -1; Prec = 0; Scale = 0) [30]-- @p1: Input Int (Size = -1; Prec = 0; Scale = 0) [1]-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 4.0.30319.1
可以看到是兩個sql語句,第二個sql語句中AssociateWith起了作用,第一個條件是體重大於@p0,第二個條件是classID;可以看到這兒產生的sql語句很不好,ClassID是唯一主鍵,按理說DataContext已經知道這事兒,但是在產生語句時還是用了嵌套的查詢,可以說是LINQ to SQL的一個敗筆。
linq to sql相關隨筆:
1. 從CUD開始,如何使用LINQ to SQL插入、修改、刪除資料
2. 查詢 使用LINQ to SQL做簡單查詢
3. 查詢 消極式載入與立即載入,使用LoadWith和AssociateWith
4. 查詢 inner join,left outer join
5. Linq to SQL中的彙總grouping having
6. LINQ to SQL查詢最佳化,需要憂慮效能嗎?