园友莱布尼茨写了一篇《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