Frage

Ich habe eine sehr einfache Zuordnungsfunktion namens „BuildEntity“, die die übliche langweilige „Links/Rechts“-Codierung durchführt, die erforderlich ist, um meine Leserdaten in mein Domänenobjekt zu übertragen.(siehe unten) Meine Frage lautet: Wenn ich nicht jede Spalte in dieser Zuordnung so zurückbringe, wie sie ist, erhalte ich die Ausnahme „System.IndexOutOfRangeException“ und wollte wissen, ob ado.net etwas hat, um dies zu korrigieren, also tue ich es nicht. Es ist nicht nötig, jede Spalte bei jedem Aufruf in SQL wiederherzustellen ...

Was ich wirklich suche, ist so etwas wie „IsValidColumn“, damit ich diese eine Zuordnungsfunktion in meiner gesamten DataAccess-Klasse mit allen definierten Links/Rechts-Zuordnungen beibehalten kann – und sie auch dann funktioniert, wenn ein Sproc nicht jede aufgelistete Spalte zurückgibt. ..

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
War es hilfreich?

Lösung

Obwohl „connection.GetSchema(“Tables“) Metadaten zu den Tabellen in Ihrer Datenbank zurückgibt, wird nicht alles in Ihrem Sproc zurückgegeben, wenn Sie benutzerdefinierte Spalten definieren.

Wenn Sie beispielsweise eine zufällige Ad-hoc-Spalte wie *SELECT ProductName,'Testing' As ProductTestName FROM dbo.Products" einfügen, wird 'ProductTestName' nicht als Spalte angezeigt, da es nicht im Schema der Tabelle Products enthalten ist.Um dieses Problem zu lösen und nach jeder in den zurückgegebenen Daten verfügbaren Spalte zu fragen, nutzen Sie eine Methode für das SqlDataReader-Objekt „GetSchemaTable()“.

Wenn ich dies dem vorhandenen Codebeispiel hinzufüge, das Sie in Ihrer ursprünglichen Frage aufgeführt haben, werden Sie feststellen, dass ich unmittelbar nach der Deklaration des Readers eine Datentabelle hinzufüge, um die Metadaten vom Reader selbst zu erfassen.Als nächstes durchlaufe ich diese Metadaten und füge jede Spalte einer anderen Tabelle hinzu, die ich im Links-Rechts-Code verwende, um zu überprüfen, ob jede Spalte vorhanden ist.

Aktualisierter Quellcode

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

Um ehrlich zu sein, ist das ein Hack sollen Geben Sie jede Spalte zurück, um Ihr Objekt richtig zu hydratisieren.Aber ich dachte darüber nach, diese Lesemethode einzubeziehen, da sie tatsächlich alle Spalten erfassen würde, auch wenn sie nicht in Ihrem Tabellenschema definiert sind.

Dieser Ansatz zum Zuordnen Ihrer relationalen Daten in Ihr Domänenmodell kann zu Problemen führen, wenn Sie in ein Lazy-Loading-Szenario geraten.

Andere Tipps

Schauen Sie sich auch das an Erweiterungsmethode, die ich geschrieben habe zur Verwendung bei Datenbefehlen:

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));
        }
    }
}

Sie können es so verwenden:

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

Dabei ist „products“ das IList<Product>, das Sie füllen möchten, und „GetProduct“ enthält die Logik zum Erstellen einer Product-Instanz aus einem Datenleser.Bei diesem speziellen Problem, dass nicht alle Felder vorhanden sind, wird es nicht helfen, aber wenn Sie viel altmodisches ADO.NET wie dieses verwenden, kann es sehr praktisch sein.

Warum nicht einfach jeden Sproc einen vollständigen Spaltensatz zurückgeben lassen, wobei Null, -1 oder akzeptable Werte verwendet werden, wenn Sie nicht über die Daten verfügen?Vermeidet, IndexOutOfRangeException abzufangen oder alles in LinqToSql neu zu schreiben.

Benutzen Sie die GetSchemaTable() Methode zum Abrufen der Metadaten der DataReader.Der DataTable Mit dem zurückgegebenen Wert kann überprüft werden, ob eine bestimmte Spalte vorhanden ist oder nicht.

Warum nutzen Sie nicht LinqToSql – alles, was Sie brauchen, wird automatisch erledigt.Der Allgemeinheit halber können Sie auch jede andere verwenden ORM-Tool für .NET

Wenn Sie kein ORM verwenden möchten, können Sie auch Reflection für solche Dinge verwenden (obwohl Sie dies in diesem Fall nicht auf die hier gezeigte vereinfachte Weise tun können, da ProductID nicht auf beiden Seiten gleich benannt ist):Listenanbieter in C#

ich würde anrufen reader.GetOrdinal für jeden Feldnamen, bevor Sie die while-Schleife starten.Bedauerlicherweise GetOrdinal wirft ein IndexOutOfRangeException Wenn das Feld nicht vorhanden ist, ist es nicht sehr leistungsfähig.

Sie könnten die Ergebnisse wahrscheinlich in einem speichern Dictionary<string, int> und benutze es ContainsKey Methode, um festzustellen, ob das Feld bereitgestellt wurde.

Am Ende habe ich mein eigenes geschrieben, aber dieser Mapper ist ziemlich gut (und einfach): https://code.google.com/p/dapper-dot-net/

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top