Domanda

Ho una funzione di mappatura molto semplice chiamata "BuildEntity" che esegue la solita noiosa codifica "sinistra/destra" richiesta per scaricare i dati del mio lettore nell'oggetto del mio dominio.(mostrato di seguito) La mia domanda è questa: se non riporto tutte le colonne in questa mappatura così come sono, ottengo l'eccezione "System.IndexOutOfRangeException" e volevo sapere se ado.net aveva qualcosa per correggerlo, quindi non lo faccio Non è necessario ripristinare ogni colonna con ogni chiamata in SQL ...

Quello che sto veramente cercando è qualcosa come "IsValidColumn" in modo da poter mantenere questa funzione di mappatura 1 in tutta la mia classe DataAccess con tutte le mappature sinistra/destra definite e farla funzionare anche quando uno sproc non restituisce tutte le colonne elencate. ..

Using reader As SqlDataReader = cmd.ExecuteReader()
  Dim product As Product
  While reader.Read()
    product = New Product()
    product.ID = Convert.ToInt32(reader("ProductID"))
    product.SupplierID = Convert.ToInt32(reader("SupplierID"))
    product.CategoryID = Convert.ToInt32(reader("CategoryID"))
    product.ProductName = Convert.ToString(reader("ProductName"))
    product.QuantityPerUnit = Convert.ToString(reader("QuantityPerUnit"))
    product.UnitPrice = Convert.ToDouble(reader("UnitPrice"))
    product.UnitsInStock = Convert.ToInt32(reader("UnitsInStock"))
    product.UnitsOnOrder = Convert.ToInt32(reader("UnitsOnOrder"))
    product.ReorderLevel = Convert.ToInt32(reader("ReorderLevel"))
    productList.Add(product)
  End While
È stato utile?

Soluzione

Sebbene Connection.GetSchema("Tables") restituisca metadati sulle tabelle nel tuo database, non restituirà tutto nel tuo sproc se definisci colonne personalizzate.

Ad esempio, se inserisci una colonna ad hoc casuale come *SELECT ProductName,'Testing' As ProductTestName FROM dbo.Products" non vedrai "ProductTestName" come colonna perché non è nello schema della tabella Products.Per risolvere questo problema e richiedere ogni colonna disponibile nei dati restituiti, sfruttare un metodo sull'oggetto SqlDataReader "GetSchemaTable()"

Se lo aggiungo all'esempio di codice esistente che hai elencato nella tua domanda originale, noterai subito dopo aver dichiarato il lettore che aggiungo una tabella dati per acquisire i metadati dal lettore stesso.Successivamente eseguo il ciclo di questi metadati e aggiungo ogni colonna a un'altra tabella che utilizzo nel codice sinistra-destra per verificare se ogni colonna esiste.

Codice sorgente aggiornato

Using reader As SqlDataReader = cmd.ExecuteReader() 
Dim table As DataTable = reader.GetSchemaTable()
Dim colNames As New DataTable()
For Each row As DataRow In table.Rows
 colNames.Columns.Add(row.ItemArray(0))
Next
Dim product As Product  While reader.Read()    
product = New Product()  
If Not colNames.Columns("ProductID") Is Nothing Then
  product.ID = Convert.ToInt32(reader("ProductID"))
End If    
product.SupplierID = Convert.ToInt32(reader("SupplierID"))    
product.CategoryID = Convert.ToInt32(reader("CategoryID"))    
product.ProductName = Convert.ToString(reader("ProductName"))    
product.QuantityPerUnit = Convert.ToString(reader("QuantityPerUnit"))    
product.UnitPrice = Convert.ToDouble(reader("UnitPrice"))    
product.UnitsInStock = Convert.ToInt32(reader("UnitsInStock"))    
product.UnitsOnOrder = Convert.ToInt32(reader("UnitsOnOrder"))    
product.ReorderLevel = Convert.ToInt32(reader("ReorderLevel"))    
productList.Add(product)  
End While

Ad essere onesti, come te, questo è un trucco Dovrebbe restituisci ogni colonna per idratare correttamente il tuo oggetto.Ma ho pensato di includere questo metodo di lettura poiché in realtà catturerebbe tutte le colonne, anche se non sono definite nello schema della tabella.

Questo approccio alla mappatura dei dati relazionali nel modello di dominio potrebbe causare alcuni problemi quando si entra in uno scenario di caricamento lento.

Altri suggerimenti

Dai un'occhiata anche a questo metodo di estensione che ho scritto per l'uso sui comandi dati:

public static void Fill<T>(this IDbCommand cmd,
    IList<T> list, Func<IDataReader, T> rowConverter)
{
    using (var rdr = cmd.ExecuteReader())
    {
        while (rdr.Read())
        {
            list.Add(rowConverter(rdr));
        }
    }
}

Puoi usarlo in questo modo:

cmd.Fill(products, r => r.GetProduct());

Dove "prodotti" è l'IList<Product> che desideri popolare e "GetProduct" contiene la logica per creare un'istanza del prodotto da un lettore di dati.Non aiuterà con questo problema specifico di non avere tutti i campi presenti, ma se stai facendo un sacco di ADO.NET vecchio stile come questo può essere abbastanza utile.

Perché non semplicemente fare in modo che ogni sproc restituisca un set di colonne completo, utilizzando valori null, -1 o accettabili in cui non si dispone dei dati.Evita di dover rilevare IndexOutOfRangeException o riscrivere tutto in LinqToSql.

Usa il GetSchemaTable() metodo per recuperare i metadati del file DataReader.IL DataTable quello restituito può essere utilizzato per verificare se una colonna specifica è presente o meno.

Perché non usi LinqToSql: tutto ciò di cui hai bisogno viene eseguito automaticamente.Per ragioni generali puoi usarne qualsiasi altro Strumento ORM per .NET

Se non vuoi utilizzare un ORM puoi anche utilizzare la riflessione per cose come queste (anche se in questo caso, poiché ProductID non ha lo stesso nome su entrambi i lati, non potresti farlo nel modo semplicistico dimostrato qui):Elenco provider in C#

Vorrei chiamare reader.GetOrdinal per ogni nome di campo prima di iniziare il ciclo while.Purtroppo GetOrdinal lancia un IndexOutOfRangeException se il campo non esiste, quindi non sarà molto performante.

Probabilmente potresti memorizzare i risultati in a Dictionary<string, int> e usalo ContainsKey metodo per determinare se il campo è stato fornito.

Ho finito per scriverne uno mio, ma questo mappatore è piuttosto buono (e semplice): https://code.google.com/p/dapper-dot-net/

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top