2014-07-04 8 views
10

Richiediamo migliaia di oggetti indietro da Dapper e raggiungiamo il limite di parametro (2100), quindi abbiamo deciso di caricarli in blocchi.Utilizzo di Async e Attendi per interrompere la chiamata al database (con Dapper)

Ho pensato che sarebbe stata una buona occasione per provare Async Await - questa è la prima volta che ho avuto un tentativo così magari facendo un errore da ragazzino!

I punti di interruzione sono stati colpiti, ma il tutto non sta semplicemente tornando. Non si tratta di un errore: sembra proprio che tutto vada in un buco nero!

Aiuto per favore!

Questo è stato il mio metodo originale - ora chiama il metodo asincrono

public List<MyObject> Get(IEnumerable<int> ids) 
    { 
     return this.GetMyObjectsAsync(ids).Result.ToList(); 
    } //Breakpoint on this final bracket never gets hit 

ho aggiunto questo metodo per suddividere gli id ​​in blocchi di 1000 e poi aspettano per i compiti da completare

private async Task<List<MyObject>> GetMyObjectsAsync(IEnumerable<int> ids) 
    { 
     var subSets = this.Partition(ids, 1000); 

     var tasks = subSets.Select(set => GetMyObjectsTask(set.ToArray())); 

     //breakpoint on the line below gets hit ... 
     var multiLists = await Task.WhenAll(tasks); 

     //breakpoint on line below never gets hit ... 
     var list = new List<MyObject>(); 
     foreach (var myobj in multiLists) 
     { 
      list.AddRange(myobj); 
     } 
     return list; 
    } 

e sotto è il compito ...

private async Task<IEnumerable<MyObject>> GetMyObjectsTask(params int[] ids) 
    { 
     using (var db = new SqlConnection(this.connectionString)) 
     { 
      //breakpoint on the line below gets hit 
      await db.OpenAsync(); 
      return await db.QueryAsync<MyObject>(@"SELECT Something FROM Somewhere WHERE ID IN @Ids", 
      new { ids}); 
     } 
    } 

Il seguente metodo divide solo la lista di id s in blocchi - questo sembra funzionare bene ...

private IEnumerable<IEnumerable<T>> Partition<T>(IEnumerable<T> source, int size) 
    { 
     var partition = new List<T>(size); 
     var counter = 0; 

     using (var enumerator = source.GetEnumerator()) 
     { 
      while (enumerator.MoveNext()) 
      { 
       partition.Add(enumerator.Current); 
       counter++; 
       if (counter % size == 0) 
       { 
        yield return partition.ToList(); 
        partition.Clear(); 
        counter = 0; 
       } 
      } 

      if (counter != 0) 
       yield return partition; 
     } 
    } 
+0

È possibile seguire un debugger tramite GetMyObjectsAsync? Corre fino alla fine? – Stilgar

risposta

10

si sta creando una situazione di stallo il modo di usare async/await in combinazione con Task<T>.Result.

La linea incriminata è:

return this.GetMyObjectsAsync(ids).Result.ToList(); 

GetMyObjectsAsync(ids).Result blocchi contesto sincronizzazione corrente. Ma GetMyObjectsAsync utilizza await che pianifica un punto di continuazione per il contesto di sincronizzazione corrente. Sono sicuro che è possibile vedere il problema con questo approccio: la continuazione non può mai essere eseguita perché il contesto di sincronizzazione corrente è bloccato da Task.Result.

Una soluzione che può funzionare in alcuni casi è quella di utilizzare ConfigureAwait(false) che significa che la continuazione può essere eseguita su qualsiasi contesto di sincronizzazione.

Ma in generale, penso che sia meglio evitare Task.Result con async/await.


prega di notare che se questa situazione di stallo si verifica in realtà dipende dal contesto di sincronizzazione che viene utilizzato quando si chiama il metodo asincrono. Per ASP.net, Windows Form e WPF questo si tradurrà in un deadlock, ma per quanto ne so non lo farà per un'applicazione console. (Grazie a Marc Gravell per il suo commento)


Microsoft ha un buon articolo su Best Practices in Asynchronous Programming.(Grazie a ken2k)

+3

bene che mi ha salvato un po 'di digitazione, grazie –

+1

+1. Inoltre, mi sento in dovere di condividere questo articolo: http://msdn.microsoft.com/en-us/magazine/jj991977.aspx – ken2k

+1

@Dirk in senso stretto, indipendentemente dal fatto che questo causi o meno un deadlock dipende dal "contesto di sincronizzazione" "che è in uso; a volte funzionerà bene come scritto - e talvolta (asp.net, winforms, wpf, ecc.): non lo farà (e lo farà come dici tu: deadlock). 'ConfigureAwait' è una opzione (che in realtà evita il contesto di sincronizzazione, piuttosto che essere specificamente sui thread), ma sono d'accordo: mixare' await' e '.Result' è una ricetta per il dolore –

0

Credo che i parametri sono case sensitive che dovrebbe essere:

return await db.QueryAsync<MyObject>(@"SELECT Something FROM Somewhere WHERE ID IN @ids", 
      new { ids}); 

invece di "@Ids" di seguito illustrati in query:

return await db.QueryAsync<MyObject>(@"SELECT Something FROM Somewhere WHERE ID IN **@Ids**", 
      new { ids}); 

non è sicuro se, ma solo provare.

Problemi correlati