EF Core linq и условно включить, а затем включить проблему - PullRequest
0 голосов
/ 26 ноября 2018

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

_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();

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

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

_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()

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

1 Ответ

0 голосов
/ 26 ноября 2018

Техника, которую вы используете, это не просто явная загрузка (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))
...