EF Core linq и проблема условного включения, а затем включения

У меня проблема с получением результата при попытке получить объекты с несколькими уровнями. Вот что я пытаюсь сделать примерно:

_context.Investors.Where(s => s.Id == userId)
    .Include(c => c.Coins) //only want this if some kind of flag is given by the user.
    .ThenInclude(ct => ct.CoinType)
    .Include(c => c.Bricks) //only want this if some kind of flag is given by the user.

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

_context.Investors.Where(s => s.Id == userId)
    .Select(i => new
    {
        i,
        Bricks = (details & GapiInvestorFlags.Bricks) != GapiInvestorFlags.Bricks ? null : i.Bricks,
        Offers = (details & GapiInvestorFlags.Offers) != GapiInvestorFlags.Offers ? null : i.Offers,
        Coins = (details & GapiInvestorFlags.Coins) != GapiInvestorFlags.Coins ? null : i.Coins,
        CoinTransactions = (details & GapiInvestorFlags.CoinTransactions) != GapiInvestorFlags.CoinTransactions ? null : i.CoinTransactions,
        OfferTransactions = (details & GapiInvestorFlags.OfferTransactions) != GapiInvestorFlags.OfferTransactions ? null : i.OfferTransactions,
        BuyTransactions = (details & GapiInvestorFlags.BuyTransactions) != GapiInvestorFlags.BuyTransactions ? null : i.BuyTransactions,
        SellTransactions = (details & GapiInvestorFlags.SellTransactions) != GapiInvestorFlags.SellTransactions ? null : i.SellTransactions
    }).AsEnumerable()
    .Select(e => e.i).FirstOrDefault();

Это работает, за исключением того, что в разделе Coins также есть тип монеты, поэтому мне тоже нужно включить его. Но когда я добавляю свой код, весь раздел перестает работать.

Вот что я пробовал:

_context.Investors.Where(s => s.Id == userId)
    .Include(c => c.Coins)
    .ThenInclude(ct => ct.CoinType)
    .Select(i => new
    {
        i,
        Bricks = (details & GapiInvestorFlags.Bricks) != GapiInvestorFlags.Bricks ? null : i.Bricks,
        Offers = (details & GapiInvestorFlags.Offers) != GapiInvestorFlags.Offers ? null : i.Offers,
        Coins = (details & GapiInvestorFlags.Coins) != GapiInvestorFlags.Coins ? null : i.Coins.Select(c => new { c, c.CoinType }).ToList(),
        CoinTransactions = (details & GapiInvestorFlags.CoinTransactions) != GapiInvestorFlags.CoinTransactions ? null : i.CoinTransactions,
        OfferTransactions = (details & GapiInvestorFlags.OfferTransactions) != GapiInvestorFlags.OfferTransactions ? null : i.OfferTransactions,
        BuyTransactions = (details & GapiInvestorFlags.BuyTransactions) != GapiInvestorFlags.BuyTransactions ? null : i.BuyTransactions,
        SellTransactions = (details & GapiInvestorFlags.SellTransactions) != GapiInvestorFlags.SellTransactions ? null : i.SellTransactions
    }).AsEnumerable()
    .Select(e => e.i).FirstOrDefault();

Я действительно не могу сказать, почему это не работает.

В основном, когда я меняю:

i.Coins

К

i.Coins.Select(c => new { c, c.CoinType }).ToList()

он перестает работать.

По сути, я получаю много флагов, указывающий, следует ли включать части объекта. - похоже, что ваш запрос слишком много делает, подумайте о разных запросах для флагов
Fabio 26.11.2018 06:55
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
1
6 844
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Используемая вами техника - это не совсем явная загрузка (Include / ThenInclude), а трюк, основанный на проекции и исправлении свойств навигации EF Core, поэтому я не могу сказать, почему он перестает работать. EF Core по-прежнему обрабатывает прогнозы и включает по-другому, так что это может быть дефект в текущей обработке.

Реализовать условное включение на уровне корневого запроса относительно просто. Обратите внимание, что метод Include начинается с (определен для) IQueryable<TEntity>, и возвращаемый IIncludableQueryable<TEntity, TPreviousProperty>> также является IQueryable<TEntity>. Это означает, что вы можете сохранить переменную запроса IQueryable<T> и применять условные преобразования, аналогичные связанным операторам Where.

Чтобы упростить задачу, вы можете создать собственный вспомогательный метод расширения следующим образом:

public static IQueryable<T> If<T>(
    this IQueryable<T> source,
    bool condition,
    Func<IQueryable<T>, IQueryable<T>> transform
)
{ 
    return condition? transform(source) : source;
}

и используйте это так:

_context.Investors.Where(s => s.Id == userId)
    .If(flagCoins, q => q.Include(e => e.Coins)
        .ThenInclude(e => e.CoinType))
    .If(flagBricks, q => q.Include(e => e.Bricks))

Если вам нужно что-то подобное для вложенных уровней (ThenInclude), то добавьте следующие 2 метода расширения:

public static IQueryable<T> If<T, P>(
    this IIncludableQueryable<T, P> source,
    bool condition,
    Func<IIncludableQueryable<T, P>, IQueryable<T>> transform
)
    where T : class
{
    return condition ? transform(source) : source;
}

public static IQueryable<T> If<T, P>(
    this IIncludableQueryable<T, IEnumerable<P>> source,
    bool condition,
    Func<IIncludableQueryable<T, IEnumerable<P>>, IQueryable<T>> transform
)
    where T : class
{
    return condition ? transform(source) : source;
}

что позволит вам использовать что-то вроде этого:

_context.Investors.Where(s => s.Id == userId)
    .If(flagCoins, q => q.Include(e => e.Coins)
        .If(flagCoinType, q2 => q2.ThenInclude(e => e.CoinType)))
    .If(flagBricks, q => q.Include(e => e.Bricks))

Большое Вам спасибо. это намного лучше, чем то, что я делал.

zawisza 26.11.2018 10:14

Гладкая реализация. Прекрасно работает.

David D 23.02.2019 13:54

Это здорово, но почему это не встроено в ядро ​​EF?

rory.ap 03.05.2019 17:51

Возможно ли, чтобы это условие работало как условие .Where(x => x.something != thisthing)?

Douglas Gaskell 26.05.2020 06:04

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

Ivan Stoev 26.05.2020 07:10

вы можете использовать мой метод расширения.

  1. С использованием:
 var filteredTransactions = _transactionRepository.GetAll()
                    .Where(x => x.CreationTime.Ticks <= input.FetchingTikes)
                    .IncludeIf(input.Filter.IsNotNullOrWhitespace(), t => t.DestinationAccountFk.Merchant, t => t.SourceAccountFk.UserFk)
  1. Расширение:
 public static IQueryable<TEntity> IncludeIf<TEntity>([NotNull] this 
   IQueryable<TEntity> source
                ,bool condition
                ,params Expression<Func<TEntity, object>>[] 
    navigationPropertyPaths)
                where TEntity : class
            {
                if (condition)
                {
                    if (navigationPropertyPaths.IsNotNullOrEmpty())
                    {
                        foreach (var navigationPropertyPath in 
   navigationPropertyPaths)
                        {
                            source = source.Include(navigationPropertyPath);
                        }
                    }
                }
                return source;
            }
  1. Другие расширения:
        public static bool IsNotNullOrEmpty<T>(this IEnumerable<T> list)
        {
            if (list.IsNotNull() && list.IsNotEmpty())
            {
                return true;
            }
            return false;
        }

        public static bool IsNotNull(this object obj)
        {
            if (obj != null)
            {
                return true;
            }
            return false;
        }

        public static bool IsNotEmpty<T>(this IEnumerable<T> list)
        {
            if (list.Count() > 0)
            {
                return true;
            }
            return false;
        }



       

хороший, но тебе действительно нужны пустые чеки? простой !=null в массиве параметров должен быть в порядке. Я думаю, что другие расширения чрезмерны

mkb 11.03.2021 15:11

Как насчет пустого чека?

Ahmad Hamzavi 14.03.2021 09:04

вам не нужно проверять, является ли enumerable пустым, для цикла foreach требуется только ненулевое перечислимое

mkb 14.03.2021 10:26

Другие вопросы по теме