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 = 0; i < 100; i++)
            {                var random = new Random();                var number = random.Next(1, 200000);                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倍呢!不必多说,你懂的!

























本文转自xsster51CTO博客,原文链接:http://blog.51cto.com/12945177/1932197 ,如需转载请自行联系原作者



相关文章
|
存储 编译器
深入解析i++和++i的区别及性能影响
在我们编写代码时,经常需要对变量进行自增操作。这种情况下,我们通常会用到两种常见的操作符:i++和++i。最近在阅读博客时,我偶然看到了有关i++和++i性能的讨论。之前我一直在使用它们,但从未从性能的角度考虑过,这让我突然产生了兴趣。尽管它们看起来相似,但它们之间存在微妙而重要的区别。在本文中,我们将详细解释i++和++i之间的区别,以及它们对代码性能的影响。
391 1
深入解析i++和++i的区别及性能影响
|
Serverless
函数计算在执行请求的过程中遇到了意外的错误
函数计算在执行请求的过程中遇到了意外的错误
93 1
|
17天前
|
传感器 存储 索引
如何解决 analogRead()函数读取到的模拟值不准确的问题
在使用analogRead()函数时,若读取到的模拟值不准确,可以通过校准ADC、增加采样次数取平均值、使用外部参考电压或检查电路连接等方式来提高读取精度。
|
1月前
|
存储 C#
【C#】大批量判断文件是否存在的两种方法效率对比
【C#】大批量判断文件是否存在的两种方法效率对比
40 1
|
2月前
|
Python
让 for 和 while 循环具有 C 级别的性能
让 for 和 while 循环具有 C 级别的性能
28 0
|
缓存 索引
这 11 个 for 循环优化你得会
这 11 个 for 循环优化你得会
|
消息中间件 数据采集 Kafka
每次join之后没有正确处理数据的重复或缺失情况
每次join之后没有正确处理数据的重复或缺失情况
126 1
|
Web App开发 测试技术
优化循环的方法-循环展开
优化循环的方法-循环展开
94 0
|
设计模式 JavaScript 前端开发
如何优雅的消除系统重复代码
在程序猿的日常工作中,不仅要跟随业务侧的发展不断开发新的需求,同时也需要维护老的已有平台。无论是开发新需求还是维护老系统,我们都会遇到同样一个问题,系统中总是充斥着很多重复的代码。
29568 11
如何优雅的消除系统重复代码
|
分布式数据库 数据库
复制延迟案例(1)-最终一致性
该案例违反因果律。 想象先生和夫人之间的对话: Mr Mrs,你能看到多远未来? Mrs 通常约10s,Mr.
92 0