Как я могу передать выражение, чтобы действовать на одно поле в анонимном типе в LINQ - PullRequest
2 голосов
/ 27 марта 2012

У меня определены следующие объекты передачи данных:

Public Class MemberWithAddressesDTO
  Public Property Member_PK As Integer
  Public Property Firstname As String
  Public Property DefaultAddress As AddressDTO
  Public Property Addresses As IQueryable(Of AddressDTO)
End Class

Public Class AddressDTO
  Public Property Address_PK As Integer
  Public Property Address1 As String
  Public Property Address2 As String
End Class   

Я определил следующее выражение, которое сопоставляет мою сущность с моим DTO, чтобы я мог повторно использовать это в запросах (фактическое выражение генерируется T4шаблон):

Public Shared AsAddressDTO As Expression(Of Func(Of Address, AddressDTO)) =
  Function(ent As Address) New AddressDTO With {.Address_PK = ent.Address_PK, _
                                                 .Address1 = ent.Address1,
                                                 .Address2 = ent.Address2}

Я могу использовать это выражение в запросе LINQ-to-Entities для преобразования списков объектов Address в список AddressDTO:

Using context As New DbContext
  Dim mem As MemberWithAddressesDTO = 
    context.Members _
      .Where(Function(m) m.Person_PK = 121) _
      .Select(Function(m) New MemberWithAddressesDTO With {
                .Member_PK = m.Person_PK, _
                .Firstname = m.Firstname, _
                .Addresses = ent.Addresses.AsQueryable.Select(AsAddressDTO)}
             ).FirstOrDefault()
End Using

Однако это работает нормальноесли я хочу использовать одно и то же выражение для преобразования одиночного поля в члене в AddressDTO в анонимном типе, единственный способ заставить это работать - поместить поле в массив из одного элемента, привестивыполнить запрос и затем вызвать select для этого - это кажется немного сложным, и мне интересно, есть ли лучший способ:

Using context As New DbContext
  Dim mem As MemberWithAddressesDTO = _
    context.Members _
      .Where(Function(m) m.Person_PK = 121) _
      .Select(Function(m) New MemberWithAddressesDTO With {
        .Member_PK = m.Person_PK, _
        .Firstname = m.Firstname, _
        .DefaultAddress = {m.DefaultAddress}.AsQueryable.Select(AsAddressDTO).FirstOrDefault}
      ).FirstOrDefault()
End Using

В основном я хочу знать, есть ли лучший синтаксис для достижения этой строки ввыше:

.DefaultAddress = {m.DefaultAddress}.AsQueryable.Select(AsAddressDTO).FirstOrDefault}

Примечание. m.DefaultAddress - это отдельное поле типа Адрес - это не коллекция.

Есть идеи?

Ответы [ 2 ]

0 голосов
/ 08 февраля 2013

NB Addresses - это еще не выполненный запрос, где DefaultAddress является действительным AddressDTO.

В любом случае, поскольку вы хотите сохранить Expression, вам необходимо сохранить IQueryable. (Обратите внимание, что Queryable.Select имеет перегрузку Expression, а Enumerable.Select - нет.)

И «отложенное выполнение», которое мы используем , равно на основе IEnumerable, поэтому для запрашиваемого .SelectOne будет все еще нужен .First, вызываемый позже.

Таким образом, вам нужно где-то превратить одно значение в перечисление. И в итоге вы получите код, почти идентичный тому, с которого вы начали:

 <Extension> _
 Public Function SelectOne(Of TSource, TResult)(ByVal source As TSource, ByVal selector As Expression(Of Func(Of TSource, TResult))) As IQueryable(Of TResult)
     Return Enumerable.Repeat(source, 1).AsQueryable.Select(selector)
 End Function

Я пытался вернуть выражение, которое Invoke selector с source, но, как я уже упоминал выше, AFAICT вам нужен IEnumerable.

0 голосов
/ 28 марта 2012

Разве вы не можете сделать что-то вроде этого:

Using context As New DbContext
  ' Get a delegate to the expression
  Dim func As Func(Of Address, AddressDTO) = AsAddressDTO.Compile()

  Dim mem As MemberWithAddressesDTO = _
    context.Members _
      .Where(Function(m) m.Person_PK = 121) _
      .Select(Function(m) New MemberWithAddressesDTO With {
        .Member_PK = m.Person_PK, _
        .Firstname = m.Firstname, _
        .DefaultAddress = func({m.DefaultAddress})}
      ).FirstOrDefault()
End Using
...