在工做當中呢,沒怎麼用到過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】:最近找工做中,工做地點:深圳,但願你們能夠推薦推薦!