[LINQ2SQL]DeferredLoadingEnabled=false ?!

[LINQ2SQL]DeferredLoadingEnabled=false ?!

Model: Parent.Id –>Child.Parent

Using dbml

Database: SQL Server 2008 express (mdb)

 

初始值

   1:          private void Initial()
   2:          {
   3:              using (DataClasses1DataContext ctx = new DataClasses1DataContext())
   4:              {
   5:                  var c = from _c in ctx.Child select _c;
   6:                  ctx.Child.DeleteAllOnSubmit(c);
   7:                  var p = from _p in ctx.Parent select _p;
   8:                  ctx.Parent.DeleteAllOnSubmit(p);
   9:                  ctx.SubmitChanges();
  10:   
  11:                  ctx.Parent.InsertOnSubmit(new Parent() { Id = 1, Name = "Lee" });
  12:                  ctx.Parent.InsertOnSubmit(new Parent() { Id = 2, Name = "Wang" });
  13:                  ctx.Child.InsertOnSubmit(new Child() { Id = 1, Name = "Hector", Parent = 1 });
  14:                  ctx.Child.InsertOnSubmit(new Child() { Id = 2, Name = "Han", Parent = 1 });
  15:                  ctx.SubmitChanges();
  16:              }
  17:          }

原先code, Parent p.Child 是有內容的, 筆數=2

 

   1:              using (DataClasses1DataContext ctx = new DataClasses1DataContext())
   2:              {
   3:                  ctx.Log = Console.Out;
   4:                  var v = from _v in ctx.Parent
   5:                          where _v.Id == 1
   6:                          select _v;
   7:                  foreach (Parent p in v)
   8:                  {                    
   9:                      Assert.AreEqual(2, p.Child.Count);
  10:                  }
  11:              }

 

但它Generate的sql, 卻有著效能的issue, 因為它會多次送出SQL 查詢Child.

   1:  SELECT [t0].[Id], [t0].[Name]
   2:  FROM [dbo].[Parent] AS [t0]
   3:  WHERE [t0].[Id] = @p0
   4:  -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
   5:  -- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.5420
   6:   
   7:  SELECT [t0].[Id], [t0].[Name], [t0].[Parent]
   8:  FROM [dbo].[Child] AS [t0]
   9:  WHERE [t0].[Parent] = @p0
  10:  -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
  11:  -- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.5420

故我參考MSDN: http://msdn.microsoft.com/zh-tw/library/bb399393(v=VS.90).aspx

將 DataContext.DeferredLoadingEnabled=false, 結果

Assert.AreEqual 失敗。預期: <2>。實際: <0>。

疑?! 它不會主動將關聯物件引入...

 

後來又參考MSDN: http://msdn.microsoft.com/zh-tw/library/Bb386920(v=VS.90).aspx

這才是正解. 因為我一直鬼打牆在設定屬性就應該可以, 所以花了很多時間在Try&Error,

最終才確認DataLoadOptions.LoadWith是必然解 ...

   1:              using (DataClasses1DataContext ctx = new DataClasses1DataContext())
   2:              {
   3:                  DataLoadOptions lo = new DataLoadOptions();                
   4:                  lo.LoadWith<Parent>(p => p.Child);
   5:                  ctx.LoadOptions = lo;
   6:   
   7:                  ctx.Log = Console.Out;
   8:                  ctx.DeferredLoadingEnabled = false;
   9:   
  10:                  var v = from _v in ctx.Parent
  11:                          where _v.Id == 1
  12:                          select _v;
  13:                  Assert.AreEqual(1, v.Count());
  14:   
  15:                  foreach (Parent p in v)
  16:                  {
  17:                      Assert.AreEqual(2, p.Child.Count);
  18:                  }
  19:              }

果然只有一組SQL, 雖然跟我預期的INNER JOIN有差距 ...

   1:  SELECT [t0].[Id], [t0].[Name], [t1].[Id] AS [Id2], [t1].[Name] AS [Name2], [t1].[Parent], (
   2:      SELECT COUNT(*)
   3:      FROM [dbo].[Child] AS [t2]
   4:      WHERE [t2].[Parent] = [t0].[Id]
   5:      ) AS [value]
   6:  FROM [dbo].[Parent] AS [t0]
   7:  LEFT OUTER JOIN [dbo].[Child] AS [t1] ON [t1].[Parent] = [t0].[Id]
   8:  WHERE [t0].[Id] = @p0
   9:  ORDER BY [t0].[Id], [t1].[Id]
  10:  -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
  11:  -- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.5420


若你用SQL 執行會出現2筆RECORD, 但Parent 並沒有因此而變2筆, 而是Grouping起來, 只有1筆, 而p.Child有2筆.

------------------------------------

BTW, 如果要INNSER JOIN , 語法改成

   1:                  var v = from _v in ctx.Parent
   2:                          from _c in _v.Child
   3:                          where _v.Id == 1
   4:                          select new { p=_v, c=_c };

結果合乎預期

   1:  SELECT [t0].[Id], [t0].[Name], [t1].[Id] AS [Id2], [t1].[Name] AS [Name2], [t1].[Parent]
   2:  FROM [dbo].[Parent] AS [t0], [dbo].[Child] AS [t1]
   3:  WHERE ([t0].[Id] = @p0) AND ([t1].[Parent] = [t0].[Id])
   4:  -- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [1]
   5:  -- Context: SqlProvider(Sql2008) Model: AttributedMetaModel Build: 3.5.30729.5420

但集合物件的結果就不同了, 變成每一個集合都有Parent物件, 相當於SQL的結果.

我順便測了一下, 雖然Parent有重覆出現, 但其實都是同一個物件.

   1:                  int count = 0;
   2:                  Parent p1 = null;
   3:                  Parent p2 = null;
   4:                  foreach (var p in v)
   5:                  {
   6:                      if (count == 0)
   7:                      {
   8:                          p1 = p.p;
   9:                      }
  10:                      else
  11:                      {
  12:                          p2 = p.p;
  13:                      }
  14:   
  15:                      count++;
  16:                  }
  17:                  Assert.AreEqual(p1, p2);
  18:                  Assert.AreSame(p1, p2);

不過Parent.Child 還是Empty...

 

 

結語: 後來才看到這篇 http://dotnetslackers.com/articles/csharp/Load-Lazy-in-LINQ-to-SQL.aspx