EF循环迭代导致如此严重的性能丢失,你知道?
前言
在工作当中呢,没怎么用到过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】:最近找工作中,工作地点:深圳,希望大家可以推荐推荐!
EF循环迭代导致如此严重的性能丢失,你知道?的更多相关文章
- c#循环迭代匿名类链表(可迭代的匿名类)
Main(){ //为什么?object是基类啊!! //报错.不能从List<anonymous>换成List<object>. //var q=(List<objec ...
- MSDN官方XmlSerializer类导致内存泄漏和性能低
MSDN官方XmlSerializer类使用说明链接: http://msdn.microsoft.com/zh-CN/library/system.xml.serialization.xmlseri ...
- Day 13 可迭代对象,迭代器对象,for循环迭代,生成器对象,枚举对象
一.迭代器概念:# 器:包含了多个值的容器# 迭代:循环反馈(一次从容器中取出一个值)# 迭代器:从装有多个值的容器中一次取出一个值给外界# ls = 'abcdef'ls = [1, 2, 3, 4 ...
- iOS - Block产生Memory Leaks循环引用导致的内存泄漏以及解决方案
在ARC(自动引用技术)前,Objective-c都是手动来分配释放 释放 计数内存,其过程非常复杂. ARC技术推出后,貌似世界和平了很多,但是其实ARC并不等同于Java或者C#中的垃圾回收,AR ...
- 关于多层for循环迭代的效率优化问题
关于多层for循环迭代的效率优化问题 今天笔试的时候遇到这么一道题目 说有上面这么循环嵌套 .问怎么优化 并说明原因. for(int i = 0 ; i < 1000 ;i++){ ...
- java用while循环设计轮询线程的性能问题
java用while循环设计轮询线程的性能问题 轮询线程在开发过程中的应用是比较广泛的,在这我模拟一个场景,有一个队列和轮询线程,主线程往队列中入队消息,轮询线程循环从队列中读取消息并打印消息内容.有 ...
- python基础知识梳理----3基本数据类型,int,bool,str ,for 循环,迭代
一:python的基本类型 1.int -----整数,主要进行数学运算 2.str -----字符串,可以保存少量数据,并进行相关操作, 3. bool ---布尔类型,判断真假 4.list ...
- [C++11]shared_ptr循环引用导致内存泄露
1 /* 2 * shared_ptr循环引用导致内存泄露 3 */ 4 5 struct A 6 { 7 shared_ptr<A> ptr; // 改为weak_ptr<A> ...
- 【java】Map、Set、List不同数据结构的各种不同循环迭代的效率对比,使用场景
Map.Set.List不同数据结构的各种不同循环迭代的效率对比,使用场景 引申一个地址:Map迭代的使用keySet和entitySet的效率
随机推荐
- jsonp帮助你知道你关注的他或她喜欢什么歌曲
利用腾讯提供的QQ音乐API,返回一段对方在QQ音乐收藏的歌曲名称json数据,并对该json做解析,就能知道你的那个他或她喜欢听什么歌曲了,然后你就知道他/她的品位了,然后就自己看着办了,嘿嘿.我只 ...
- 基于shell脚本比较数字加减乘除
让用户输入两个数来比较他们的大小 先用touch命令新建一个2.sh文件 在用vi进入i进入编辑状态 输入 保存后检查
- smack 4.1创建群聊
smack 4.1.1版本对群聊修改了很多,MultUserChat的构造函数修改成了私有,以前通过new MultUserChat创建聊天室,现在通过MultUserChatMananger先通过r ...
- Web开发者需养成的好习惯
Web开发者需养成的8个好习惯 每个行业有着每个行业的标准和一些要求,自己只是一个进入前端领域的小白,但是深刻的知道,习惯很重要,就Web开发分享一下,要养成的一些好的习惯. 优秀的Web开发人员工作 ...
- [LintCode] Product of Array Except Self 除本身之外的数组之积
Given an integers array A. Define B[i] = A[0] * ... * A[i-1] * A[i+1] * ... * A[n-1], calculate B WI ...
- centos7安装mysql5.7
http://jingyan.baidu.com/album/93f9803f010d8fe0e56f555e.html?picindex=15
- 关于mysql ERROR 1045 (28000)错误的解决办法
错误情景: 使用Navicat打开mysql的时候弹出错误框 错误代码: ERROR 1045 (28000): Access denied for user 'ODBC'@'localhost' ( ...
- navicat 结合快捷键
ctrl+q 打开查询窗口ctrl+/ 注释sql语句ctrl+shift +/ 解除注释ctrl+r 运行查询窗口的sql语句ctrl+shift+r 只运行选中的sql语句F6 打开一个mysql ...
- eclipse maven java1.8支持
下载Java 8并确保你的Eclipse版本是Kepler SR2. 选择菜单:"Help > Eclipse Marketplace-". 在搜索框中输入"Jav ...
- sqlserver 连接mysql
配置与电脑相对应的odbc http://dev.mysql.com/downloads/connector/odbc/