Конвертировать EntityModel в DTO, используя «Базовый» пользовательский метод - PullRequest
0 голосов
/ 18 марта 2019

Во-первых, я прошу прощения, если это обман, поиск правильных условий поиска казался невозможным ...

Мы пытаемся использовать передовой опыт и рассматриваем рефакторинг дублированного кода в наших проектах. В ряде случаев у нас есть что-то вроде:

public List<EventModel> GetEvents(bool showInactive, bool showPastEvents)
{
    return eventRepository
        .GetEvents(_customerId, showInactive, showPastEvents)
        .Select(e => New EventModel() {  Id = e.EventId, Name = e.EventName, Capacity = e.EventCapacity, Active = e.EventActive })
        .ToList();
}

Итак, мы попытались сделать что-то подобное вместо этого;

public List<EventModel> GetEvents(bool showInactive, bool showPastEvents)
{
    return eventRepository
        .GetEvents(_customerId, showInactive, showPastEvents)
        .Select(e => ConvertPocoToModel(e))
        .ToList();
}

private EventModel ConvertPocoToModel(TsrEvent tsrEvent)
{
    EventModel eventModel = new EventModel()
    {
        Id = tsrEvent.EventId,
        Name = tsrEvent.EventName,
        Capacity = tsrEvent.EventCapacity,
        Active = tsrEvent.EventActive                
    };
    return eventModel;
}

Иногда это работает, но периодически мы получаем;

System.NotSupportedException: «LINQ to Entities не распознает метод 'Bll.Models.EventModel ConvertPocoToModel (Dal.Pocos.TsrEvent)' метод, и этот метод не может быть преобразован в выражение хранилища. '

Я знаю, что мы могли бы добавить .ToList () или аналогичный, чтобы заставить преобразование происходить в C #, но я считаю, что это означает, что SQL будет выполнять SELECT * вместо SELECT EVentId, EventName, EventCapacity, EventActive

Может кто-нибудь объяснить;

  • Почему у EF возникают проблемы, пытаясь понять, как справиться с этим простым отображением?
  • почему оно работает с перебоями?
  • Как мы должны это делать?

Ответы [ 2 ]

2 голосов
/ 18 марта 2019

Entity Framework не знает, как перевести ваш метод.Вы должны использовать метод, который возвращает Expression<Func<TsrEvent,EventModel>> или свойство, которое его хранит.

public List<EventModel> GetEvents(bool showInactive, bool showPastEvents)
{
    return eventRepository
        .GetEvents(_customerId, showInactive, showPastEvents)
        .Select(ConvertPocoToModelExpr)
        .ToList();
}

private static Expression<Func<TsrEvent,EventModel>> ConvertPocoToModelExpr =>  (x)=>new EventModel()
    {
        Id = x.EventId,
        Name = x.EventName,
        Capacity = x.EventCapacity,
        Active = x.EventActive                
    };
1 голос
/ 18 марта 2019

Вы должны знать о различиях между IEnumerable и IQueryable.

Объект IEnumerable содержит все для перечисления в последовательности.Вы можете запросить первый элемент, и как только у вас появится элемент, вы можете запросить следующий, если есть следующий.IEnumerable предназначен для процессов локально вашим процессом.

Перечисление на самом низком уровне выполняется путем запроса перечислителя и повторного вызова MoveNext, пока вам больше не нужны элементы.Например:

IEnumerable<Student> students = ...
IEnumerator<Student> studentEnumerator = students.GetEnumerator();
while (studentEnumerator.MoveNext())
{
    // there is still a Student to process:
    Student student = studentEnumerator.Current;
    ProcessStudent(student);
}

Вы можете сделать это явно или неявно вызвать его, используя foreach или одну из функций LINQ.

С другой стороны, подразумевается IQueryableобрабатываться другим процессом, обычно системой управления базами данных.IQueryable содержит Expression и Provider.Expression выражает запрос, который должен быть выполнен в некотором общем формате.Provider знает, кто должен выполнить запрос (обычно это система управления базами данных), и язык, который этот процесс использует (обычно что-то вроде SQL).

Как только вы начинаете перечисление с помощью вызова GetEnumerator, Expression отправляется Provider, который пытается перевести Expression в SQL и выполняет запрос.Извлеченные данные помещаются в перечисляемую последовательность и возвращается перечислитель.

Вернуться к вашему вопросу

Проблема в том, что SQL не знает ConvertPocoToModel,Следовательно, ваш провайдер не может конвертировать Expression.Компилятор не может обнаружить это, потому что он не знает, насколько умен ваш Provider.Вот почему вы не получите эту ошибку, пока не наберете GetEnumerator, в вашем случае - ToList.

Решение

Решение состоит в том, чтобыфункция, которая изменяет выражение.Самый простой способ - это функция расширения.См. Расширение методов расширения .Таким образом, вы можете использовать его как любой другой метод LINQ:

public static IQueryable<EventModel> ToEventModels(this IQueryable<TsrEvent> tsrEvents)
{
    return tsrEvent.Select(tsrEvent =>  new EventModel
    {
        Id = tsrEvent.EventId,
        Name = tsrEvent.EventName,
        Capacity = tsrEvent.EventCapacity,
        Active = tsrEvent.EventActive                
    };
}

Обратите внимание, что я опускаю () в конструкторе: SQL не может вызывать конструкторы!

Использование:

var result = dbContext.TsrEvents
     .Where(tsrEvent => tsrEvent.Active && tsrEvent.Date == Today)
     .ToEventModels()
     .GroupBy(...)
     ... etc

Или, если ваш GetEvents вернет IQueryable<TsrEvents>

return eventRepository.GetEvents(_customerId, showInactive, showPastEvents)
      .ToEventModels();

Окончательное замечание

Лучше оставить ваши данные-Функции fetch возвращают IQueryable<...> и IEnumerable<...> как можно дольше.Пусть только конечный пользователь материализует запрос.Было бы напрасной тратой вычислительной мощности, если вы выполните ToList(), а ваш абонент хочет только FirstOrDefault()

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...