The Will Will Web | 解決 N-Tier 多層架構設計下 LINQ to SQL 效能不彰的問題

The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

解決 N-Tier 多層架構設計下 LINQ to SQL 效能不彰的問題

ASP.NET MVC 的開發原則有個 SoC (Separation of Concern) 的觀念,我們在開發較大型的 ASP.NET MVC 應用程式時會特別將資料存取層(Data Access Layer) 再細分為兩個層次,分別是 Repository Layer (資料倉儲層) 負責資料存取與欄位格式驗���,與 Service Layer (服務提供層) 負責資料篩選與商業邏輯驗證,但分層之後遇到了一個之前沒想過的問題,進而導致 LINQ to SQL 查詢效能不彰。

我寫了一個很小的專案來驗證這個問題:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication2
{
    class Program
    {
        private static MyDataContext db;

        static void Main(string[] args)
        {
            db = new MyDataContext();
            db.Log = Console.Out;

            var all = GetCustomers();

            Console.WriteLine("Total Rows: " + all.Count());
            Console.WriteLine();

            var page1 = all.Where(p => p.LastName == "Liu");

            Console.WriteLine("Total Rows: " + page1.Count());
        }

        private static IEnumerable<Customer> GetCustomers()
        {
            return db.Customers;
        }
    }
}

由上述程式可得知,我定義了一個 GetCustomers() 方法,用來回傳資料庫中所有 Customers 表格的資料,我為了將回傳的資料抽象化,所以改用 IEnumerable<T> 型別回傳,執行的結果如下:

 

兩次執行 Count() 查詢時都沒有最佳化,而是將所有資料都回傳回來變成 Entity 物件後才對這些物件進行彙整運算。

這時,我們稍微修改一下程式,在透過 GetCustomers() 取回結果的時候加上 AsQueryable() 擴充方法將結果轉型成 IQueryable<T> 型別,請看以下程式第 16 行的地方:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication2
{
    class Program
    {
        private static MyDataContext db;

        static void Main(string[] args)
        {
            db = new MyDataContext();
            db.Log = Console.Out;

            var all = GetCustomers().AsQueryable();

            Console.WriteLine("Total Rows: " + all.Count());
            Console.WriteLine();

            var page1 = all.Where(p => p.LastName == "Liu");

            Console.WriteLine("Total Rows: " + page1.Count());
        }

        private static IEnumerable<Customer> GetCustomers()
        {
            return db.Customers;
        }
    }
}

我們看一下修改過後的結果:

你會發現雖然結果一樣,但是程式執行的效率卻差非常多,如果你的資料表有 100 萬筆資料時,那才真的會「很有感覺」,所以在撰寫程式的時候必須特別小心這個地方。

除此之外,就算你用的是 Entity Framework 也是會有一樣的狀況,都需要先將 IEnumerable<T> 轉型成 IQueryable<T> 才能提高執行效率!(需透過 SQL Server Profiler 分析實際執行的 T-SQL 語法)

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication3
{
    class Program
    {
        private static AdventureWorksLTEntities db 
            = new AdventureWorksLTEntities();

        static void Main(string[] args)
        {
            var all = GetCustomers().AsQueryable();

            Console.WriteLine("Total Rows: " + all.Count());
            Console.WriteLine();

            var page1 = all.Where(p => p.LastName == "Liu");
            Console.WriteLine("Total Rows: " + page1.Count());
        }

        private static IEnumerable<Customer> GetCustomers()
        {
            return db.Customers;
        }
    }
}

相關連結