轉自:http://www.cnblogs.com/nianming/archive/2013/06/07/3123103.html#2699851
記錄下。
園友萊布尼茨寫了一篇《Entity Framework數據插入性能追蹤》的文章,我感覺不錯,至少他提出了問題,寫了出來,引起了大家的討論,這就是一個氛圍。讀完文章+評論,於是我自己也寫了個簡單的程序試了試。
先曬一下代碼:
兩個簡單的類:
1: /// <summary> 2: /// 消費者 3: /// </summary> 4: public class Consumer 5: { 6: public int CId { get; set; } 7: public string CName { get; set; } 8: public List<Order> Orders { get; set; } 9: } 10: 11: /// <summary> 12: /// 訂單 13: /// </summary> 14: public class Order 15: { 16: public int OrderNo { get; set; } 17: public DateTime OrderDate { get; set; } 18: public decimal TotalMoney { get; set; } 19: public int CId { get; set; } 20: 21: public Consumer Consumer { get; set; } 22: } 映射配置: 1: public class ConsumerConfiguration : EntityTypeConfiguration<Consumer> 2: { 3: public ConsumerConfiguration() 4: { 5: HasKey(t => t.CId).Property(t => t.CId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 6: Property(t => t.CName).IsRequired().HasMaxLength(50); 7: } 8: } 9: 10: public class OrderConfiguration : EntityTypeConfiguration<Order> 11: { 12: public OrderConfiguration() 13: { 14: HasKey(t => t.OrderNo); 15: HasRequired(t => t.Consumer).WithMany(t => t.Orders).HasForeignKey(t => t.CId); 16: } 17: }
Context: 1: public class TestContext : DbContext 2: { 3: public DbSet<Consumer> Consumers { get; set; } 4: public DbSet<Order> Orders { get; set; } 5: 6: protected override void OnModelCreating(DbModelBuilder modelBuilder) 7: { 8: modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); 9: 10: modelBuilder.Configurations.Add(new ConsumerConfiguration()); 11: modelBuilder.Configurations.Add(new OrderConfiguration()); 12: 13: base.OnModelCreating(modelBuilder); 14: } 15: } 測試代碼: 1: static void Main(string[] args) 2: { 3: using (var ctx = new TestContext()) 4: { 5: ctx.Consumers.Add(new Consumer() 6: { 7: CName = "張三" 8: }); 9: ctx.SaveChanges(); 10: 11: Stopwatch sw = new Stopwatch(); 12: sw.Start(); 13: Console.WriteLine("訂單開始:\n"); 14: 15: for (int outer = 0; outer < 20000; outer++) 16: { 17: ctx.Orders.Add(new Order() 18: { 19: OrderDate = DateTime.Now, 20: TotalMoney = 100, 21: CId = 1, 22: }); 23: } 24: ctx.SaveChanges(); 25: sw.Stop(); 26: Console.WriteLine(sw.Elapsed.Minutes + "分" + sw.Elapsed.Seconds + "秒" + sw.Elapsed.Milliseconds + "毫秒"); 27: } 28: }
上面的代碼是最平常的代碼了,沒有什么可解釋的,將內容放到重點上。
運行以上代碼的環境是VS2012+SQL SERVER 2008 R2,機器配置:4G,N年以前的CPU。
運行上面的代碼非常的慢,正如萊布尼茨說的,在數據Add到上下文這個階段比較耗時。出現這個問題的原因是:每次調用ctx.Orders.Add(order)之前,EF都會調用DetectChanges,在StackOverFlow上有解釋,地址是:http://stackoverflow.com/questions/9439430/improving-performance-of-initializing-dbset-in-seed,另外在Programming Entity Framework DbContext這本書的60也有DetectChange的介紹。
解決上面速度慢的問題的辦法就是設置
1: ctx.Configuration.AutoDetectChangesEnabled = false;
下面來看看禁用以后的執行速度:
另外一個解決辦法就是使用DbSet<T>.AddRange方法,這個方法是在6.0 beta1中加入的。
1: List<Order> orderList = new List<Order>(); 2: for (int outer = 0; outer < 20000; outer++) 3: { 4: orderList.Add(new Order() 5: { 6: OrderDate = DateTime.Now, 7: TotalMoney = 100, 8: CId = 1 9: }); 10: } 11: ctx.Orders.AddRange(orderList); 12: ctx.SaveChanges();
AddRange方法在System.Data.Entity 泛型DbSet類中,下圖是我通過Reflector截的圖
從上面兩幅圖中可以看到,Add和AddRange都是添加到_internalSet中,但是如果AutoDetectChangesEnabled設置為true的話,添加任何實體之前都會調用DetectChanges,注意看Remarks中的解釋。