2

Currently, I am using a SqlDataReader loaded up with a simple database 'Select' query. Each row represents a domain object, so I inflate each object like this:

Dim loadedItems As New List(Of Item)
Dim dr As SqlDataReader = GetItemsDataReader()
While dr.Read()
  Dim item As Item = GetItemFromData(dr)
  loadedItems.Add(item)
End While

The GetItemFromData method I wrote looks something like this:

Private Function GetItemFromData(dr As SqlDataReader) As Item
  Dim loadedItem As New Item()
  loadedItem.ID = dr("ID")
  loadedItem.Name = dr("Name")
  'etc., etc.'
  Return loadedItem
End Function

In some cases, I have to read data from a DataRow instead of a SqlDataReader. But the code would be the exact same! As I look at my GetItemFromData method, I want to accept a more generic type of object in the dr parameter so that I can treat a DataReader the same as a DataRow, since I would be writing the exact same code inside the method if I wrote one that was meant to use a DataRow. Is there a way to do that?

Brandon Montgomery
  • 6,924
  • 3
  • 48
  • 71

2 Answers2

6

Only way I can think of off the top of my head would be to wrap a few classes and implement an inferface - something like:

Interface IIndexer
    Default ReadOnly Property Item(ByVal index As String)
End Interface

Class DataReaderWrapper
    Implements IIndexer

    Private ReadOnly _reader As IDataReader

    Public Sub New(reader As IDataReader)
        _reader = reader
    End Sub

    Public ReadOnly Property Item(index As String) As Object Implements IIndexer.Item
        Get
            Return _reader(index)
        End Get
    End Property
End Class

Class DataRowWrapper
    Implements IIndexer

    Private ReadOnly _row As DataRow

    Public Sub New(row As DataRow)
        _row = row
    End Sub

    Public ReadOnly Property Item(index As String) As Object Implements IIndexer.Item
        Get
            Return _row(index)
        End Get
    End Property
End Class

You would then have to wrap your row or reader before passing it to your method:

Private Function GetItemFromData(indexer As IIndexer) As Item
    Dim loadedItem As New Item()
    loadedItem.ID = indexer("ID")
    loadedItem.Name = indexer("Name")
    'etc., etc.'
    Return loadedItem
End Function

Dim i As Item = GetItemFromData(New DataRowWrapper(dr))
Joe Enos
  • 39,478
  • 11
  • 80
  • 136
  • 4
    This is essentially the adapter pattern wrapping both objects. I will note that `SqlDataReader` implements `IDataReader`, which exposes the `Item` properties. Unfortunately, `DataRow` does not implement this same interface, although it exposes the same functionality. In this case, using the adapter pattern to adapt *just* the `DataRow` (`class DataRowAdapter : IDataReader`), and then having the method accept `IDataReader` might be a viable course of action. – Anthony Pegram Oct 05 '11 at 20:14
  • 5
    That DataRow does not implement IDataRecord is a sad mistake in the bcl :( – Joel Coehoorn Oct 05 '11 at 20:16
  • 2
    @AnthonyPegram Makes sense, but there's a boatload of properties and methods in IDataReader, so you'd have to have a bunch of `NotImplementedException` or similar on all but one of the properties of the implementation (or actually implement them, which doesn't seem appropriate if all you care about is the indexer). – Joe Enos Oct 05 '11 at 20:18
  • @Joe - you could build your wrapper only on IDataRecord, which doesn't have nearly as much to implement. – Joel Coehoorn Oct 05 '11 at 20:23
  • @AnthonyPegram @JoelCoehoorn Agreed - `DataRow` definitely should have implemented `IDataRecord`. I wonder if there was any reason or if it simply was an oversight that hasn't been fixed. – Joe Enos Oct 05 '11 at 20:23
  • @Joe, yes, I believe I misspoke in my original comment which I now cannot edit. I intended to say `IDataRecord`, but got myself lost somewhere. Although you raise a good point about the fat interface. – Anthony Pegram Oct 05 '11 at 20:24
  • @JoelCoehoorn Check out the [MSDN docs](http://msdn.microsoft.com/en-us/library/system.data.idatarecord.aspx) - there are a bunch of `GetXXX` methods to implement. Not as much as `IDataReader`, but still enough to make me want to avoid it personally. – Joe Enos Oct 05 '11 at 20:25
0

How about using a DataTable instead:

Dim DataTable table AS new DataTable()
table.Load(GetItemsDataReader())
foreach (DataRow row in table.Rows) {
   Dim item As Item = GetItemFromData(row)
   loadedItems.Add(item)
}

Change your GetItemFromData() method to take a DataRow instead of a DataReader (sorry about the VB.NET/C# hybrid pseudo code).

C-Pound Guru
  • 15,967
  • 6
  • 46
  • 67