Domanda

La mia domanda riguarda lo stato della connessione SQL, il caricamento, ecc. in base al seguente codice:

public IEnumberable<MyType> GetMyTypeObjects()
{
  string cmdTxt = "select * from MyObjectTable";

  using(SqlConnection conn = new SqlConnection(connString))
  {
    using(SqlCommand cmd = new SqlCommand(cmdTxt, conn))
    {
      conn.Open();
      using(SqlDataReader reader = cmd.ExecuteReader())
      {
         while(reader.Read())
         {
            yield return Mapper.MapTo<MyType>(reader);
         }
       }
    }
  }
  yield break;
}

Posso vedere che questo potrebbe essere un problema se ci sono molti processi che eseguono codice simile con tempi di esecuzione lunghi tra iterazioni dell'oggetto IEnumerable, perché le connessioni saranno aperte più a lungo, ecc. Tuttavia, sembra anche plausibile che questo riduca l'utilizzo della CPU sul server SQL perché restituisce dati solo quando si utilizza l'oggetto IEnumerable. Riduce inoltre l'utilizzo della memoria sul client perché il client deve caricare solo un'istanza di MyType mentre funziona anziché caricare tutte le occorrenze di MyType (ripetendo l'intero DataReader e restituendo un Elenco o qualcosa del genere).

  • Ci sono casi in cui riesci a pensare a dove non vorresti usare IEnumerable in questo modo o che ritieni si adatti perfettamente?

  • Che tipo di carico viene caricato sul server SQL?

  • È qualcosa che vorresti usare nel tuo codice (salvo qualsiasi menzione di NHibernate, Subsonic, ecc.)?

  •  -
È stato utile?

Soluzione

Non lo userei, perché nasconde ciò che sta accadendo e potrebbe lasciare le connessioni al database senza una corretta eliminazione.

L'oggetto di connessione verrà chiuso dopo aver letto l'ultimo record e se si interrompe la lettura prima che l'oggetto di connessione non verrà eliminato. Se ad esempio sai che hai sempre dieci record in un risultato e hai solo un ciclo che legge quei dieci record dall'enumeratore senza effettuare l'undicesima chiamata Read che legge oltre l'ultimo elemento, la connessione non viene chiusa correttamente. Inoltre, se si desidera utilizzare solo una parte del risultato, non è possibile chiudere la connessione senza leggere il resto dei record.

Anche le estensioni incorporate per gli enumeratori possono causare questo anche se li utilizzi correttamente:

foreach (MyType item in GetMyTypeObjects().Take(10)) {
   ...
}

Altri suggerimenti

Questo non è uno schema che seguirei. Non mi preoccuperei tanto del caricamento sul server quanto dei blocchi. Seguire questo schema integra il processo di recupero dei dati nel flusso della logica aziendale e sembra una ricetta completa per i problemi; non hai idea di cosa accada dal lato dell'iterazione e ti stai inserendo in esso. Recupera i tuoi dati in un colpo solo, quindi consenti al codice client di enumerarli dopo aver chiuso il lettore.

Sconsiglio di preottimizzare. In molte situazioni, le connessioni verranno raggruppate.

Inoltre non mi aspetto alcuna differenza nel carico su SQL Server: la query sarà già stata compilata e sarà in esecuzione.

La mia preoccupazione è che ti stai mettendo completamente in balia del codice client.

Hai già detto che il codice chiamante potrebbe mantenere la connessione aperta più a lungo del necessario, ma c'è anche la possibilità che non permetterebbe mai agli oggetti di essere chiusi / eliminati.

Fintanto che il codice client utilizza foreach , using etc o chiama esplicitamente il metodo Dispose dell'enumeratore, allora stai bene, ma c'è niente per impedirlo di fare qualcosa del genere:

var e = GetMyTypeObjects().GetEnumerator();
e.MoveNext();    // open the connection etc

// forget about the enumerator and go away and do something else
// now the reader, command and connection won't be closed/disposed
// until the GC kicks in and calls their finalisers
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top