pigouvian
asked on
Using .Net 2.0 Generics to instantiate object from datarecord
I am trying to get my head around .NET generics and need the help of a guru. Here is my code thus far:
Protected Function GetListFromSP(Of T As New)(ByVal cmd As MySqlCommand) As List(Of T)
Dim conn As New MySqlConnection(Me.Connect ionString)
Dim reader As MySqlDataReader
Dim list As New List(Of T)
cmd.Connection = conn
Try
conn.Open()
reader = Me.ExecuteReader(cmd)
If reader.HasRows Then
Dim rec As IDataRecord
For Each rec In reader
list.Add(New T(rec)) ' PROBLEM HERE
Next
End If
Catch ex As Exception
If LogExceptions Then WriteToEventLog("SWReports ", "Application", "MySQLAccountProvider: " & ex.Message, Diagnostics.EventLogEntryT ype.Error)
Finally
conn.Close()
conn.Dispose()
cmd.Dispose()
End Try
Return list
End Function
It appears that generics do not allow the creation of an object with a parameterized constructor. My questions: (1) Is it possible to do this; and (2) if not, all of my objects have parameterless constructors -- how do I set properties of T?
Thanks in advance for any help you can provide.
Protected Function GetListFromSP(Of T As New)(ByVal cmd As MySqlCommand) As List(Of T)
Dim conn As New MySqlConnection(Me.Connect
Dim reader As MySqlDataReader
Dim list As New List(Of T)
cmd.Connection = conn
Try
conn.Open()
reader = Me.ExecuteReader(cmd)
If reader.HasRows Then
Dim rec As IDataRecord
For Each rec In reader
list.Add(New T(rec)) ' PROBLEM HERE
Next
End If
Catch ex As Exception
If LogExceptions Then WriteToEventLog("SWReports
Finally
conn.Close()
conn.Dispose()
cmd.Dispose()
End Try
Return list
End Function
It appears that generics do not allow the creation of an object with a parameterized constructor. My questions: (1) Is it possible to do this; and (2) if not, all of my objects have parameterless constructors -- how do I set properties of T?
Thanks in advance for any help you can provide.
ASKER
Unless I completely have misunderstood what generics are about, the list collection will not always be of the type IDataRecord, but rather a list of type T. The IDataRecord is what I am trying to pass to the constructor for type T.
I doubt your problem has anything to do with using Generics...
Consider Generics as a "strongly-typed" collection... Instead of having just a List class (which defaults to the object type), you can now create a List class of a particular type. This is particularly important when dealing with value types (integers, for example) since they would have to undergo a boxing/unboxing operation each time they are move to/from the object type. For reference types (such as your example), it's not so important.
I can't see what your T class is... but I suppose it has New() method that takes a IDataRecord as a parameter? Post a few relevant lines of the T class for us to see.
Consider Generics as a "strongly-typed" collection... Instead of having just a List class (which defaults to the object type), you can now create a List class of a particular type. This is particularly important when dealing with value types (integers, for example) since they would have to undergo a boxing/unboxing operation each time they are move to/from the object type. For reference types (such as your example), it's not so important.
I can't see what your T class is... but I suppose it has New() method that takes a IDataRecord as a parameter? Post a few relevant lines of the T class for us to see.
Since generics are type-safe collections, you need to provide the type in place of "T"; "T" is a type parameter.
Protected Function GetListFromSP(ByVal cmd As MySqlCommand) As List(Of IDataRecord)
Dim conn As New MySqlConnection(Me.Connect ionString)
Dim reader As MySqlDataReader
Dim list As New List(Of IDataRecord)
cmd.Connection = conn
Try
conn.Open()
reader = Me.ExecuteReader(cmd)
If reader.HasRows Then
For Each rec As IDataRecord In reader
list.Add(rec)
Next
End If
Catch ex As Exception
If LogExceptions Then WriteToEventLog("SWReports ", "Application", "MySQLAccountProvider: " & ex.Message, Diagnostics.EventLogEntryT ype.Error)
Finally
conn.Close()
conn.Dispose()
cmd.Dispose()
End Try
Return list
End Function
Protected Function GetListFromSP(ByVal cmd As MySqlCommand) As List(Of IDataRecord)
Dim conn As New MySqlConnection(Me.Connect
Dim reader As MySqlDataReader
Dim list As New List(Of IDataRecord)
cmd.Connection = conn
Try
conn.Open()
reader = Me.ExecuteReader(cmd)
If reader.HasRows Then
For Each rec As IDataRecord In reader
list.Add(rec)
Next
End If
Catch ex As Exception
If LogExceptions Then WriteToEventLog("SWReports
Finally
conn.Close()
conn.Dispose()
cmd.Dispose()
End Try
Return list
End Function
ASKER
Thank you graye and Chaosian for your continued help. Perhaps I am going about my DAL the entirely wrong way. Any suggestions you have would be greatly appreciated. As for the current plan, the T class would be one of several different objects (account, transaction, user, invoice, etc.). Here is an example for the particular object called 'Account':
'*** DAL Base Class
Public MustOverride Class DALBaseClass
Protected Function GetListFromSP(Of T As New)(ByVal cmd As MySqlCommand) As List(Of T)
'See above'
End Function
End Class
'*** mySQL Implementation of account provider
Public Class mySQLAccountProvider
Inherits AccountProvider
'Non-relevant code omitted
Public Overrides Function GetAccountsByUser(ByVal x500 As String) As List(Of Ledger.Account)
Dim cmd As New MySqlCommand
Dim list As New List(Of Ledger.Account)
Dim trans As New OracleTransactionProvider
cmd.CommandText = "pr_GetAccountsByX500"
cmd.CommandType = CommandType.StoredProcedur e
cmd.Parameters.Add("prm_x5 00", MySqlDbType.String).Value = x500
list = Me.GetListFromSP(Of Ledger.Account)(cmd)
trans.SetAccountBalances(l ist)
Return list
End Function
End Class
'*** Account object
Public Class Account
Inherits Objects.ObjectsBaseClass
Implements IComparable, IEditableObject
'Non-relevant code omitted
#Region "Constructors"
Public Sub New()
End Sub
Public Sub New(ByVal Fund As String, _
ByVal Area As String, _
ByVal Org As String, _
ByVal StartDate As DateTime, _
ByVal EndDate As DateTime, _
ByVal LongDescription As String, _
ByVal ShortDescription As String, _
ByVal AccountTypes As ArrayList)
_Fund = Fund
_Area = Area
_Org = Org
_StartDate = StartDate
_EndDate = EndDate
_LongDescription = LongDescription
_ShortDescription = ShortDescription
_AccountTypes = AccountTypes
End Sub
Public Sub New(ByVal dr As IDataRecord)
_Fund = Me.GetRecordValue(Of String)("Fund", dr)
_Area = Me.GetRecordValue(Of String)("Area", dr)
_Org = Me.GetRecordValue(Of String)("Org", dr)
_StartDate = Me.GetRecordValue(Of DateTime)("PeriodStart", dr)
_EndDate = Me.GetRecordValue(Of DateTime)("PeriodEnd", dr)
_LongDescription = Me.GetRecordValue(Of String)("Title", dr)
'_ShortDescription = Me.GetRecordValue(Of String)("ShortDescription" , dr)
End Sub
#End Region
#Region "Methods"
#End Region
#Region "Helper Functions"
#End Region
End Class
'*** DAL Base Class
Public MustOverride Class DALBaseClass
Protected Function GetListFromSP(Of T As New)(ByVal cmd As MySqlCommand) As List(Of T)
'See above'
End Function
End Class
'*** mySQL Implementation of account provider
Public Class mySQLAccountProvider
Inherits AccountProvider
'Non-relevant code omitted
Public Overrides Function GetAccountsByUser(ByVal x500 As String) As List(Of Ledger.Account)
Dim cmd As New MySqlCommand
Dim list As New List(Of Ledger.Account)
Dim trans As New OracleTransactionProvider
cmd.CommandText = "pr_GetAccountsByX500"
cmd.CommandType = CommandType.StoredProcedur
cmd.Parameters.Add("prm_x5
list = Me.GetListFromSP(Of Ledger.Account)(cmd)
trans.SetAccountBalances(l
Return list
End Function
End Class
'*** Account object
Public Class Account
Inherits Objects.ObjectsBaseClass
Implements IComparable, IEditableObject
'Non-relevant code omitted
#Region "Constructors"
Public Sub New()
End Sub
Public Sub New(ByVal Fund As String, _
ByVal Area As String, _
ByVal Org As String, _
ByVal StartDate As DateTime, _
ByVal EndDate As DateTime, _
ByVal LongDescription As String, _
ByVal ShortDescription As String, _
ByVal AccountTypes As ArrayList)
_Fund = Fund
_Area = Area
_Org = Org
_StartDate = StartDate
_EndDate = EndDate
_LongDescription = LongDescription
_ShortDescription = ShortDescription
_AccountTypes = AccountTypes
End Sub
Public Sub New(ByVal dr As IDataRecord)
_Fund = Me.GetRecordValue(Of String)("Fund", dr)
_Area = Me.GetRecordValue(Of String)("Area", dr)
_Org = Me.GetRecordValue(Of String)("Org", dr)
_StartDate = Me.GetRecordValue(Of DateTime)("PeriodStart", dr)
_EndDate = Me.GetRecordValue(Of DateTime)("PeriodEnd", dr)
_LongDescription = Me.GetRecordValue(Of String)("Title", dr)
'_ShortDescription = Me.GetRecordValue(Of String)("ShortDescription"
End Sub
#End Region
#Region "Methods"
#End Region
#Region "Helper Functions"
#End Region
End Class
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Thanks for the link, graye. One of the related articles seems to validate my fear that I cannot use a parameterized constructor for T. This then raises the question can I even get/set the properties of T, or call one of T's methods? Or should I just write type-specific routines? What are your thoughts?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Excellent! Thanks, Chaosian. While I probably have received by 500 pts worth, if I may impose on you and graye I would certainly be appreciative of any advice you may have. What are your thoughts on best practices when it comes to composing a DAL using objects rather than datasets?
I'd seriously reconsider the original premise that a data access layer should be created with hand-made objects.
Personally, I'd use a disconnected DataTable to store my info... That's essentially what you're doing.... you're recreating (from scratch) what researchers have already taken years to develop (namely disconnected DataSet). You can use an XSD file to create a strongly-typed DataSet (using no code at all) that will end up equivalent to what you'd do by hand.
Personally, I'd use a disconnected DataTable to store my info... That's essentially what you're doing.... you're recreating (from scratch) what researchers have already taken years to develop (namely disconnected DataSet). You can use an XSD file to create a strongly-typed DataSet (using no code at all) that will end up equivalent to what you'd do by hand.
Actaully, graye, there's an objectdatasource in 2.0 now... that's the way I'd go about it.
In fact, by adding the tables from your database to the project, your DAL is created for you using the objectdatatable, objectdataadapter, etc -- each table has it's own objects created for you that you can simply drag-and-drop to your forms.
In fact, by adding the tables from your database to the project, your DAL is created for you using the objectdatatable, objectdataadapter, etc -- each table has it's own objects created for you that you can simply drag-and-drop to your forms.
Why are you using generics in this situation? It appears that the "list" collection will always be of type IDataRecord.