建议使用Reflection将数据读取器映射到对象吗?我认为传统的做法如下(没有反思):
Public Class Person
Public Function Make(ByVal objDR As dbDataReader) As PersonType
Dim Person As New PersonType
'Loop through dbDataReader and create PersonType
Return Person
End Function
End Class
你会在每个域类中都有一个Make函数,例如Order,OrderItem等。
我正在考虑做这样的事情:
Public Class clsTypes
Public Function Make(ByVal objDR As dbDataReader)
'Use Reflection to map data reader to appropriate type
End Function
End Class
OrderType,PersonType,OrderItemType等继承自clsType,因此他们可以调用clsType.Make,例如
Dim p as new Person p.Make(objDR)
一旦我将所有映射代码放在一个地方,我打算引入AutoMapper。但是,与此同时,当您在编译时知道类型是什么时,使用Reflection是不好的做法吗?这样做的原因是减少代码量,可能以处理速度为代价。
答案 0 :(得分:2)
您可以通过从类型定义中取出此方法来使用泛型,如下所示:
Public Shared Function Make(Of T)(ByVal objDR As dbDataReader) As T
重要:这假设您的类型具有与dbDataReader对应的所有属性。
然而,如果您需要循环访问属性并选择相关列,则必须对该类型使用反射,并且还必须检查该类型中是否存在该特定列。 dbDataReader。
在这种情况下,您可以使用实用程序函数来检查dbDataReader中是否存在,如下所示:
Public Shared Function IfExists(ByVal record As Common.DbDataRecord, ByVal columnName As String) As Boolean
For i As Integer = 0 To record.FieldCount - 1
If record.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase) Then
Return True
End If
Next
Return False
End Function
在循环dbDataReader并传递每个DbDataRecord的地方,您将反射的属性名称作为columnName传递。
在这种情况下,我不认为反思应该是一种不好的做法。当然,会有一些性能损失,但是你获得了代码重用和DRY。选择最终是你的。
是的,得到一个ORM让你自己摆脱这一切。
更新(根据评论):
我将用一个例子来说明它。 (请注意,这些仅仅是示例,并且最好是粗略的)。正如你所说,你的类继承了一个普通类型,你知道这些属性。
所以你的基类是这样的:
Public Class ClsType
Public Property Id As String
Public Property Desc As String
End Class
这些类继承自ClsType
:
Public Class Person
Inherits ClsType
End Class
Public Class Order
Inherits ClsType
End Class
所以你创建了一个Utility
类(或你的库代码):
Public NotInheritable Class Utility
Public Shared Function Make(Of T As {ClsType, New})(ByVal objDR As Common.DbDataReader) As List(Of T)
Dim result = New List(Of T)
For Each rec As Common.DbDataRecord In objDR
Dim tmp As T = New T
If IfExists(rec, "Id") AndAlso Not rec.IsDBNull(rec.GetOrdinal("Id")) Then tmp.Id = rec.GetString(rec.GetOrdinal("Id"))
If IfExists(rec, "Desc") AndAlso Not rec.IsDBNull(rec.GetOrdinal("Desc")) Then tmp.Desc = rec.GetString(rec.GetOrdinal("Desc"))
result.Add(tmp)
Next
Return result
End Function
Public Shared Function IfExists(ByVal record As Common.DbDataRecord, ByVal columnName As String) As Boolean
For i As Integer = 0 To record.FieldCount - 1
If record.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase) Then
Return True
End If
Next
Return False
End Function
End Class
现在,无论何时需要从datareader填充任何类的数据(请注意,根据您的问题知道类型,以及属性),您只需将此实用程序称为:
Dim result As List(Of Person) = Nothing
Dim objDR As Common.DbDataReader = Nothing
result = Utility.Make(Of Person)(objDR)
注意强> :
这纯粹基于已知类型及其属性的假设。在这种情况下,您可以安全地对所有已知属性进行硬编码。当然,正如@tony所说,如果更改数据库,则必须自己同步这些类。
如果不是,那么您无选择,但要在T
方法中对Make
使用反射。
希望有所帮助。
答案 1 :(得分:0)
你可以拥有:
var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });
并自动返回类Dog的通用列表,其中填充了查询的字段。您只需要在类中具有相同名称的查询字段。
查找更多信息