知道类型时的反思

时间:2014-01-06 13:43:02

标签: .net vb.net design-patterns

建议使用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是不好的做法吗?这样做的原因是减少代码量,可能以处理速度为代价。

2 个答案:

答案 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)

你知道Dapper吗?它是一个迷你ORM,基本上从SQL查询映射到类。

你可以拥有:

var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = (int?)null, Id = guid });

并自动返回类Dog的通用列表,其中填充了查询的字段。您只需要在类中具有相同名称的查询字段。

您可以在https://code.google.com/p/dapper-dot-net/

查找更多信息