Can some help me give some sample code how to call this method?
Public Shared Function ReadObjects(Of T)(ByVal query As String, ByVal func As Func(Of IDataReader, T)) As IEnumerable(Of T)
Dim outbound As New List(Of T)
Using connection As OleDbConnection = NewConnection()
connection.Open()
Using command As OleDbCommand = New OleDbCommand(query, connection)
command.CommandType = CommandType.Text
command.CommandTimeout = 0
Dim reader As OleDbDataReader = command.ExecuteReader
While reader.Read()
outbound.Add(func.Invoke(reader))
End While
End Using
connection.Close()
End Using
Return outbound
End Function
Had no idea how to go about this one :)
First of all, I would write the function like this:
Public Shared Iterator Function ReadObjects(Of T)(query As String, transform As Func(Of IDataRecord, T), parameters As Action(Of OleDbParameterCollection)) As IEnumerable(Of T)
Using connection As OleDbConnection = NewConnection()
Using command As OleDbCommand = New OleDbCommand(query, connection)
If parameters IsNot Nothing Then parameters(command.Parameters)
command.CommandTimeout = 0
connection.Open()
Dim reader As OleDbDataReader = command.ExecuteReader()
While reader.Read()
Yield transform(reader)
End While
End Using
End Using
End Function
There are several changes here, but the most important is the ability to accept query parameters separate from the command string. Otherwise, the prior code would have forced you put HUGE GAPING WIDE SECURITY VULNERABILITIES into your application.
That out of the way, it sounds like you're asking how to call the method, and it's likely the Func(Of IDataRecord, T)
argument is the confusing part... and now also the Action(Of OleDbParameterCollection)
.
Here's how to do it.
First, let's get an example. Say you have an Employee
table with columns for ID
, LastName
, and FirstName
. You also have a class named Employee
, with properties that match the column names. You have a variable named lastName
, and you want to run a query to give you every record from the table where the value of the LastName
column matches the value of the lastName
variable. Then we loop through the results and write each Employee to the Console:
That would look like this:
Dim lastName As String = "Bilazon"
Dim SQL As String = "SELECT * FROM Employee WHERE LastName = ?"
Dim employees = ReadObjects(SQL,
Function(e) New Employee() With {ID = e("ID"), LastName = e("LastName"), FirstName = e("FirstName")},
Sub(p) p.Add("?", OleDbType.VarWChar, 25).Value = lastName)
For Each emp As Employee In employees
Console.WriteLine($"ID: {emp.ID}{vbTab}Last Name: {emp.LastName}{vbTab}First Name: {emp.FirstName}")
Next
The inline Function
and Sub
expressions above are called Lambda Expressions.
I might futher make this code a little easier to write and understand by adding a Shared method called FromDataRecord()
to my Employee
class:
Public Shared Function FromDataRecord(row As IDataRecord) As Employee
Return New Employee() With {
ID = row("ID"),
LastName = row("LastName"),
FirstName = row("FirstName")
}
End Function
This is especially valuable as the class adds more properties or gets more complex. Now the function call can also be simplified a bit:
Dim lastName As String = "Bilazon"
Dim SQL As String = "SELECT * FROM Employee WHERE LastName = ?"
Dim employees = ReadObjects(SQL, Employee.FromDataRecord,
Sub(p) p.Add("?", OleDbType.VarWChar, 25).Value = lastName)
For Each emp As Employee In employees
Console.WriteLine($"ID: {emp.ID}{vbTab}Last Name: {emp.LastName}{vbTab}First Name: {emp.FirstName}")
Next
The next level up is moving the ReadObjects()
method to it's own Module (and marking it private), so you also write a public method in the new Module for each of the queries you want to run.