前言

在工作当中呢,没怎么用到过EF,所以为了遗忘这一部分知识,偶尔会去写写小的demo,偶然机会在EF循环迭代中发现居然影响性能这么严重,当我们在用时或许大概也许可能都曾这样写过,但是你注意到了吗,你怀疑过吗?这就是本节所要讨论的话题。若有错误,请批评指出。

话题

关于基础知识我们就不废话了哈,我们假设这样一个场景(不一定严谨,只是为了引出话题):当在下单中,如果有多个人下单,此时我们需要通过订单Id去得到客户Id。在这一场景中我们给出一个订单类以及订单处理类。如下:

     //订单类
public class Order
{
public int Id { get; set; }
public int OrderId { get; set; }
public int CustomerId { get; set; }
public string Filed1 { get; set; }
public string Filed2 { get; set; }
public string Filed3 { get; set; }
public string Filed4 { get; set; }
public string Filed5 { get; set; }
}
    //订单处理类
public class OrderProcess
{
public int OrderId { get; set; }
public int CustomerId { get; set; }
}

订单类是poco类存于数据库中,而订单处理类为将订单类进行DTO的类,我们将订单Id传到订单处理类中,通过订单类来获取到客户Id并赋给订单处理类中的客户Id。

为了大家可能方便测试,将其余说了很多次的映射类以及上下文都给出来。

    //订单映射类
public class OrderMap : EntityTypeConfiguration<Order>
{
public OrderMap()
{
HasKey(k => k.Id);
Property(p => p.OrderId);
Property(p => p.CustomerId);
Property(p => p.Filed1);
Property(p => p.Filed2);
Property(p => p.Filed3);
Property(p => p.Filed4);
Property(p => p.Filed5);
}
}
    //EF上下文以及预热
public class EFDbContext : DbContext
{
public EFDbContext()
: base("name=EFIteration")
{
var objectContext = ((IObjectContextAdapter)this).ObjectContext;
var mappingCollection = (StorageMappingItemCollection)objectContext.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace);
mappingCollection.GenerateViews(new List<EdmSchemaError>());
} /// <summary>
/// 通过反射一次性将表进行映射
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{ var typesRegister = Assembly.GetExecutingAssembly().GetTypes()
.Where(type => !(string.IsNullOrEmpty(type.Namespace))).Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>));
foreach (var type in typesRegister)
{
dynamic configurationInstance = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
} }
}

在数据库中对订单表生成了20万条数据。我们在订单处理类中随机插入100条订单Id,如下:

            var orderProcessList = new List<OrderProcess>();
for (int i = ; i < ; i++)
{
var random = new Random();
var number = random.Next(, );
var orderProcess = new OrderProcess() { OrderId = number };
orderProcessList.Add(orderProcess);
}

为了将订单类中客户Id赋给订单处理类中客户Id,你是不是会像如下这样操作呢?

            var context = new EFDbContext();
foreach (var op in orderProcessList)
{
var order = context.Set<Order>().FirstOrDefault(o => o.OrderId == op.OrderId);
op.CustomerId = order.CustomerId;
}

此时我们来测试下耗费的时间。

结果是3.2秒,看到这里有人就说了,而且大家都看到过都知道,在查询时为了性能我们可以关闭EF的变更追踪,于是乎就有了下面的代码:

改良一

 var order = context.Set<Order>().FirstOrDefault(o => o.OrderId == op.OrderId);

//替换成

 var order = context.Set<Order>().AsNoTracking().FirstOrDefault(o => o.OrderId == op.OrderId);

此时看看演示结果:

此时耗费的时间为1.7秒,当然有时会更短,反正比上述加了变更追踪情况的耗费时间要少。

到了这里你是不是就觉得已经很满足了呢?是不是觉得已经得到了很大的改善了呢?要是这样关闭变更追踪就可以解决了问题,那我这篇文章你说还有什么意义呢?好了废话不多说。本文讨论的话题就在于EF循环迭代问题,难道仅仅只有上述一种方式能达到我们的需求吗,上述我们将EF迭代放在了遍历订单处理类中,我们难道就不能事先得到符合条件的订单Id,然后直接取到对应的客户Id呢?思路来了,说完就开始干。

改良二

(1)筛选出符合订单处理类中的订单。

            var orderToProcessIds = orderProcessList.Select(o => o.OrderId).ToList();
var allOrders = context.Set<Order>().AsNoTracking().Where(o => orderToProcessIds.Contains(o.OrderId));

(2)将符合条件的订单转换成以订单Id为键的字典。

            var allOrdersDictionary = allOrders.ToDictionary(o => o.OrderId);
foreach (var op in orderProcessList)
{
var order = allOrdersDictionary[op.OrderId];
op.CustomerId = order.CustomerId;
}

这样不就解决了每次都要去迭代订单吗。接下来我们来测试比较改良一和改良二中耗费的时间。

 改良一中的耗时为3.4秒,改良二中耗时为0.3秒,通过小小的改善是不是又将性能提升了10倍呢!不必多说,你懂的!

总结

本节我们验证了在EF循环迭代中会导致性能的丢失,我们不经意间的操作就导致性能的丢失,有时候转换思维很重要,不仅仅只局限于固定思维,从上面可以看出:能够避免的应该尽量避免在遍历数据时去进行EF的循环迭代。好了,本来打算早点睡觉的,偶然发现这样也会导致性能的丢失,于是马不停蹄导致本文的产生。

【Advertisement】:最近找工作中,工作地点:深圳,希望大家可以推荐推荐!

最新文章

  1. 【翻译】首个基于NHibernate的应用程序
  2. 修改MS SQL忽略大小写 分类: SQL Server 数据库 2015-06-19 09:18 34人阅读 评论(0) 收藏
  3. java 集合list遍历时删除元素
  4. Ubuntu中如何打开终端terminal
  5. C#面向对象的三大特性
  6. HDU 5682/BestCoder Round #83 1003 zxa and leaf 二分+树
  7. lua注释
  8. 服务器的SVN项目版本较低,check out 下来后报错
  9. 全世界只有我们Erlang程序员是正确的
  10. 什么是HTTP Keep-Alive呢?
  11. 搭建Go开发及调试环境(LiteIDE + GoClipse)
  12. Spring+SpringMVC+MyBatis+easyUI整合优化篇(一)System.out.print与Log
  13. linux 用户空间与内核空间——高端内存详解
  14. 循环输出ViewBag集合
  15. 数据挖掘(二)——Knn算法的java实现
  16. 文件如何转换成pdf或html格式
  17. 在TensorFlow中基于lstm构建分词系统笔记
  18. sql server 备份恢复效率
  19. c++ stl常用
  20. chrome的uget扩展程序红色 Unable to connect with uget-integrator问题

热门文章

  1. jquery easyui 动态绑定数据列
  2. 李洪强iOS经典面试题155 - const,static,extern详解(面试必备)
  3. Angularjs ng-if和ng-show的区别
  4. Markdown Blog Testing
  5. sublime下安装ctags
  6. 有关DTCoreText无法加载网络图片及应用问题
  7. 五、jquery使用工具函数
  8. SQL Server数据库镜像的页面自动修复原理
  9. 谷歌发布的首款基于HTTP/2和protobuf的RPC框架:GRPC
  10. C#实现DNS解析服务和智能DNS服务