2014-01-06 3 views
1

Можно ли использовать 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 в каждом классе домена, например, Заказ, 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, когда вы знаете, какие типы находятся во время компиляции? Причиной этого является уменьшение количества кода, возможно, за счет скорости обработки.

+1

вы смотрели на LINQ к SQL ли? –

+0

Как бы код, используя отражение, знал, какой тип был подходящим, основываясь только на считывателе данных? У читателя данных есть свойство, которое каким-то образом определяет тип данных, которые он читает? –

+0

@Steven Doggart, все типы наследуются от clsType i.e. clsType.Make используется всеми типами (PersonType, OrderType и т. Д.). Имеет ли это смысл? – w0051977

ответ

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. Выбор в конечном счете - ваш.

И да, получите ОРМ, чтобы вывести себя из всего этого.

Update (на основе комментариев):

Я проиллюстрирую это на примере. (Обратите внимание, что это всего лишь примеры и в лучшем случае являются грубыми). Как вы сказали, ваши классы наследуются от общего типа, и свойства вам известны.

Таким образом, ваш базовый класс это:

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.

Надеюсь, что это поможет.

+0

Спасибо. Вы использовали этот подход раньше? +1. – w0051977

+0

Да и Нет Да, для отражения, то есть для второй части моего ответа, особенно для целей отчетности, когда мне приходится заполнять разные DTO из универсальных представлений. Нет, для общей части. Я предпочитаю хранить отдельные слои данных для каждого объекта домена. – Abhitalks

+0

Я не понимаю, как код в части 1 заполняет объект без Reflection. – w0051977

0

Знаете ли вы, Dapper? Это мини-ORM, который в основном отображает из SQL-запроса в класс.

Вы можете иметь это:

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

и он автоматически возвращает список Generic из класса Dog, наполненный полями запроса. Вам нужно только иметь поля запроса с тем же именем в классе.

Вы можете найти более подробную информацию на https://code.google.com/p/dapper-dot-net/

Смежные вопросы