2009-04-16 13 views
5

Sto scrivendo un metodo per restituire una riga 'risorsa' dal database. Contiene stringhe, interi e una matrice di byte (questa potrebbe essere un'immagine/film/documento).Il modo più efficiente per ottenere una riga di dati dal DB in ASP.NET

Ora per la maggior parte dell'accesso di riga, utilizzo il seguente metodo che restituisce un NameValueCollection in quanto è un oggetto leggero, facile da usare e da trasmettere int e stringhe.

 public static NameValueCollection ReturnNameValueCollection(Database db, DbCommand dbCommand) 
    { 

     var nvc = new NameValueCollection(); 

     using (IDataReader dr = db.ExecuteReader(dbCommand)) 
     { 
      if (dr != null) 
      { 
       while (dr.Read()) 
       { 
        for (int count = 0; count < dr.FieldCount; count++) 
        { 
         nvc[dr.GetName(count)] = dr.GetValue(count).ToString(); 
        } 
       } 
      } 
     } 

     dbCommand.Dispose(); 
     return nvc.Count != 0 ? nvc : null; 
    } 

Ora la mia suddetto approccio a questo tipo di accesso ai dati sarebbe normalmente per ottenere un metodo per restituire un DataRow.

 public static DataRow ReturnDataRow(Database db, DbCommand dbCommand) 
    { 
     var dt = new DataTable(); 

     using (IDataReader dr = db.ExecuteReader(dbCommand)) 
      if (dr != null) dt.Load(dr); 

     dbCommand.Dispose(); 
     return dt.Rows.Count != 0 ? dt.Rows[0] : null; 
    } 

Sembra un po 'inutile creare un DataTable e quindi restituire il suo primo datarow.

C'è un modo migliore per farlo?

Sto pensando forse a un dizionario di oggetti che poi ho lanciato manualmente ogni membro di.

Sarebbe interessante vedere come altri hanno affrontato questo. So che questo tipo rientra nel campo dell'ottimizzazione micro e fintanto che non restituisco i DataSet per ogni query di riga (vorrei avere un chilo per ogni volta che l'ho visto in una riga di codice) dovrebbe essere ok.

Detto questo è probabile che questo metodo venga chiamato per l'assegnazione di query di accesso ai dati su tutti i siti su una casella.

Acclamazioni

Steve

+1

Solo un piccolo problema, ma il chiamante deve chiamare il metodo DbCommand.Dispose, non i metodi di lettura dei dati. Poiché il tuo chiamante ha fornito l'oggetto dbCommand, dovrebbe dipingerlo. –

risposta

7

come va?

C'è un motivo per cui non si dispone di contenitori di oggetti che rappresentano una riga nel database? La creazione di un oggetto personalizzato è molto più semplice da gestire in altri livelli della soluzione. Quindi, seguendo questo approccio, ci sono due soluzioni molto valide per i tuoi problemi.

Supponiamo che tu abbia un oggetto personalizzato che rappresenta un prodotto nel tuo database. Faresti definire l'oggetto come questo:

public class Product { 
    public int ProductID { get; set; } 
    public string Name { get; set; } 
    public byte[] Image { get; set; } 
} 

E che ci si riempie una collezione di prodotti (Collection) come questo:

var collection = new Collection<Product>(); 

using (var reader = command.ExecuteReader()) { 
    while (reader.Read()) { 
     var product = new Product(); 

     int ordinal = reader.GetOrdinal("ProductID"); 
     if (!reader.IsDBNull(ordinal) { 
      product.ProductID = reader.GetInt32(ordinal); 
     } 

     ordinal = reader.GetOrdinal("Name"); 
     if (!reader.IsDBNull(ordinal)) { 
      product.Name = reader.GetString(ordinal); 
     } 

     ordinal = reader.GetOrdinal("Image"); 
     if (!reader.IsDBNull(ordinal)) { 
      var sqlBytes = reader.GetSqlBytes(ordinal); 
      product.Image = sqlBytes.Value; 
     } 

     collection.Add(product); 
    } 
} 

Si noti che sto recuperando un valore tramite il lettore di Get x dove x è il tipo che voglio recuperare dalla colonna. Questo è il modo consigliato da Microsoft di recuperare i dati per una colonna per http://msdn.microsoft.com/en-us/library/haa3afyz.aspx (secondo paragrafo) perché il valore recuperato non deve essere racchiuso in System.Object e unboxed in un tipo primitivo.

Poiché hai menzionato che questo metodo verrà chiamato molte, molte volte, in un'applicazione ASP.NET, potresti voler riconsiderare un approccio così generico come questo. Il metodo che si utilizza per restituire un valore NameValueCollection non è performante in questo scenario (e probabilmente in molti altri scenari). Per non parlare del fatto che si converte ogni colonna del database in una stringa senza tenere conto della cultura dell'utente corrente, mentre la cultura è una considerazione importante in un'applicazione ASP.NET. Direi che questo NameValueCollection non dovrebbe essere usato anche negli altri tuoi sforzi di sviluppo. Potrei andare avanti all'infinito, ma ti risparmierò le mie invenzioni.

Ovviamente, se si intende creare oggetti direttamente mappati ai propri tavoli, è consigliabile esaminare LINQ to SQL o ADO.NET Entity Framework. Sarai felice di averlo fatto.

+0

+1 perché non ho mai notato i metodi reader.Getxxx prima d'ora e questo è un ottimo consiglio! – BenAlabaster

2

Quello che stai demonstarting è un codice odore chiamato Primitive Obsession. Crea un tipo personalizzato e restituiscilo dal tuo metodo di repository. Non cercare di essere eccessivamente generico ... finirai per spingere quella complessità nel tuo codice aziendale perché interagirai con le tue entità usando un codice puramente procedurale. Meglio creare oggetti che modellano la tua attività.

Se si è interessati a un numero eccessivo di codice di accesso ai dati, cercare di utilizzare un framework ORM per occuparsi di generarlo. Non dovresti lasciare che questa preoccupazione imponga un cattivo design nel tuo livello applicativo.

3

In termini di efficienza del codice, è probabile che lo abbiate fatto con il minor numero possibile di tasti e, anche se sembra uno spreco, è probabilmente il più semplice da mantenere. Tuttavia, se sei tutto su efficienza di fare solo ciò che è strettamente necessario, è possibile creare un leggero struct/classe per popolare con i dati e usare qualcosa di simile a:

public class MyAsset 
{ 
    public int ID; 
    public string Name; 
    public string Description; 
} 

public MyAsset GetAsset(IDBConnection con, Int AssetId) 
{ 
    using (var cmd = con.CreateCommand("sp_GetAsset")) 
    { 
     cmd.CommandType = CommandType.StoredProcedure; 
     cmd.Parameters.Add(cmd.CreateParameter("AssetID")); 
     using(IDataReader dr = cmd.ExecuteReader()) 
     { 
      if (!dr.Read()) return null; 

      return new MyAsset() { 
       ID = dr.GetInt32(0), 
       Name = dr.GetString(1), 
       Description = dr.GetString(2) 
      }; 
     } 
    } 
} 

Allo stesso modo, è possibile scaricare i dati in modo simile a destra nella vostra collezione di KVPS ...

E 'non è così pulito cercando il tuo codice originale, ma non creare l'intera tabella solo per ottenere la singola riga ...

come ha sono stato menzionato in un altro post riguardo all'odore del codice, ma probabilmente non avrei passato il comando come parametro, penso che lo sarei più probabile incapsulare il comando all'interno di questo metodo, passando solo la connessione al database e l'id della risorsa che volevo - assumendo che non usassi la cache, naturalmente, e passando di nuovo l'istanza MyAsset. Ciò mantiene il metodo abbastanza generico da poter essere utilizzato su qualsiasi tipo di database - presupponendo che il proc memorizzato esistesse, naturalmente. In questo modo il resto del mio codice è protetto dal bisogno di sapere qualcosa sul database diverso dal tipo di database che è ...e nel resto della mia app, posso fare riferimento alle informazioni sulle risorse utilizzando MyAssetInstance.ID, MyAssetInstance.Name, MyAssetInstance.Description ecc ...

0

Otterrete molto più beneficio dai dati di cache rispetto al tentativo di ottimizzare la restituzione di una singola riga . Se si seleziona per chiave primaria, è improbabile che venga visualizzata una differenza tra la restituzione di un DataTable o di un DataRow o un oggetto personalizzato. Questo mi sembra un'ottimizzazione prematura. Sarei più definito, ma non sono sicuro che avere un array di byte nel mix cambi le cose.

0

Grazie per tutti gli input ragazzi. So che l'ORM è probabilmente la strada da percorrere e quella e il framework MVC sono i prossimi sulla mia lista.

Per dare un po 'più di dettaglio, il codice che sto visualizzando proviene dalla sezione helpers del mio livello di accesso ai dati che poi passa la raccolta di valori nome o riga al livello aziendale per trasformarsi in oggetti.

Penso che gli esempi di codice mnero0429 e balabaster mi diano la giusta direzione. Usa un datareader e recupera manualmente i dati in quel modo senza preoccuparti degli oggetti intemediari. Grazie per il dettagliato link MS mnero0429. Mi piace l'ossessione primigenia, perché in realtà ne faccio una vera e propria asset class nel livello aziendale;)

Guarderò anche al framework di entità ADO.

Ancora una volta, grazie per il consiglio: so che il mondo continuerà a girare anche se ho usato DataSet.Tables [0].Righe [0] ["bob"] o alcune di queste ma quando ottieni quel prurito - che cosa è il MIGLIORE wat per farlo, è bello averlo graffiato!