2016-10-04 3 views
0

у меня есть довольно основной Entity Framework объект, который выглядит следующим образом:избежать OUTER APPLY в Entity Framework проекциях

public class Student 
{ 
    public string Given { get; set; } 
    public string Surname { get; set; } 

    public ICollection<Address> Addresses { get; set; } 
} 

Я хотел бы использовать AutoMapper для отображения этого объекта к соответствующему плоской ViewModel, который выглядит следующим образом :

public class StudentViewModel 
{ 
    public string Given { get; set; } 
    public string Surname { get; set; } 

    public string PhysicalAddressStreet { get; set; } 
    public string PhysicalAddressCity { get; set; } 
    public string PhysicalAddressState { get; set; } 

    public string PostalAddressStreet { get; set; } 
    public string PostalAddressCity { get; set; } 
    public string PostalAddressState { get; set; } 
} 

Для этого я попробовал следующую конфигурацию отображения:

CreateMap<Student, StudentViewModel>() 
    .ForMember(dest => dest.Given, opt => opt.MapFrom(src => src.Given)) 
    .ForMember(dest => dest.Surname, opt => opt.MapFrom(src => src.Surname)) 
    .ForMember(dest => dest.PhysicalAddressStreet, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Physical).Street)) 
    .ForMember(dest => dest.PhysicalAddressCity, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Physical).City)) 
    .ForMember(dest => dest.PhysicalAddressState, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Physical).State)) 
    .ForMember(dest => dest.PostalAddressStreet, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Postal).Street)) 
    .ForMember(dest => dest.PostalAddressCity, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Postal).City)) 
    .ForMember(dest => dest.PostalAddressState, opt => opt.MapFrom(src => src.Addresses.FirstOrDefault(add => add.Type == AddressType.Postal).State)); 

Проблема заключается в том, когда я запускаю это отображение с помощью проекции:

studentDbSet.Where(st => st.Id == studentId) 
      .ProjectTo<TProjection>(_mapper.ConfigurationProvider); 

Я получаю следующее сообщение об ошибке:

Dynamic SQL Error SQL error code = -104 Token unknown - line 14, column 2 OUTER

Это Firebird ошибка, это, кажется, что при составлении Linq для SQL запроса который генерируется, включает OUTER APPLY, который не поддерживается в Firebird.

Есть ли способ переделать мою проекцию, чтобы избежать OUTER APPLY?

Насколько мне известно, OUTER APPLY генерируется по вызову FirstOrDefault(). Есть ли другой способ написать Linq, чтобы избежать его использования?

Редактировать для разъяснения: Это ситуация, когда я не в состоянии изменить сущность или схему базы данных, поэтому предположим, что они неприкосновенны.

+0

Моя догадка что вы должны полностью загрузить свои адреса в коллекцию, используя .Include() или проецируя его в коллекцию адресов и только после этой карты в свои свойства PhysicalAddress/PostalAddress. –

+0

@radderick спасибо, вот чего я надеялся избежать, но я боюсь, что вы можете быть правы ... –

+0

Боюсь, вы можете загружать только адреса отдельно. Нет ли лучшего поставщика запросов Firebird? –

ответ

0

Я думаю, что у вас есть проблема моделирования в основном здесь. Если вам нужен физический адрес, просто включите свойство PhysicalAddress в модель и поддерживайте эту связь. У вас все еще может быть коллекция адресов с типом. Похоже, вы делаете «FirstOrDefault», то есть либо вы можете иметь только один физический адрес, либо только первые вопросы. Я предполагаю, что это может быть только один.

Так что просто имейте это. На модели Student (и в таблице Student) введите FK в адресную таблицу «PhysicalAddress». Затем в местах, в которых вы поддерживаете адреса, обновите PhysicalAddress соответствующим образом. Инкапсулировать дочернюю коллекцию, чтобы вы не могли выполнять какую-либо операцию добавления/удаления.

Как только у вас есть отношение PhysicalAddress к Студенту, эта проблема становится тривиальной, это просто нормальное отображение.

+0

Извините, для этого вопроса предположим, что схема Entity и базы данных не может быть изменена (они являются частью другого проекта, в котором я не контролирую). Я отредактирую свой вопрос, чтобы добавить это, так как ваш ответ отличный, иначе жаль, что потратил ваше время. –

0

Вот единственный способ написания запроса LINQ, что позволяет избежать OUTER APPLY (не уверен, как это может быть сопоставлен с AutoMapper, оставив эту часть для вас, если вам это действительно нужно):

var query = 
    from student in studentDbSet 
    where student.Id == studentId 
    from physicalAddress in student.Addresses.Where(a => a.Type == AddressType.Physical) 
    from postalAddress in student.Addresses.Where(a => a.Type == AddressType.Postal) 
    select new StudentViewModel 
    { 
     Given = student.Given, 
     Surname = student.Surname, 
     PhysicalAddressStreet = physicalAddress.Street, 
     PhysicalAddressCity = physicalAddress.City, 
     PhysicalAddressState = physicalAddress.State, 
     PostalAddressStreet = postalAddress.Street, 
     PostalAddressCity = postalAddress.City, 
     PostalAddressState = postalAddress.State, 
    }; 
+0

Как использовать это для проекции Entity Framework? Предположим, что у меня был 'studentDbSet', я бы, конечно, проецировал его, используя:' studentDbSet.Select ([Expression is here]) '. –

+0

Я не понимаю комментария. Проецирование означает метод «Выбрать» или «выбрать», поэтому указанный выше запрос использует проецирование. То, как он транслирует синтаксис метода, выходит за рамки вопроса. Первоначальная проблема вызвана вызовами FirstOrDefault. Я пробовал разные варианты, и, как я уже упоминал в начале, это * единственный способ, который я нашел, который дает желаемый результат. –

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