Оптимальный способ использования функции для свойства в дереве AutoMapper ProjectTo IQueryable - PullRequest
0 голосов
/ 08 апреля 2020

Я использую AutoMapper .ProjectTo<OrderDto>(), чтобы сопоставить некоторые Order с OrderDto.

Исходный ордер - довольно большой объект с около 30 свойствами и 15 коллекциями.

Для моего OrderDto у меня есть свойство DisplayStatus с некоторыми пользовательскими логинами c на основе 4 свойств в исходном Order. Мне не нужны эти свойства в моем последнем OrderDto, мне просто нужно, чтобы они делали что-то вроде GetDisplayStatus().

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

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

Затем я попытался сделать что-то вроде этого

.ForMember(target => target.DisplayStatus, option => option.MapFrom(source => GetDisplayStatus(source))

Проблема в том, что Entity Framework считает это «черным ящиком» и не имеет возможности узнать, что мне нужно и что я нет. Поэтому весь исходный объект передается в функцию. В результате в последнем запросе SQL применяется Select ко всем столбцам, начиная с Order, что приводит к увеличению размера запроса из-за большого количества ненужных данных, и производительность падает с большим отрывом.

В конце концов, текущий способ сделать это в нашей кодовой базе выглядит примерно так:

.ForMember(target => target.DisplayStatus, option => option.MapFrom(source => 
 GetDisplayStatus(new TempOrder{
     someProperty = source.someProperty
     anotherPropery = source.anotherProperty
}))

Этим способом я могу точно определить, какие свойства исходного Ордена я хочу чтобы перейти к моей GetDisplayStatus() функции и окончательный SQL Запрос остается чистым.

Недостатком является то, что код может быстро стать довольно уродливым, когда вещи, которые вы хотите передать своей функции, требуют некоторой работы сами.

.ForMember(target => target.DisplayStatus, option => option.MapFrom(source => 
 GetDisplayStatus(source.OrderProducts.Where(//a lot of work here too//).Select(op => 
 new TempOrderProduct
 {
     ProductTypeId = op.ProductTypeId,
     HasFiles = op.OrderFiles.Any(),
     VisitDate = op.VisitDate
 }), source.OrderStatus))) 

Опять же, это не правильно. Есть лучший способ сделать это ?

Спасибо.

Редактировать

Здесь тот же запрос без AutoMapper, как и в комментариях

public static IQueryable<OrderDTO> MapOrderDTO(this IQueryable<Orders> orders)
        {
            return orders
                .Select(order => new OrderDTO
                {
                    OrderID = order.OrderId,
                    OrderCreationDate = order.OrderCreationDate,
                    OrderStatus = order.OrderStatus,
                    DisplayStatus = GetDisplayStatus(order.OrderProducts
                                   .Where(//a lot of work//)
                                   .Select(op => new TempOrderProduct 
                                   { ProductTypeId = op.ProductTypeId,
                                     HasFiles = op.OrderFiles.Any(),
                                     VisitDate = op.VisitDate }),
                                     order.OrderStatus)
                                   })
                });
        }
...