Доступ к дочернему свойству из вложенного DTO типа IQueryable <T> - PullRequest
2 голосов
/ 10 марта 2020

Использование. NET Core 3.1 с EF Core 3.1

Учитывая эти 3 вложенных DTO:

public class AdminImmoOrderDto
{
    public IQueryable<AdminImmoOrderProductDto> OrderProducts { get; set; }
    public bool IsCompleted { get; set; }
}

public class AdminImmoOrderProductDto
{
    public IQueryable<AdminImmoOrderProductFileDto> OrderFiles { get; set; }
    public bool IsCompleted { get; set; }
}

public class AdminImmoOrderProductFileDto
{
    public string FileName { get; set; }
    public string FileUrl { get; set; }
}

Заказ содержит несколько OrderProducts, а эти OrderProducts содержат некоторые OrderFiles. Логика c выглядит следующим образом: чтобы быть IsCompleted, у OrderProduct должен быть хотя бы один OrderFile.

Я написал некоторый метод расширения для создания логики отображения и проекции c, например:

public static IQueryable<AdminImmoOrderProductDto> MapOrderProductToDto(this ICollection<OrderProducts> products)
        {
            return products
                .AsQueryable()
                .Select(p => new AdminImmoOrderProductDto
                {
                    IsCompleted = p.OrderFiles.Any(of => of != null),  
                    OrderFiles = p.OrderFiles.MapOrderProductFileToDto()
                });
        }
    }

До там все работает. Но, как вы можете видеть, я использую какой-то метод для отображения коллекции потомков в методе родительского отображения.

Имея это в виду, при попытке установить IsCompleted для верхнего уровня DTO OrderDto мне нужно отфильтровать дочернее свойство OrderProductDto.IsCompleted. На этот раз все OrderProducts должны быть завершены, чтобы родительский ордер считался завершенным. Мне нужно получить доступ к IsCompleted в коллекции детей, но я не могу, поэтому я пытаюсь это:

public static IQueryable<AdminImmoOrderDto> MapOrderToDto(this IQueryable<Orders> orders)
        {
            return orders
                .Include(order => order.OrderProducts)
                    .ThenInclude(orderProduct => orderProduct.OrderFiles)
                .Select(o => new AdminImmoOrderDto
                {
                    OrderProducts = o.OrderProducts.MapOrderProductToDto(),
                    IsCompleted = o.OrderProducts.MapOrderProductToDto().All(od => od.IsCompleted == true)
                });
        }
    }

Но эта строка

IsCompleted = o.OrderProducts.MapOrderProductToDto().All(od => od.IsCompleted == true)

Выдает следующее исключение EF Core

.All(od => od.IsCompleted == True)' by 'NavigationExpandingExpressionVisitor' failed.
This may indicate either a bug or a limitation in EF Core.
See https://go.microsoft.com/fwlink/?linkid=2101433 for more detailed information.

И, если вы видите, что ссылка относится к оценке клиента и сервера

Это свойство IsCompleted предназначено для последующей фильтрации моих заказов. Я попытался переместить этот фрагмент кода в мой фильтр без успеха. Я также попытался сделать эту логику c перед сопоставлением моего DTO, используя код linq blo c вместо свойства в дочернем DTO (так как он не существует до того, как будет выполнено сопоставление).

Я также пытался явно форсировать оценку клиента, вызывая .ToList () или .AsEnumerable () перед вызовом .All ()

Мой вопрос: как я могу установить свойство IsCompleted для OrderDto в зависимости от свойства IsCompleted в дочернем элементе Dto OrderProductDto без выравнивания моего верхнего уровня IQueryable?

Спасибо.

Обновление 10/03

I ' Я пытался переместить это в мой фильтр Query Object, мне не нужно свойство, если я могу фильтровать дочерние элементы по порядку. К сожалению, ошибка все та же. См. Приведенный ниже код.

    public enum OrdersFilterBy
    {
        NoFilter = 0,
        IsCompleted = 1,
    }
    public static class AdminImmoOrderListDtoFilter
    {
        public static IQueryable<AdminImmoOrderDto> FilterOrdersBy(this IQueryable<AdminImmoOrderDto> orders, OrdersFilterBy filterBy)
        {
            switch(filterBy)
            {
                case OrdersFilterBy.NoFilter:
                    return orders;
                case OrdersFilterBy.IsCompleted:
                    return orders.Where(o => o.OrderProducts.AsEnumerable().All(op => op.IsCompleted == true));
                default:
                    throw new ArgumentOutOfRangeException
                        (nameof(filterBy), filterBy, null);
            }
        }
    }
}

Эта строка

return orders.Where(o => o.OrderProducts.AsEnumerable().All(op => op.IsCompleted == true));

С или без .AsEnumerable () для выравнивания дочерней коллекции результат одинаков. Та же ошибка, что и в предыдущем случае.

Обновление 10/03 Часть 2

Вот пример результата API, если он помогает лучше понять иерархию.

{
        "id": "1",
        "orderPicture": null,
        "saleType": 2,
        "clientName": "Toto",
        "clientPhone": "000000000",
        "fullAddress": "Toto road, 420",
        "smsCode": "XYZVDF",
        "creationDate": "2020-03-09T15:08:36.157",
        "visitDate": "2020-03-16T08:00:00",
        "orderProducts": [
            {
                "productTypeID": 1,
                "productName": "A sample product",
                "visitDate": "2020-03-16T08:00:00",
                "urgenceDate": null,
                "description": "7",
                "companyEmployeeNames": null,
                "isCompleted": false,
                "orderFiles": []
            }
        ]
    },
...