StackOverflowException, вызванное запросом linq
-
04-07-2019 - |
Вопрос
изменить № 2: Вопрос решен наполовину.Смотри ниже
В качестве дополнительного вопроса: знает ли кто-нибудь ненавязчивый способ решить то, что я пытаюсь сделать ниже (а именно, связывание объектов друг с другом без запуска бесконечных циклов)?
Я пытаюсь создать веб-приложение asp.net-mvc и получаю исключение StackOverFlowException.Контроллер запускает следующую команду:
public ActionResult ShowCountry(int id)
{
Country country = _gameService.GetCountry(id);
return View(country);
}
GameService обрабатывает это следующим образом (WithCountryId — это расширение):
public Country GetCountry(int id)
{
return _gameRepository.GetCountries().WithCountryId(id).SingleOrDefault();
}
GameRepository обрабатывает это следующим образом:
public IQueryable<Country> GetCountries()
{
var countries = from c in _db.Countries
select new Country
{
Id = c.Id,
Name = c.Name,
ShortDescription = c.ShortDescription,
FlagImage = c.FlagImage,
Game = GetGames().Where(g => g.Id == c.GameId).SingleOrDefault(),
SubRegion = GetSubRegions().Where(sr => sr.Id == c.SubRegionId).SingleOrDefault(),
};
return countries;
}
Метод GetGames() вызывает исключение StackOverflowException:
public IQueryable<Game> GetGames()
{
var games = from g in _db.Games
select new Game
{
Id = g.Id,
Name = g.Name
};
return games;
}
Объекты My Business отличаются от классов linq2sql, поэтому я заполняю их новыми.
В mscorlib.dll произошло необработанное исключение типа «System.StackOverflowException».
редактировать №1: Я нашел виновника, это следующий метод, он запускает метод GetCountries(), который в свою очередь снова запускает GetSubRegions(), до тошноты:
public IQueryable<SubRegion> GetSubRegions()
{
return from sr in _db.SubRegions
select new SubRegion
{
Id = sr.Id,
Name = sr.Name,
ShortDescription = sr.ShortDescription,
Game = GetGames().Where(g => g.Id == sr.GameId).SingleOrDefault(),
Region = GetRegions().Where(r => r.Id == sr.RegionId).SingleOrDefault(),
Countries = new LazyList<Country>(GetCountries().Where(c => c.SubRegion.Id == sr.Id))
};
}
Возможно, здесь придется подумать о чем-то еще :) Вот что происходит, когда вы думаете объектно-ориентированно из-за слишком большого количества кофе.
Решение
Проблема может заключаться в следующем: у стран есть субрегионы, а у субрегионов есть страны. Я не знаю, как вы реализуете ленивый список, но это может продолжать вызывать GetCountries, а затем GetSubRegions и так далее. Чтобы выяснить это, я бы запустил отладчик и установил точки останова в заголовках методов GetCountries и GetSubRegions.
Я пробовал похожие шаблоны с LinqToSql, но трудно заставить работать двунаправленную навигацию, не сильно влияя на производительность. Это одна из причин, по которой я сейчас использую NHibernate. Р>
Другие советы
Хай!Я думаю, что ваши модели непреднамеренно рекурсивно вызывают метод, что приводит к переполнению стека.Например, ваш объект Subregion пытается получить объекты Country, которые, в свою очередь, должны получить Subregions.
В любом случае всегда полезно проверить стек в исключении StackOverflow.Если вы видите, что к свойству обращаются снова и снова, скорее всего, это потому, что вы делаете что-то вроде этого:
общественный объект MyProperty {set {MyProperty = значение;}}
Легче обнаружить ситуации, подобные вашей, когда метод A вызывает метод B, который вызывает метод A, потому что вы можете видеть, что одни и те же методы появляются в стеке вызовов два или более раз.
Чтобы ответить на ваш отредактированный вопрос, а именно:«связывание объектов друг с другом без запуска бесконечных циклов»:
Предположим, у вас есть какие-то отношения, в которых обе стороны должны знать друг о друге...получить все соответствующие объекты с обеих сторон, а затем связать их вместе, вместо того, чтобы пытаться заставить выборку одной стороны автоматически получать другую.Или просто сделай один сторону, возьмите другую, а затем исправьте оставшуюся.Итак, в вашем случае варианты будут такими:
Опция 1:
- Выбрать все страны (субрегионы оставив пустыми).
- Получить все субрегионы (оставив страны пустыми).
- Для каждого субрегиона просмотрите список стран и добавьте субрегион к стране и страну к субрегиону.
Вариант 2:
- Выбрать все страны (субрегионы оставив пустыми).
- Получите все субрегионы, установив Subregion.Countries через список стран, полученный выше.
- Для каждого субрегиона просмотрите все входящие в него страны и добавьте его в эту страну.
(Или поменяйте местами страну и субрегион)
По сути, это эквивалентные ответы, просто они меняются, когда вы делаете некоторые ссылки.