2014-11-22 76 views
3

我有一個簡單的方案,但我想知道如果我的方法是正確的,最好建議選擇一個任務來保存我的失敗的訂單,或者我可以開始並開始多項任務並等待他們全部完成。在涉及到連接到Db和保存實體時,此場景的正確方法是什麼。正確使用異步/等待多任務到Db

我已經有下面是一個基於任務的版本保存一個實體到數據庫。

public async static Task SaveOrdersAsync(OrderService oService, OrderItemService oiService, IEnumerable<OrderTemplate> toSaveList, IUnitOfWork uow, IProgress<string> progress) 
    { 
     var toSave = toSaveList as IList<OrderTemplate> ?? toSaveList.ToList(); 
     var tasks = new Task[toSave.Count()]; 

     for (var i = 0; i < tasks.Length; i++) 
     { 
      var i1 = i; 

      tasks[i] = new Task(() => SaveToDb(oService, oiService, toSave.ElementAt(i1), uow), TaskCreationOptions.PreferFairness); 

      var message = string.Format("- Order: {0} has been resaved.\n", toSave.ElementAt(i1).Order.FriendlyId); 

      if (progress != null) 
       progress.Report(message); 
     } 

     await Task.WhenAll(tasks); 
    } 

目前,我已經測試上面,相信隨着進度條不斷循環周圍的任務還沒有開始。我的假設是Task.WhenAll應該爲我開始我的任務 - 那是我的想法?

還是應該在循環中使用這樣的:

 tasks[i] = Task.Run(() => SaveToDb(oService, oiService, toSave.ElementAt(i1), uow)); 

我想我靠近,只是希望有人告訴我,如果我正確或不這樣做。

反饋納入版本:

public async static Task SaveOrdersAsync(OrderService oService, OrderItemService oiService, IEnumerable<OrderTemplate> toSaveList, IUnitOfWork uow, IProgress<string> progress) 
    { 
     var saveList = toSaveList as IList<OrderTemplate> ?? toSaveList.ToList(); 
     var saveTask = Task.Run(() => 
     { 
      foreach (var ot in saveList) 
      { 
       SaveToDbBatch(oService, oiService, ot); 

       var message = string.Format("- Order: {0} has been resaved.\n", ot.Order.FriendlyId); 
       if (progress != null) 
        progress.Report(message); 
      } 
     }); 

     await saveTask; 
     await Cache.UoW.SaveAsync(); 
    } 

回答

5

What is the correct approach for this scenario when it comes to connecting to a Db and saving entities.

一般來說,你應該:

  1. 批次的撲救,如果可能的話。換句話說,調用一種方法來同時更新多個記錄。例如,EF有SaveChangesAsync
  2. 使用數據庫的自然異步API而不是Task.Run(或者更糟 - 任務構造函數)。例如,EF有SaveChangesAsync
+0

乾杯,斯蒂芬希望你會回覆。好酷看起來像我做了一些改變。 =) – IbrarMumtaz 2014-11-22 15:14:57

2

是的,你是正確的,在創建任務不會啓動。調用Task.Run(...)是更好的選擇。

但是,更好的選擇是使用從調用ExecuteAsync(...)返回的任務並等待。這是因爲ExecuteAsync任務是一個IO任務&不是一個線程,因此它執行的方式不同,並且不會佔用線程池線程。

作爲旁註:根據「保存」的複雜性,連續執行每次「保存」可能更加可靠。這是因爲如果有任何由並行任務引起的數據庫錯誤(如約束違規),那麼如果它們並行執行(即在隨機時間),將很難重現。

+0

優秀,我會鞏固這一切到一個任務,我沒有想到這一點。 – IbrarMumtaz 2014-11-22 15:12:24

1

new Task(...)不啓動任務。啓動它們不是Task.WhenAll的責任。任務幾乎不應該使用。使用Task.Run

0

看起來像將這個問題簡化爲一個任務,併發布在我的更新中,它也解決了我認爲我會在這裏提出的一個側面問題,因爲一些人熱衷於追求我的原始方法。但我同意@jaytre,這取決於你的保存和被保存的對象的複雜性,它可能會更好地做每個保存連續的錯誤處理 - 但這取決於你。

所以,如果你追求我原來的做法,你可能會遇到這樣的錯誤:

An EdmType cannot be mapped to CLR classes multiple times. The EdmType 'FrootPipe.Data.Order' is mapped more than once.

這基本上降低到鎖定/同步的問題 - 所以不同的任務是在更多或更少的同時訪問模型所有這些都試圖將失敗的訂單重新添加到數據模型中。所以我的方案的錯誤是有點難以分辨,但一些谷歌搜索引導我到下面。

進一步的閱讀在這裏看到:Entity framework MappingException: The type 'XXX has been mapped more than once