程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 使用LINQ to SQL更新數據庫(下):性能測試

使用LINQ to SQL更新數據庫(下):性能測試

編輯:關於.NET

在上一篇隨筆中,我們列舉了使用LINQ to SQL對數據庫進行更新的5中方案。本文將對這幾種方案進 行測試和對比,力求找出一個最佳實踐。

准備工作

我們的測試還是基於Products表。為了使測試更符合實際,我們將與之關聯的Categories、Suplliers 和Order_Details表都添加進來。首先創建一個IProductRepository接口,定義插入、查找、更新操作:

public interface IProductRepository
{
   void InsertProduct(Product product);

   Product GetProduct(int id);

   void UpdateProduct(Product product);
}

然後創建一個AbstractProductRepository抽象類,實現IProductRepository接口。由於插入操作都是 一樣的,我們在抽象基類中提供了默認實現。同時還提供可選的查詢操作,因為除了“禁用查詢跟蹤”方 案外,其余的查詢操作也是一樣的。

public abstract class AbstractProductRepository : IProductRepository
{
   public void InsertProduct(Product product)
   {
     NorthwindDataContext db = new NorthwindDataContext();
     db.Products.InsertOnSubmit(product);
     db.SubmitChanges();
   }

   public virtual Product GetProduct(int id)
   {
     NorthwindDataContext db = new NorthwindDataContext();
     return db.Products.SingleOrDefault(p => p.ProductID == id);
   }

   public abstract void UpdateProduct(Product product);
}

接下來創建各個方案的實現類(具體的代碼請參考文章最後的下載鏈接):

方案一 重新賦值:CopyPropertiesProductRepository

方案二 禁用對象跟蹤:EnableObjectTrackingProductRepository

方案三 移除關聯:DetachAssociationProductRepository

方案四 使用委托:UsingDelegateProductRepository

開始測試

計時器采用CodeTimer,由於在下開發用的機器是XP(汗一個),所以使用的是修改版的CodeTimer。

我們對每個方案分別執行一次插入、查找和更新操作,代碼如下(方案四的代碼略有不同):

static void Execute(IProductRepository pr)
{
   var p1 = new Product
   {
     CategoryID = 1,
     ProductName = "Before changing",
     SupplierID = 1,
     UnitPrice = (decimal)2.0,
     UnitsInStock = 100,
     Discontinued = true,
     ReorderLevel = 10 
   };
   pr.InsertProduct(p1);
   var p2 = pr.GetProduct(p1.ProductID);
   p2.CategoryID = 2;
   p2.ProductName = "Arfer changing";
   p2.SupplierID = 2;
   p2.UnitPrice = (decimal)3;
   p2.UnitsInStock = 200;
   p2.Discontinued = false;
   p2.ReorderLevel = 20;
   pr.UpdateProduct(p2);
}

然後分別計時:

static void Main(string[] args)
{
   CodeTimer.Time("Copy Properties", 100, () => Execute(new  CopyPropertiesProductRepository()));

   CodeTimer.Time("Enable Object Tracking", 100, () => Execute(new  EnableObjectTrackingProductRepository()));

   CodeTimer.Time("Detach Associations", 100, () => Execute(new  DetachAssociationProductRepository()));

   CodeTimer.Time("Using Delegate", 100, () => ExecuteDelegate(new  UsingDelegateProductRepository()));

   Console.ReadLine();
}

執行100次的結果如下圖所示:

執行1000次的結果如下圖所示:

可見,使用反射復制屬性的方法時最不可取的。實際上,即使不使用反射而直接復制屬性,其性能都 是最差的。下圖是使用直接復制屬性時的數據:

直觀上來看,禁用對象跟蹤方法似乎性能最好,委托次之。但這種差距我認為是可以接受的(1000次 操作的執行時間之差在1秒之內,使用的對象數量也相差無幾)。那麼剩下的比較就在代碼的簡潔性和API 的易用性等方面了。

禁用對象跟蹤方法的代碼略多,因為為了能夠訪問與查詢對象關聯的其他對象,必須使用 DataLoadOptions類來進行加載。

public override Product GetProduct(int id)
{
   NorthwindDataContext db = new NorthwindDataContext();
   db.ObjectTrackingEnabled = false;
   DataLoadOptions loads = new DataLoadOptions();
   loads.LoadWith<Product>(p => p.Order_Details);
   loads.LoadWith<Product>(p => p.Category);
   loads.LoadWith<Product>(p => p.Supplier);
   return db.Products.SingleOrDefault(p => p.ProductID == id);
}

而方案四的API略顯復雜,畢竟不是所有業務層的程序員都能對表達式樹和委托運用自如。另一方面, 這種接口的約束也過於寬泛,不太好控制。因此可以將方法簽名改成如下的形式:

public void UpdateProduct(int id, Action<Product> action)

這樣一來性能超越了方案二,執行1000次的截圖如下:

方案四的優勢似乎已經很明顯了(當然單從執行時間上來說,方案二與其1秒以內的差距同樣是可以忽 略的),更少的代碼,更快的速度。

然而讓人遺憾的是,這仍然是一個避開Attach方法的方案。此外,由於必須將所有屬性的賦值放置在 一個委托中,也喪失了一定的靈活性。比如在實際的項目中,我們常常會希望獲取到Product的實體後, 針對每個屬性做一些操作,在方法的不同位置對不同屬性的值進行修改,然後再統一調用Update 方法進 行更新。這時方案四就顯得很別扭了。

因此我更傾向於提供多個UpdateProduct方法的重載版本,在不同的場景下使用不同的重載。您的意見 呢?

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved