Entity Framework - 顯示LINQ語法轉成SQL的字串

在用Entity Framework(簡稱EF)時,會常常使用到LINQ語法來對DB資料進行CRUD操作。然而LINQ語法畢竟不是標準的SQL語法,所以每次Debug遇到執行的結果與預期不同時,因為不知道轉出來的SQL是怎樣,所以通常都不知道如何下手。但是不用擔心,因為EF有提供LINQ語法所轉出來的SQL字串LOG,讓開發者可以輕鬆Debug,現在就來看看如何取得轉換出來的SQL字串吧。


在講方法前先來說說EF6與之前版本的差異。在EF之前的版本,建置一個DB連線需要寫一堆Code,譬如ObjectContext的每個ObjectSet屬性都要複寫get方法呼叫CreateObjectSet<T>(""),還有其他哩哩叩叩的程式碼要寫,而EF6為了加快開發速度,將ObjectContext包裝成DbContextObjectSet包裝成DbSet,少寫了1/3左右的程式。再來進入重點,EF6的DbContext中有個Database屬性,Database屬性其實就像是一般ADO.NET中管理DB連線相關功能的集合體,例如DbConnection, DbCommand...等,可以用Database屬性提供的方法來進行SQL的CRUD。EF6在Database中加入一個Log屬性,Log屬性其實就是本篇要介紹的取得LINQ轉換出的SQL字串方法,下面來看看MSDN上說明Database.Log的屬性是什麼東東:



從MSDN說明看來,可以知道EF在執行的時候會產生LOG,這些LOG如果沒有指定處理方式的話就不會顯示出來,因此才有了Log這個屬性。Log屬性為Action<string>的Type,就是EF回傳入String型態的資料到你所指定的Function裡進行處理。以下限量會以兩種情境來使用EF的Log。第一種為在Console App中將EF轉換出的SQL字串印出來;第二種為在VS Debug時看EF轉換出的SQL字串。在範例中,先進行DELETE動作將資料表清空,再來新增一筆資料,最後將資料表內所有資料撈出,這三個動作會有三個SQL,下面就來看看範例產生的SQL長得如何。

情境一(Console印出)


範例程式碼:

class Program
{
 static void Main( string[] args )
 {
  using (var db = new PTMContext())
  {
   // 將Log用Console.Write寫出
   db.Database.Log = Console.Write;
   // 刪除所有USERINFO TABLE資料
   db.Database.ExecuteSqlCommand("DELETE FROM USERINFO");
   // 新增一筆USERINFO
   var user = new UserInfo
   {
    Id = Guid.NewGuid(),
    Account = "Admin",
    Name = "Limited",
    Nickname = "Administrator",
    Password = "admin123",
    Email = "Test@Test.com",
    PhoneNum = "0912345678",
    AddDate = DateTime.Now,
    AddUserId = "Test",
    ModDate = DateTime.Now,
    ModUserId = "Test"
   };
   db.Users.Add(user);
   // Commit
   db.SaveChanges();
   // 取得所有USERINFO TABLE資料
   var users = db.Users.ToList();
   // 印出TABLE資料
   PrintTable<UserInfo>(users);
  }
  
  Console.ReadLine();
 }
 
 /// <summary>
 /// Console印出Table資料
 /// </summary>
 /// <typeparam name="T">Table Mapping Model</typeparam>
 /// <param name="list">Records</param>
 static void PrintTable<T>(IEnumerable<T> list) where T : class
 {
  var type = typeof(T);
  var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
  var colNames = props.Select(x => x.Name);
  var headerStr = string.Join(" | ", colNames);

  Console.WriteLine(type.Name);
  Console.WriteLine(headerStr);
  for (var i = 0; i < headerStr.Length; i++)
  {
   Console.Write("-");
  }
  Console.WriteLine();

  foreach (var row in list)
  {
   var rowStr = string.Empty;
   foreach (var prop in props)
   {
    var propVal = prop.GetValue(row);
    rowStr = string.Join(" | ", rowStr, propVal == null ? string.Empty : propVal.ToString());
   }
   Console.WriteLine(rowStr);
   for (var i = 0; i < rowStr.Length; i++)
   {
    Console.Write("-");
   }
   Console.Write("\n\n\n");
  }

 }
}


執行結果:



情境二(在VS的輸出視窗中顯示)


將Database.Log修改成以下:

db.Database.Log = log => System.Diagnostics.Debug.Write(log);

執行結果:


這樣就能輕輕鬆鬆的在Debug階段Trace用LINQ下的SQL語法是否正確了。取到了SQL字串後就可以用在其他應用上,像是寫出成檔案或丟給Log模組寫Log...等。



參考來源:
One Unicorn - EF6 SQL Logging – Part 1: Simple Logging







留言