園友萊布尼茨寫了一篇《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中的解釋。
測試源碼下載地址:http://www.ef-community.com/forum.php?mod=viewthread&tid=437&extra=page%3D1
夜已深!
天亮了,就要高考了,祝福所有的考生!