Fork me on GitHub

数据访问组件&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 标签页选择直接下载最新的源码压缩包

 

posted @ 2013-06-07 23:25  郭明锋  阅读(7174)  评论(59编辑  收藏  举报