StackOverflowException вызвано запросом linq - PullRequest
2 голосов
/ 26 октября 2008

edit # 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;

    }

Мои бизнес-объекты отличаются от классов linq2sql, поэтому я заполняю их новым select.

Произошло необработанное исключение типа «System.StackOverflowException» в mscorlib.dll


edit # 1: Я нашел виновника, это следующий метод, он вызывает метод GetCountries (), который в свою очередь снова вызывает GetSubRegions (), ad nauseam:

    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))
               };
    }

Здесь, возможно, придется подумать о чем-то еще :) Вот что происходит, когда вы думаете, что в ОО-мышлении слишком много кофе

Ответы [ 3 ]

2 голосов
/ 26 октября 2008

Хай! Я думаю, что ваши модели рекурсивно вызывают метод непреднамеренно, что приводит к переполнению стека. Как, например, ваш объект Subregion пытается получить объекты Country, которые, в свою очередь, должны получить Subregions.

Во всяком случае, всегда помогает проверить стек в исключении StackOverflow. Если вы видите свойство, к которому обращаются снова и снова, скорее всего, потому что вы делаете что-то вроде этого:

открытый объект MyProperty {set {MyProperty = value; }}

Легче обнаружить ситуации, подобные вашей, когда метод A вызывает метод B, который вызывает метод A, потому что вы можете видеть, что одни и те же методы появляются два или более раз в стеке вызовов.

1 голос
/ 26 октября 2008

Чтобы ответить на ваш отредактированный вопрос, а именно: «связывание объектов друг с другом без запуска бесконечных циклов»:

Предполагая, что у вас есть какая-то связь, в которой обеим сторонам нужно знать о другой ... овладеть всеми соответствующими сущностями в обеих сторонах, а затем связать их вместе, вместо того, чтобы пытаться получить одну сторону автоматически получить другой. Или просто заставьте одну сторону выбрать другую, а затем исправьте оставшуюся. Так что в вашем случае варианты будут:

Вариант 1:

  • Выбрать все страны (оставив субрегионы пустыми)
  • Выбрать все субрегионы (с указанием пустых стран)
  • Для каждого субрегиона просмотрите список стран и добавьте субрегион к стране, а страну к субрегиону

Вариант 2:

  • Выбрать все страны (оставив субрегионы пустыми)
  • Выбрать все субрегионы, установив субрегион. Страны через список стран, выбранный выше
  • Для каждого субрегиона пройдите все его страны и добавьте его в эту страну

(или поменять страну и субрегион)

Это в основном равнозначные ответы, они просто меняются, когда вы выполняете некоторые ссылки.

1 голос
/ 26 октября 2008

Проблема может быть в следующем: у стран есть субрегионы, а у субрегионов есть страны. Я не знаю, как вы реализуете ленивый список, но это может продолжать вызывать GetCountries, а затем GetSubRegions и так далее. Чтобы выяснить это, я бы запустил точки отладки и набор точек останова в заголовках методов GetCountries и GetSubRegions.

Я пробовал аналогичные паттерны с LinqToSql, но трудно заставить двунаправленную навигацию работать, не сильно влияя на производительность. Это одна из причин, по которой я сейчас использую NHibernate.

...