В последнее время я выполняю рефакторинг некоторых наших неуклюжих и не работающих запросов LINQ, когда нахожу проблемы в нашем проекте. Вот тот, который бросает меня за петлю. Должен признаться, я разработчик Android, катапультировавшийся в C # со структурой сущностей, поэтому я не совсем понимаю, что здесь происходит. Тем не менее эти запросы выглядят для меня почти одинаково (добавьте сюда подзапрос и добавьте новое соединение). Первый метод вернется без ошибок:
public virtual IQueryable<ScratchGameDetailsModel> GetScratchGameDetails(string reconGUID)
{
var lotteryReconHeader = _lotteryReconHeaderRepository.GetAll().AsEnumerable(); // Changed on Nov 7,2017
var lotteryReconDetails = _lotteryReconDetailRepository.GetAll().Where(s => s.ReconGUID == reconGUID).AsEnumerable();
var lotteryGames = _lotteryGameRepository.GetAll().AsEnumerable();
var lotteryActivations = _lotteryActivationRepository.GetAll().AsEnumerable();
var response = (from ltrd in lotteryReconDetails
join lg in lotteryGames on ltrd.GameGUID equals lg.GameGUID
join la in lotteryActivations on lg.GameGUID equals la.GameGUID into LotteryActivations_join
from la in LotteryActivations_join.DefaultIfEmpty()
join ltrh in lotteryReconHeader on ltrd.ReconGUID equals ltrh.ReconGUID // Added on Nov 7,2017
select new ScratchGameDetailsModel()
{
ReconGUID = ltrd.ReconGUID,
GameGUID = ltrd.GameGUID,
GameNumber = lg.GameNumber ?? Constants.Zero,
GameName = lg.GameName ?? String.Empty,
GameUPC = lg.GameUPC ?? string.Empty,
GameBin = lg.GameBin ?? Constants.Zero,
StartCount = ltrd.StartCount,
EndCount = ltrd.EndCount,
CashierSales = ltrd.CashierSales,
ExpectedStartCount = ltrd.ExpectedStartCount,
QtyOnRoll = lg.QtyOnRoll ?? Constants.Zero,
BookNumber = (la == null || la.BookNumber == null ? string.Empty : la.BookNumber.Split(new char[] { '-' }, 2).ElementAt(1).TrimStart('-')),
ReconTimeStamp = ltrh.ReconTimeStamp // Added on Nov 7,2017
});
return response.AsQueryable();
}
Вы, вероятно, можете сказать, почему мне не нравится этот метод, 3 из этих бесчисленных списков являются полными дампами таблиц БД, к которым позже присоединяются. Когда БД растет, время запроса этих переменных увеличивается. Полная трата ресурсов, если вы спросите меня. Я уже исправил некоторые методы, которые заняли более 5 минут для заполнения данных в другой таблице из-за объявления больших переменных, и теперь они выполняются за <1 мс. </p>
Вот метод замены, который должен возвращать все то же самое, за исключением того, что теперь есть вложенный запрос. Он также больше не устанавливает бесчисленные списки и просто обрабатывает запрос, используя контекст БД. Это выдает ошибку, заявляющую:
Метод 'System.String TrimStart (Char [])' поддерживается только в LINQ to Entities, если в качестве аргументов не указаны символы усечения.
public virtual IQueryable<ScratchGameDetailsModel> GetScratchGameDetailsNew(string reconGUID)
{
var newResponse = (from ltrd in _context.LotteryReconDetail
join lg in _context.LotteryGame on ltrd.GameGUID equals lg.GameGUID
join la in _context.LotteryActivation
on new
{
lg.GameGUID,
BookNumber =
((from la in _context.LotteryActivation
where la.GameGUID == lg.GameGUID
orderby la.row_id descending
select new { la.BookNumber }).FirstOrDefault().BookNumber)
}
equals new { la.GameGUID, la.BookNumber } into la_join
from la in la_join.DefaultIfEmpty()
join ltrh in _context.LotteryReconHeader on ltrd.ReconGUID equals ltrh.ReconGUID
where ltrd.ReconGUID == reconGUID
select new ScratchGameDetailsModel()
{
ReconGUID = ltrd.ReconGUID,
GameGUID = ltrd.GameGUID,
GameNumber = lg.GameNumber ?? Constants.Zero,
GameName = lg.GameName ?? String.Empty,
GameUPC = lg.GameUPC ?? string.Empty,
GameBin = lg.GameBin ?? Constants.Zero,
StartCount = ltrd.StartCount,
EndCount = ltrd.EndCount,
CashierSales = ltrd.CashierSales,
ExpectedStartCount = ltrd.ExpectedStartCount,
QtyOnRoll = lg.QtyOnRoll ?? Constants.Zero,
BookNumber = (la == null || la.BookNumber == null ? string.Empty : la.BookNumber.Split(new char[] { '-' }, 2).ElementAt(1).TrimStart('-')),
ReconTimeStamp = ltrh.ReconTimeStamp
});
return newResponse.AsQueryable();
}
При использовании моего метода, если я использую те же операции (например, установка переменных) и использую их для запроса, я получаю «Ссылка на объект не установлена на экземпляр объекта». Эврика! Но почему? Этот же необработанный запрос SQL не имеет проблем, и мне бы очень хотелось узнать, в чем отличие LINQ при использовании контекста БД по сравнению с контекстом запроса / перечисления.
Я также изменил эти объявления переменных и использовал AsQueryable () вместо AsEnumerable (), чтобы весь список не выводился, а использовался как контекст БД в запросе. Если я не ошибаюсь, это заметная разница между перечислимым и запрашиваемым.
Это было решено, как указал @TheGeneral. Назначение вызова было обновлено для обработки усечения значения по мере необходимости.
BookNumber = (la == null || la.BookNumber == null ? string.Empty : la.BookNumber),
Затем обновил вызывающий метод для обработки обрезки
var scratchGameDetails = _lotteryReconService.GetScratchGameDetailsNew(reconGUID).ToList().Select(s => new
{
s.ReconGUID,
s.GameGUID,
s.GameNumber,
s.GameName,
s.GameUPC,
s.GameBin,
s.StartCount,
s.EndCount,
s.CashierSales,
s.ExpectedStartCount,
RangeStart = gameStartCount,
RangeEnd = useBaseZero ? (s.QtyOnRoll - 1) : s.QtyOnRoll,
BookNumber = (s.BookNumber == string.Empty ? string.Empty : s.BookNumber.Split(new char[] { '-' }, 2).ElementAt(1).TrimStart('-')),
s.ReconTimeStamp
}).OrderByDescending(x => x.ReconTimeStamp).Distinct();