數據訪問組件&ORM測試框架(1)——EF方案實現


〇、目錄

一、前言

二、設計思路

三、核心代碼分析

四、如何使用(以EntityFramework為例) 

  (一) 在項目中建立專屬文件夾

  (二) 在文件夾內實現數據操作的基礎准備

  (三) 繼承TesterBase實現自己的Tester類

  (四) 添加測試調用入口

  (五) 運行測試

五、寫在后面

六、源碼下載

一、前言

  首先,鑒於本文所展現的ORM耗時測試已成為了博友的吐嘈點,我想我有必要聲明一點:我發布這個測試框架,相當於一個活動,目的是收集各種數據訪問解決方案的實現示例,並在性能,易用性,代碼量上做一個綜合的對比,讓大家更好的了解各個解決方案的優缺點,選擇的時候更明確。時間的對比只是其中一個方面,並不是對比的全部。

  最近又掀起了數據訪問組件&ORM的性能比拼了,但基本都是各說各的好,沒有一個統一的標准與平台來進行對比。

  郭某不才,花了點時間寫了個數據訪問性能測試框架,主要是想各個ORM在一個統一的環境下完成相同的事,來進行一下公平的對比,看看哪家的性能更好、更易用、完成相同的業務代碼量更少。

  特別說明,本框架測試環境如下:VS2010+SP1或以上,SQL2005或以上,.NET 4.0,能運行以上環境的操作系統

  本框架的代碼已發布到 codeplex 上,同學們可以通過VS自帶的團隊項目管理功能(TFS)或SVN 隨時獲取最新代碼。

    項目地址:https://datatester.codeplex.com

    TFS獲取地址:https://tfs.codeplex.com:443/tfs/TFS17,(似乎只有團隊成員可以用TFS方式)需要登錄,並在項目的 SOURCE CODE 標簽的 Connect 連接處獲取VS端的登錄用戶名,密碼為自己的賬號密碼。

    SVN獲取地址:https://datatester.svn.codeplex.com/svn

二、設計思路

  這個測試框架主要是設計一個通用的測試基類,把要做的事規定好,並把實現細節開放出來供給各家ORM自己去實現,為公平起見,規定如下:

  1. 所有數據訪問操作都從一個數據庫連接字符串開始。
  2. 要實現的業務最終結果必須相同,比如有些同學給出的ado.net與ORM的對比測試,ORM查詢出來的是一個實體,而ado.net去只是執行一條相應的sql語句,顯然是不公平的,ado.net也必須構造出一個實體,才算把業務實現了。
  3. 統一的調用方案,所有的測試方法的調用入口相同。

  根據這個設計思想,設計了單個實體與多個實體的添加,查詢,修改,刪除操作,為了體現各個ORM的易用性,還添加了一個比較復雜的查詢操作。以上這些操作都是在基類中定義了對應的 protected abstract 的方法,需要在具體的實現類中進行實現。並且唯一的前提條件只有基類中的的一個只讀的數據庫連接字符串ConnectionString,如果該連接串不滿足要求,也可以在實現類中進行重寫。 

  本次測試使用到的數據庫結構如下(偷下懶,直接上EF4.0的反向數據庫功能生成的 edmx 圖上)

  由圖可以看出,實體關系如下:

  1. 分類:一個產品分類可以對應多個產品
  2. 客戶:一個客戶可以對應多個訂單
  3. 產品:一個產品必定對應一個分類,且可以對應多個訂單明細
  4. 訂單:一個訂單必定對應一個客戶,且可以對應多個訂單明細
  5. 訂單明細:一個訂單明細必定對應一個產品與一個訂單

  在本框架中,規定的要實現的業務如下:

  • 單個操作
  1. 添加一個客戶信息(客戶名稱做有唯一處理)
  2. 根據客戶名稱獲取步驟1中添加的客戶信息,並返回
  3. 將步驟2中獲取的客戶信息的 PostalCode 與 Tel 信息更新為指定字符串
  4. 刪除這個客戶信息
  • 批量操作
  1. 批量添加指定數量的客戶信息(客戶名稱做有唯一處理)
  2. 查詢出剛添加的指定數量的客戶信息,並返回客戶信息的集合
  3. 將步驟2獲取的客戶信息的 Address 信息更新為 Address + CustomerName
  4. 刪除批量操作所添加的客戶信息
  • 復雜查詢

  復雜查詢將查詢已完成的訂單信息的集合,並以如下視圖模型作為結果裝載

  視圖模型定義如下:

 1 namespace DataTestFramework.ViewModels
 2 {
 3     /// <summary>
 4     /// 視圖模型——訂單信息
 5     /// </summary>
 6     public class OrderView
 7     {
 8         public int OrderId { get; set; }
 9 
10         public DateTime OrderDate { get; set; }
11 
12         public decimal SumMoney { get; set; }
13 
14         public bool Finished { get; set; }
15 
16         /// <summary>
17         /// 訂單關聯的客戶名稱
18         /// </summary>
19         public string CustomerName { get; set; }
20 
21         /// <summary>
22         /// 當前訂單的所有訂單明細所關聯的產品名稱,多個以逗號分隔
23         /// </summary>
24         public string ProductNames { get; set; }
25     }
26 }

 

三、核心代碼分析

  借助VS2012更新了Update1后的新功能 Code Map,測試基類的結構展現如下:

  上圖展示了測試基類TesterBase的調用結構:

  1. Work方法是公共方法,測試的運行由這個方法開始。
  2. SingleCrudTest、MultipleCrudTest、RetrieveComplex三個方法是私有方法,分別負責單個實體,多個實體,復雜查詢的功能調用、計時與輸出功能。
  3. 再下一級的方法是受保護的抽象方法,負責各個功能的業務實現,需要具體的實現類中進行實現。

  測試基類 TesterBase 的具體代碼如下:

 
        
 
        
 
        
  1 namespace DataTestFramework.Infrastructure
  2 {
  3     /// <summary>
  4     ///     測試類基類
  5     /// </summary>
  6     public abstract class TesterBase
  7     {
  8         private const string _connectionString = "Data Source=.; Integrated Security=True;" +
  9             " Initial Catalog=DataTestFramework; Pooling=True; MultipleActiveResultSets=True;";
 10 
 11         /// <summary>
 12         ///     獲取 數據庫連接字符串
 13         /// </summary>
 14         protected virtual string ConnectionString
 15         {
 16             get { return _connectionString; }
 17         }
 18 
 19         #region 受保護方法
 20 
 21         #region 單個操作
 22 
 23         /// <summary>
 24         ///     添加單個客戶信息
 25         /// </summary>
 26         /// <param name="customer">待添加的客戶信息</param>
 27         protected abstract void CreateCustomer(Customer customer);
 28 
 29         /// <summary>
 30         ///     獲取指定名稱的客戶信息
 31         /// </summary>
 32         /// <param name="customerName">帶Token的客戶名稱</param>
 33         /// <returns>指定名稱的客戶信息,不存在時返回null</returns>
 34         protected abstract Customer RetrieveCustomer(string customerName);
 35 
 36         /// <summary>
 37         ///     更新指定客戶信息的 PostalCode="100000",Tel="13800138000"
 38         /// </summary>
 39         /// <param name="customer">待更新的客戶信息</param>
 40         protected abstract void UpdateCustomer(Customer customer);
 41 
 42         /// <summary>
 43         ///     刪除指定客戶信息
 44         /// </summary>
 45         /// <param name="customerId">客戶編號</param>
 46         protected abstract void DeleteCustomer(int customerId);
 47 
 48         #endregion
 49 
 50         #region 批量操作
 51 
 52         /// <summary>
 53         ///     批量添加客戶信息
 54         /// </summary>
 55         /// <param name="customers">待添加的客戶信息集合</param>
 56         protected abstract void CreateCustomers(IEnumerable<Customer> customers);
 57 
 58         /// <summary>
 59         ///     反向(先倒序排序)獲取指定數量的客戶信息
 60         /// </summary>
 61         /// <param name="count">要獲取的數量</param>
 62         /// <returns>獲取的客戶信息集合</returns>
 63         protected abstract IEnumerable<Customer> RetrieveCustomers(int count);
 64 
 65         /// <summary>
 66         ///     更新指定的多個客戶信息,在Address后面加上當前客戶的CustomerName信息
 67         /// </summary>
 68         /// <param name="customers">待更新的客戶信息</param>
 69         protected abstract void UpdateCustomers(IEnumerable<Customer> customers);
 70 
 71         /// <summary>
 72         ///     批量刪除指定編號的客戶信息
 73         /// </summary>
 74         /// <param name="customerIds">待刪除的客戶編號集合</param>
 75         protected abstract void DeleteCustomers(IEnumerable<int> customerIds);
 76 
 77         #endregion
 78 
 79         #region 復雜查詢
 80 
 81         /// <summary>
 82         ///     查詢所有已完成(Finished == true)的訂單,構建訂單視圖模型
 83         /// </summary>
 84         /// <returns>滿足條件的訂單視圖模型集合</returns>
 85         protected abstract IEnumerable<OrderView> RetrieveOrderViews();
 86 
 87         #endregion
 88 
 89         #endregion
 90 
 91         #region 私有方法
 92 
 93         /// <summary>
 94         ///     單個實體操作測試
 95         /// </summary>
 96         /// <returns>是否繼續</returns>
 97         private bool SingleCrudTest()
 98         {
 99             string token = DateTime.Now.ToString("hhmmssfff");
100             Stopwatch watch = new Stopwatch();
101             Customer customer = new Customer
102             {
103                 CustomerName = "郭明鋒@中國" + token,
104                 ContactName = "郭明鋒",
105                 Address = "北京,北京",
106                 PostalCode = "100001",
107                 Tel = "13800138001"
108             };
109 
110             //單個客戶添加
111             watch.Restart();
112             CreateCustomer(customer);
113             watch.Stop();
114             Console.WriteLine("單個實體添加成功,耗時:{0}", watch.Elapsed);
115 
116             //查詢最后添加的客戶信息
117             watch.Restart();
118             Customer lastCustomer = RetrieveCustomer(customer.CustomerName);
119             watch.Stop();
120             if (lastCustomer != null && lastCustomer.CustomerName == customer.CustomerName)
121             {
122                 Console.WriteLine("單個實體查詢成功,耗時:{0}", watch.Elapsed);
123             }
124             else
125             {
126                 Console.WriteLine("單個實體查詢失敗,耗時:{0},測試終止。", watch.Elapsed);
127                 return false;
128             }
129 
130             //更新上一步查詢出來的客戶信息
131             watch.Restart();
132             UpdateCustomer(lastCustomer);
133             watch.Stop();
134             lastCustomer = RetrieveCustomer(customer.CustomerName);
135             if (lastCustomer != null && lastCustomer.PostalCode == "100000" && lastCustomer.Tel == "13800138000")
136             {
137                 Console.WriteLine("單個實體更新成功,耗時:{0}", watch.Elapsed);
138             }
139             else
140             {
141                 Console.WriteLine("單個實體更新失敗,耗時:{0},測試終止。", watch.Elapsed);
142                 return false;
143             }
144 
145             //刪除本次添加的單個客戶信息
146             watch.Restart();
147             DeleteCustomer(lastCustomer.CustomerId);
148             watch.Stop();
149             lastCustomer = RetrieveCustomer(customer.CustomerName);
150             if (lastCustomer == null)
151             {
152                 Console.WriteLine("單個實體刪除成功,耗時:{0}", watch.Elapsed);
153             }
154             else
155             {
156                 Console.WriteLine("單個實體刪除成功,耗時:{0},測試終止", watch.Elapsed);
157                 return false;
158             }
159             return true;
160         }
161 
162         /// <summary>
163         ///     批量實體操作測試
164         /// </summary>
165         /// <returns>是否繼續</returns>
166         private bool MultipleCrudTest()
167         {
168             string token = DateTime.Now.ToString("hhmmssfff");
169             Stopwatch watch = new Stopwatch();
170             Console.WriteLine("要開始批量測試,請輸入批量大小:");
171             int count;
172             bool flag = int.TryParse(Console.ReadLine(), out count);
173             while (!flag)
174             {
175                 Console.WriteLine("要開始批量測試,請輸入批量大小:");
176                 flag = int.TryParse(Console.ReadLine(), out count);
177             }
178             List<Customer> customers = new List<Customer>();
179             for (int index = 0; index < count; index++)
180             {
181                 customers.Add(new Customer
182                 {
183                     CustomerName = string.Format("郭明鋒@中國{0}{1}", token, index),
184                     ContactName = "郭明鋒",
185                     Address = "北京,北京",
186                     PostalCode = "100001",
187                     Tel = "13800138001"
188                 });
189             }
190             Console.WriteLine("開始進行批量測試,測試數量為:{0}", count);
191 
192             //批量客戶添加
193             watch.Restart();
194             CreateCustomers(customers);
195             watch.Stop();
196             Console.WriteLine("批量實體添加成功,耗時:{0}", watch.Elapsed);
197 
198             //查詢最后添加的客戶信息
199             watch.Restart();
200             List<Customer> lastCustomers = RetrieveCustomers(count).ToList();
201             watch.Stop();
202             //以前后客戶名稱集合取差集來判斷是否一致
203             if (!lastCustomers.Select(m => m.CustomerName).Except(customers.Select(m => m.CustomerName)).Any())
204             {
205                 Console.WriteLine("批量實體查詢成功,耗時:{0}", watch.Elapsed);
206             }
207             else
208             {
209                 Console.WriteLine("批量實體查詢失敗,耗時:{0},測試終止。", watch.Elapsed);
210                 return false;
211             }
212 
213             //批量更新客戶信息
214             watch.Restart();
215             UpdateCustomers(lastCustomers);
216             lastCustomers = RetrieveCustomers(count).ToList();
217             if (!lastCustomers.Select(m => m.Address).Intersect(customers.Select(m => m.Address)).Any())
218             {
219                 Console.WriteLine("批量實體更新成功,耗時:{0}", watch.Elapsed);
220             }
221             else
222             {
223                 Console.WriteLine("批量實體更新失敗,耗時:{0},測試終止。", watch.Elapsed);
224                 return false;
225             }
226 
227             //批量刪除本次添加的客戶信息
228             List<int> customerIds = lastCustomers.Select(m => m.CustomerId).ToList();
229             watch.Restart();
230             DeleteCustomers(customerIds);
231             watch.Stop();
232             lastCustomers = RetrieveCustomers(count).ToList();
233             if (!lastCustomers.Select(m => m.CustomerName).Intersect(customers.Select(m => m.CustomerName)).Any())
234             {
235                 Console.WriteLine("批量實體刪除成功,耗時:{0}", watch.Elapsed);
236             }
237             else
238             {
239                 Console.WriteLine("批量實體刪除失敗,耗時:{0},測試終止。", watch.Elapsed);
240                 return false;
241             }
242             return true;
243         }
244 
245         private void RetrieveComplex()
246         {
247             Stopwatch watch = new Stopwatch();
248             watch.Restart();
249             List<OrderView> orderViews = RetrieveOrderViews().ToList();
250             watch.Stop();
251             Console.WriteLine("復雜查詢執行成功,耗時{1},共獲取{0}個訂單視圖信息。", watch.Elapsed, orderViews.Count);
252         }
253 
254         #endregion
255 
256         #region 公共方法
257 
258         /// <summary>
259         ///     開始測試工作,主要對 Customer 表進行操作,工作順序為增、查、改、刪,進行單個操作,批量操作與復雜查詢
260         /// </summary>
261         public void Work()
262         {
263             bool isContinue = SingleCrudTest();
264             if (!isContinue)
265             {
266                 return;
267             }
268             isContinue = MultipleCrudTest();
269             if (!isContinue)
270             {
271                 return;
272             }
273             RetrieveComplex();
274         }
275 
276         #endregion
277     }
278 }

 

四、如何使用(以EntityFramework為例) 

  下面,我就以EntityFramework 4.4 來演示一下怎樣使用這個測試框架。可能有同學會說,EF6都出來了,為什么還使用4.4版本?原因一、.net4.0只支持到4.4版本,原因二、windows 2003 只支持到 .net 4.0,原因三、我正在使用的是4.4版本。如果你覺得4.4版本 out 了,可以自己去實現一個 EF6的測試示例,呵呵。廢話不多說,下面我們來實現 EntityFramework 4.4 的測試示例。

  (一) 在項目中建立專屬文件夾

  為了更好的管理各個數據訪問方案的實現代碼,也為防止項目結構混亂,各個方案的代碼應在自己的文件夾內實現。在這里,創建一個名為EntityFramework的文件夾

  (二) 在文件夾內實現數據操作的基礎准備

  首先要進行相應數據訪問方案的基礎准備,比如ado.net方案,可能需要一個SqlHelper的輔助操作類,又或者其他ORM,需要進行數據映射,生成數據實體等等。對於EntityFramework,只需要實現一個數據上下文類即可:

 1 namespace DataTestFramework.EntityFramework
 2 {
 3     public class EFDbContext : DbContext
 4     {
 5         public EFDbContext(string connectionStringOrName)
 6             : base(connectionStringOrName) { }
 7 
 8         public DbSet<Customer> Customers { get; set; }
 9 
10         public DbSet<Category> Categories { get; set; }
11 
12         public DbSet<Product> Products { get; set; }
13 
14         public DbSet<Order> Orders { get; set; }
15 
16         public DbSet<OrderDetail> OrderDetails { get; set; }
17 
18         protected override void OnModelCreating(DbModelBuilder modelBuilder)
19         {
20             modelBuilder.Entity<Product>().HasRequired(m => m.Category).WithMany(n => n.Products).HasForeignKey(m => m.CategoryId);
21             modelBuilder.Entity<Order>().HasRequired(m => m.Customer).WithMany(n => n.Orders).HasForeignKey(m => m.CustomerId);
22             modelBuilder.Entity<OrderDetail>().HasRequired(m => m.Product).WithMany(n => n.OrderDetails).HasForeignKey(m => m.ProductId);
23             modelBuilder.Entity<OrderDetail>().HasRequired(m => m.Order).WithMany(n => n.OrderDetails).HasForeignKey(m => m.OrderId);
24         }
25     }
26 }

 

  特別說明:項目中已經添加了測試所需的POCO實體類,如果這些類不符合具體的數據訪問方案的要求(比如實體類是代碼生成器來生成的),可以自行在自己的文件夾中定義需要的實體類,在數據訪問實現中再把操作結果轉換為系統定義的POCO實體類,以進行數據操作結果的驗證。

  (三) 繼承TesterBase實現自己的Tester類

  測試基類 TesterBase 中已定義了需要實現的測試用例,需要繼承這個基類,實現相應數據訪問方案的具體操作實現。對於 EntityFramework,實現如下:

  1 namespace DataTestFramework.EntityFramework
  2 {
  3     /// <summary>
  4     ///     EntityFramework測試類
  5     /// </summary>
  6     public class EntityFrameworkTester : TesterBase
  7     {
  8         private const int _pageSize = 300;
  9 
 10         #region 單個操作
 11 
 12         /// <summary>
 13         ///     添加單個客戶信息
 14         /// </summary>
 15         /// <param name="customer">待添加的客戶信息</param>
 16         protected override void CreateCustomer(Customer customer)
 17         {
 18             using (EFDbContext db = new EFDbContext(ConnectionString))
 19             {
 20                 db.Customers.Add(customer);
 21                 db.SaveChanges();
 22             }
 23         }
 24 
 25         /// <summary>
 26         ///     獲取指定名稱的客戶信息
 27         /// </summary>
 28         /// <param name="customerName">帶Token的客戶名稱</param>
 29         /// <returns>指定名稱的客戶信息,不存在時返回null</returns>
 30         protected override Customer RetrieveCustomer(string customerName)
 31         {
 32             using (EFDbContext db = new EFDbContext(ConnectionString))
 33             {
 34                 return db.Customers.SingleOrDefault(m => m.CustomerName == customerName);
 35             }
 36         }
 37 
 38         /// <summary>
 39         ///     更新指定客戶信息的 PostalCode="100000",Tel="13800138000"
 40         /// </summary>
 41         /// <param name="customer">待更新的客戶信息</param>
 42         protected override void UpdateCustomer(Customer customer)
 43         {
 44             customer.PostalCode = "100000";
 45             customer.Tel = "13800138000";
 46             using (EFDbContext db = new EFDbContext(ConnectionString))
 47             {
 48                 if (db.Entry(customer).State == EntityState.Detached)
 49                 {
 50                     db.Customers.Attach(customer);
 51                     db.Entry(customer).State = EntityState.Modified;
 52                 }
 53                 db.SaveChanges();
 54             }
 55         }
 56 
 57         /// <summary>
 58         ///     刪除指定客戶信息
 59         /// </summary>
 60         /// <param name="customerId">客戶編號</param>
 61         protected override void DeleteCustomer(int customerId)
 62         {
 63             using (EFDbContext db = new EFDbContext(ConnectionString))
 64             {
 65                 Customer customer = db.Customers.SingleOrDefault(m => m.CustomerId == customerId);
 66                 if (customer == null)
 67                 {
 68                     return;
 69                 }
 70                 db.Customers.Remove(customer);
 71                 db.SaveChanges();
 72             }
 73         }
 74 
 75         #endregion
 76 
 77         #region 批量操作
 78 
 79         /// <summary>
 80         ///     批量添加客戶信息
 81         /// </summary>
 82         /// <param name="customers">待添加的客戶信息集合</param>
 83         protected override void CreateCustomers(IEnumerable<Customer> customers)
 84         {
 85             using (EFDbContext db = new EFDbContext(ConnectionString))
 86             {
 87                 try
 88                 {
 89                     db.Configuration.AutoDetectChangesEnabled = false;
 90                     List<Customer> customerList = customers as List<Customer> ?? customers.ToList();
 91                     int pageCount = customerList.Count / _pageSize;
 92                     pageCount = customerList.Count % _pageSize > 0 ? pageCount + 1 : pageCount;
 93                     for (int index = 0; index < pageCount; index++)
 94                     {
 95                         List<Customer> pageData = customerList.Skip(index * _pageSize).Take(_pageSize).ToList();
 96                         foreach (Customer customer in pageData)
 97                         {
 98                             db.Customers.Add(customer);
 99                         }
100                         db.SaveChanges();
101                     }
102                 }
103                 finally
104                 {
105                     db.Configuration.AutoDetectChangesEnabled = true;
106                 }
107             }
108         }
109 
110         /// <summary>
111         ///     反向(先倒序排序)獲取指定數量的客戶信息
112         /// </summary>
113         /// <param name="count">要獲取的數量</param>
114         /// <returns>獲取的客戶信息集合</returns>
115         protected override IEnumerable<Customer> RetrieveCustomers(int count)
116         {
117             using (EFDbContext db = new EFDbContext(ConnectionString))
118             {
119                 return db.Customers.OrderByDescending(m => m.CustomerId).Take(count).ToList();
120             }
121         }
122 
123         /// <summary>
124         ///     更新指定的多個客戶信息,在Address后面加上當前客戶的CustomerName信息
125         /// </summary>
126         /// <param name="customers">待更新的客戶信息</param>
127         protected override void UpdateCustomers(IEnumerable<Customer> customers)
128         {
129             using (EFDbContext db = new EFDbContext(ConnectionString))
130             {
131                 List<int> ids = customers.Select(m => m.CustomerId).ToList();
132                 int pageCount = ids.Count / _pageSize;
133                 pageCount = ids.Count % _pageSize > 0 ? pageCount + 1 : pageCount;
134                 for (int index = 0; index < pageCount; index++)
135                 {
136                     List<int> pageIds = ids.Skip(index * _pageSize).Take(_pageSize).ToList();
137                     db.Customers.Update(m => pageIds.Contains(m.CustomerId), n => new Customer
138                     {
139                         Address = n.Address + n.CustomerName
140                     });
141                 }
142             }
143         }
144 
145         /// <summary>
146         ///     批量刪除指定編號的客戶信息
147         /// </summary>
148         /// <param name="customerIds">待刪除的客戶編號集合</param>
149         protected override void DeleteCustomers(IEnumerable<int> customerIds)
150         {
151             List<int> ids = customerIds as List<int> ?? customerIds.ToList();
152             using (EFDbContext db = new EFDbContext(ConnectionString))
153             {
154                 int pageCount = ids.Count / _pageSize;
155                 pageCount = ids.Count % _pageSize > 0 ? pageCount + 1 : pageCount;
156                 for (int index = 0; index < pageCount; index++)
157                 {
158                     List<int> pageIds = ids.Skip(index * _pageSize).Take(_pageSize).ToList();
159                     db.Customers.Delete(m => pageIds.Contains(m.CustomerId));
160                 }
161             }
162         }
163 
164         #endregion
165 
166         #region 復雜查詢
167 
168         /// <summary>
169         ///     查詢所有已完成(Finished == true)的訂單,構建訂單視圖模型
170         /// </summary>
171         /// <returns>滿足條件的訂單視圖模型集合</returns>
172         protected override IEnumerable<OrderView> RetrieveOrderViews()
173         {
174             using (EFDbContext db = new EFDbContext(ConnectionString))
175             {
176                 var orders = db.Orders.Where(m => m.Finished).Select(m => new
177                 {
178                     m.OrderId,
179                     m.OrderDate,
180                     m.SumMoney,
181                     m.Finished,
182                     Customer = new { m.Customer.CustomerName },
183                     ProductNames = m.OrderDetails.Select(n => n.Product.ProductName)
184                 }).ToList();
185                 return orders.Select(order => new OrderView
186                 {
187                     OrderId = order.OrderId,
188                     OrderDate = order.OrderDate,
189                     SumMoney = order.SumMoney,
190                     Finished = order.Finished,
191                     CustomerName = order.Customer.CustomerName,
192                     ProductNames = order.ProductNames.ExpandAndToString(",")
193                 }).ToList();
194             }
195         }
196 
197         #endregion
198     }
199 }

 

  對於批量修改,刪除操作,我使用了 EntityFramework.Extended.dll 程序集(可以在Nuget中獲取)來進行實現,該程序集對於每次批量操作只生成一條sql語句,而不會像EntityFramework提供的原生方法那樣批量N條數據就要生成N條sql語句。並且使用了逐部分處理的方式,以免進行大量數據處理時一次提交太多的數據導致性能低下。

  復雜查詢的業務,對於EntityFramework來說,實現是相當簡單的,完全是要什么取什么,使用IQueryable<T>的擴展方法Select來按需獲取,然后用匿名類來裝載查詢結果,再根據這個查詢結果構造視圖模型的列表集合,具體實現如上面代碼中 166行-197行所示。

  添加EntityFrameworkTester類之后的 Code Map如下所示:

  (四) 添加測試調用入口

  在Program類中,定義了很多的Method方法供調用,請不要修改Program類的Main方法,只需要在 HelpInfo 方法中添加使用哪個序號的Method進行調用的信息,然后在相應序號的Method方法中,如這里EntityFramework使用 Method01方法來作為入口,則進行如下修改:

  1. 修改HelpInfo方法,添加第6行所示代碼,其中1表示Method01的序號,后面為測試名稱:
    1 private static void HelpInfo()
    2 {
    3     Console.WriteLine("=============幫-助-信-息============");
    4     Console.WriteLine("h.幫助信息");
    5     Console.WriteLine("0.退出程序");
    6     Console.WriteLine("1.性能測試——EF");
    7     Console.WriteLine("=============幫-助-信-息============");
    8 }
  2. 修改Method01方法,實例化Tester類,調用Work方法
    1 private static void Method01()
    2 {
    3     Console.WriteLine("=============EntityFramework測試開始============");
    4     EntityFrameworkTester tester = new EntityFrameworkTester();
    5     tester.Work();
    6     Console.WriteLine("=============EntityFramework測試結束============");
    7 }

  (五) 運行測試

  至此,EntityFramework使用此測試框架的代碼已添加完畢,運行測試,忽略第一次運行的結果,分別執行5000條與10000條的批量數據操作,結果如下:

 五、寫在后面

  現在代碼示例中只有本人添加的一個EntityFramework示例,其他的方案比如ado.net及其他ORM期待高手來添加,請大家下載並編寫自己的數據訪問方案的實現,可在評論處留下載鏈接,也可直接發給我,我整理之后將在本系列后續中詳解實現過程,並進行綜合的對比。

  另外,如果當前框架不能滿足部分數據訪問方案的要求導致無法添加測試的話,也希望能提出來,我再進行更新。

六、源碼下載

  本框架的代碼已發布到 codeplex 上,同學們可以通過VS自帶的團隊項目管理功能(TFS)或SVN 隨時獲取最新代碼。

  • 項目地址:https://datatester.codeplex.com
  • TFS獲取地址:https://tfs.codeplex.com:443/tfs/TFS17,(似乎只有團隊成員可以用TFS方式)需要登錄,並在項目的 SOURCE CODE 標簽的 Connect 連接處獲取VS端的登錄用戶名,密碼為自己的賬號密碼。
  • SVN獲取地址:https://datatester.svn.codeplex.com/svn
  • 以上方案都不可用的情況下,可以項目的 SOURCE CODE 標簽頁選擇直接下載最新的源碼壓縮包

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM