在ASP.NET中觀察LINQ to SQL所產生的T-SQL語法

接連在好幾個小專案裡用了LINQ to SQL,慢慢掌握要領,煎、煮、炒、炸查詢、新增、修改、刪除,各種料理操作都已能手到擒來,就愈發感受到它的便利性。

說穿了,LINQ to SQL只不過是ORM的一種具體實踐,並無深奧學問,之所以用來得心應手、讓人驚豔,不外乎是在與Visual Studio 2008整合深度上佔了優勢。以一個開發者的角度而言,我不在乎這對其他解決方案是否公允? 也不關心這類綁標圖利是否會有爭議? 給我方便的開發工具,其餘免談。

過去曾用ADO/ADO.NET開發過很長一段時間,在效能議題上下過一些功夫。切換到LINQ to SQL後,完全不沾SqlConnection、SqlCommand、SqlParameter就能搞定與資料庫相關的大小事,固然讓人心曠神怡;但過去對效能斤斤計較,換到LINQ,我還是常常懷疑LINQ所自動轉譯成的T-SQL到底長得什麼德性,會不會荒腔走板、效能低落?

要解除疑慮,最直接有效的方法就是檢查LINQ to SQL所產出的T-SQL語法,沒有什麼比眼見為憑更具說服力了! System.Data.Linq.DataContext類別有個屬性叫Log,我們可以接上一個TextWriter,DataContext會在執行T-SQL指令時,輸出實際使用的T-SQL語法、參數細節,提供極佳的觀察與偵錯資訊。

不過,如MSDN文件所示,能找到的Log應用範例幾乎都是接上Console.Out適用於Console Application,如果我們是在網頁中執行,想要如同System.Diagnositcs.Debug.WriteLine一般輸出在VS2008的偵錯輸出視窗,該怎麼做呢?

我找到了Kris Vadermotten寫的DebuggerWriter類別,可以滿足以上的需求:

using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Text;
 
    /// <summary>
    /// Original by Kris Vadermotten: http://www.u2u.info/Blogs/Kris/Lists/Posts/Post.aspx?ID=11 <br />
    /// Remixed by Jeffrey Lee, 2009-07-11 http://blog2.darkthread.net<br />
    /// Implements a <see cref="TextWriter"/> for writing information to the debugger log. 
    /// </summary>
    /// <seealso cref="Debugger.Log"/>
    public class DebuggerWriter : TextWriter
    {
        private bool isOpen;
        private static UnicodeEncoding encoding =
            new UnicodeEncoding(false, false);
        public int Level { get; private set; }
        public string Category { get; private set; }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="DebuggerWriter"/> class.
        /// </summary>
        public DebuggerWriter()
            : this(0, Debugger.DefaultCategory) { }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="DebuggerWriter"/> class with the specified level and category.
        /// </summary>
        /// <param name="level">A description of the importance of the messages.</param>
        /// <param name="category">The category of the messages.</param>
        public DebuggerWriter(int level, string category)
            : this(level, category, CultureInfo.CurrentCulture) { }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="DebuggerWriter"/> class with the specified level, category and format provider.
        /// </summary>
        /// <param name="level">A description of the importance of the messages.</param>
        /// <param name="category">The category of the messages.</param>
        /// <param name="formatProvider">An <see cref="IFormatProvider"/> object that controls formatting.</param>
        public DebuggerWriter(int level, string category, IFormatProvider formatProvider)
            : base(formatProvider)
        {
            Level = level;
            Category = category;
            this.isOpen = true;
        }
 
        protected override void Dispose(bool disposing)
        {
            isOpen = false;
            base.Dispose(disposing);
        }
 
        public override void Write(char value)
        {
            if (!isOpen)
                throw new ObjectDisposedException(null);
            Debugger.Log(Level, Category, value.ToString());
        }
 
        public override void Write(string value)
        {
            if (!isOpen)
                throw new ObjectDisposedException(null);
            if (value != null)
                Debugger.Log(Level, Category, value);
        }
 
        public override void Write(char[] buffer, int index, int count)
        {
            if (!isOpen)
                throw new ObjectDisposedException(null);
            if (buffer == null || index < 0 || count < 0 || buffer.Length - index < count)
                base.Write(buffer, index, count); // delegate throw exception to base class
            Debugger.Log(Level, Category, new string(buffer, index, count));
        }
 
        public override Encoding Encoding
        {
            get { return encoding; }
        }
 
    }

將以上的DebuggerWriter.cs放入App_Code,然後youDataContext.Log = new DebuggerWriter(),設定Breakpoint,再一列一列Debug,你就可以觀察到何時LINQ to SQL會執行什麼樣的T-SQL指令,甚至包含參數值等細節,很犀利吧?

 

以上的範例,其實會觸發兩次SQL查詢,若要再精簡,可以改寫成:

var q = (from o in db.Players
        where o.ID < 20
        select o).ToList<Player>();

大家實際動手玩玩便知。

【2009-07-12補充】艾小克提供可以整合在VS2008裡,在開發階段檢視查詢所對應T-SQL並可直接試連DB做查詢的工具一枚,十分實用,一併列出供大家參考。

【2009-07-13補充】本草綱目有記載,若要查Query對應的CommandText,可以用DataContext.GetCommand,Insert/Update/Delete的部分則還沒看到Log法的替代方案,如有線報,歡迎提供。

歡迎推文分享:
Published 12 July 2009 05:33 PM 由 Jeffrey
Filed under: , ,
Views: 19,311



意見

# demo said on 12 July, 2009 09:09 AM

我都是使用LinqPad直接看他會丟什麼給我個人覺得還滿好用的

demo.tc/view.aspx

# jane said on 14 October, 2010 01:34 AM

你的文章幫助我很多~~謝謝^^

幫我更快能找出LINQ的問題

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<July 2009>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication