EF循環迭代致使如此嚴重的性能丟失,你知道?

前言

在工做當中呢,沒怎麼用到過EF,因此爲了遺忘這一部分知識,偶爾會去寫寫小的demo,偶然機會在EF循環迭代中發現竟然影響性能這麼嚴重,當咱們在用時或許大概也許可能都曾這樣寫過,可是你注意到了嗎,你懷疑過嗎?這就是本節所要討論的話題。如有錯誤,請批評指出。數據庫

話題

關於基礎知識咱們就不廢話了哈,咱們假設這樣一個場景(不必定嚴謹,只是爲了引出話題):當在下單中,若是有多我的下單,此時咱們須要經過訂單Id去獲得客戶Id。在這一場景中咱們給出一個訂單類以及訂單處理類。以下:app

     //訂單類
     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。dom

爲了你們可能方便測試,將其他說了不少次的映射類以及上下文都給出來。ide

    //訂單映射類
    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;
            }

此時咱們來測試下耗費的時間。ui

結果是3.2秒,看到這裏有人就說了,並且你們都看到過都知道,在查詢時爲了性能咱們能夠關閉EF的變動追蹤,因而乎就有了下面的代碼:this

改良一

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

//替換成

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

此時看看演示結果:spa

此時耗費的時間爲1.7秒,固然有時會更短,反正比上述加了變動追蹤狀況的耗費時間要少。code

到了這裏你是否是就以爲已經很知足了呢?是否是以爲已經獲得了很大的改善了呢?要是這樣關閉變動追蹤就能夠解決了問題,那我這篇文章你說還有什麼意義呢?好了廢話很少說。本文討論的話題就在於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】:最近找工做中,工做地點:深圳,但願你們能夠推薦推薦!

相關文章
相關標籤/搜索