У меня проблема с получением результата при попытке получить объекты с несколькими уровнями. Вот что я пытаюсь сделать примерно:
_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()
он перестает работать.





Используемая вами техника - это не совсем явная загрузка (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))
Большое Вам спасибо. это намного лучше, чем то, что я делал.
Гладкая реализация. Прекрасно работает.
Это здорово, но почему это не встроено в ядро EF?
Возможно ли, чтобы это условие работало как условие .Where(x => x.something != thisthing)?
@DouglasGaskell Нет, это просто компактный способ условно включить связанные данные, когда строительство запрос. Следовательно, условия могут быть основаны только на некоторых параметрах, которые можно оценить во время вызова методов.
вы можете использовать мой метод расширения.
var filteredTransactions = _transactionRepository.GetAll()
.Where(x => x.CreationTime.Ticks <= input.FetchingTikes)
.IncludeIf(input.Filter.IsNotNullOrWhitespace(), t => t.DestinationAccountFk.Merchant, t => t.SourceAccountFk.UserFk)
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;
}
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 в массиве параметров должен быть в порядке. Я думаю, что другие расширения чрезмерны
Как насчет пустого чека?
вам не нужно проверять, является ли enumerable пустым, для цикла foreach требуется только ненулевое перечислимое