2014-04-04 15 views
7

mam EF klasy pomocnika, który zapisuje zmiany asynchronicznie:Entity Framework 6.1.0 SaveChangesAsync

public async Task<int> SaveOrUpdateAsync<TEntity>(TEntity entity) 
     where TEntity : class, IContextEntity 
    { 
     if (entity.Id == 0) 
      context.Set<TEntity>().Add(entity); 
     else 
     { 
      TEntity dbEntry = context.Set<TEntity>().Find(entity.Id); 
      if (dbEntry != null) dbEntry = entity; 
     } 

     return await context.SaveChangesAsync(); 
    } 

public void Save() 
{ 
Task saveEntit1Async = repository.SaveOrUpdateAsync<Entity1>(entity1); 
Task saveEntity2Async = repository.SaveOrUpdateAsync<Entity2>(entity2); 
Task saveEntity3Async = repository.SaveOrUpdateAsync<Entity3>(Entity3); 

Task.WaitAll(saveEntit1Async, saveEntity2Async, saveEntity3Async); 

string test = "test"; 
) 

Wezwanie utknie na

Task.WaitAll(saveEntit1Async, saveEntity2Async, saveEntity3Async); 

linii i nigdy nie trafia do

string test = "test"; 

Ale jeśli uruchomię to jako:

public void Save() 
{ 
repository.SaveOrUpdateAsync<Entity1>(entity1); 
repository.SaveOrUpdateAsync<Entity2>(entity2); 
repository.SaveOrUpdateAsync<Entity3>(Entity3); 

string test = "test"; 
) 

To działa dobrze, wszystkie zmiany są zapisywane i robi się

string test = "test"; 

Dlaczego

Task.WaitAll(saveEntit1Async, saveEntity2Async, saveEntity3Async); 

zawiesza operację i nigdy nie przekazuje rozmowę do następnej linii kodu (test ciąg = "test";)?

+1

Task.WaitAll, system nie blokuje bieżącego wątku wykonania, a następna linia zostanie wykonana, z Task.WaitAll, bieżący wątek będzie czekał aż wszystkie operacje zostaną wykonane, następnie ciąg test = 'test'; zostaje wykonany – Sherlock

+0

Dobrze! Ale dlaczego telefon utknął na WaitAll na zawsze! i nigdy nie dociera do drugiej linii? –

+0

możesz mieć wyjątek w zadaniu, który może nigdy nie wrócić, więc będzie czekać na zawsze – Sherlock

Odpowiedz

12

Wymyśliłem!

Oto problem, który wystąpił, gdy użytkownik czeka na Zadanie z metodą "Czekaj" lub bierze wynik bezpośrednio z właściwości "Wynik" zadania, blokuje główny wątek w tym samym czasie . Kiedy w końcu zadanie zostanie wykonane wewnątrz tej metody (SaveOrUpdateAsync (obiekt TEntity)) w puli wątków, wywoła ona kontynuację, aby wysłać z powrotem do głównego wątku (ponieważ nigdy go nie opuścił), ponieważ SynchronizationContext.Current jest dostępny i przechwytywany . Ale tutaj jest problem: główny wątek jest blokowany metodą "Czekaj" i tak właśnie dostałem impasu!

Aby naprawić problem z zakleszczeniem, musiałem określić, aby nie kontynuować przechwyconego kontekstu dla kontekstu.SaveChangesAsync().

public async Task<int> SaveOrUpdateAsync<TEntity>(TEntity entity) 
     where TEntity : class, IContextEntity 
    { 
     if (entity.Id == 0) 
      context.Set<TEntity>().Add(entity); 
     else 
     { 
      TEntity dbEntry = context.Set<TEntity>().Find(entity.Id); 
      if (dbEntry != null) dbEntry = entity; 
     } 

     return await context.SaveChangesAsync().ConfigureAwait(continueOnCapturedContext: false); 
    } 
+1

Nawet przy użyciu Task.Wait, tylko czekać i nadal miał problem. Ten fragment naprawił mój kod. Stukrotne dzięki. – TombMedia

0

może jestem głupi (!), Ale dlaczego Twój kod powiedzieć

if (dbEntry != null) dbEntry = entity; 

pewno, że jeśli oświadczenie powinno być

if (dbEntry == null) dbEntry = entity; 

Chyba C# zerowej-koalescencyjny operator może również zastąpić obydwie linie:

TEntity dbEntry = context.Set<TEntity>().Find(entity.Id) ?? entity;