linqクエリが原因のStackOverflowException
-
04-07-2019 - |
質問
編集#2:質問は途中で解決しました。下を見てください
フォローアップの質問として、誰かが私が下でやろうとしていること(つまり、無限ループをトリガーせずにオブジェクトを相互にリンクすること)を解決する非侵入的な方法を知っていますか?
asp.net-mvc Webアプリケーションを作成しようとすると、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」タイプの未処理の例外が発生しました
edit#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を使用している理由の1つです。
他のヒント
はい!モデルが意図せずにメソッドを再帰的に呼び出しているため、スタックオーバーフローが発生していると思います。たとえば、SubregionオブジェクトがCountryオブジェクトを取得しようとしていますが、CountryオブジェクトはSubregionを取得する必要があります。
とにかく、常にStackOverflow例外のスタックをチェックするのに役立ちます。何度もアクセスされるプロパティが表示される場合、おそらく次のようなことをしているためです:
public object MyProperty {set {MyProperty = value; }}
コールスタックで同じメソッドが2回以上表示されるため、メソッドAがメソッドAを呼び出すメソッドBを呼び出すような状況を簡単に見つけることができます。
編集した質問に答えるには、次のようにします。「無限ループを引き起こさずにオブジェクトを互いにリンクする」:
両側が相手について知る必要がある何らかの関係があると仮定します...片側を取得しようとするのではなく、両側のすべての関連エンティティを取得し、それらをリンクします他方を自動的に取得します。または、一方側に他方をフェッチさせてから、残りの側を修正します。あなたの場合、オプションは次のようになります:
オプション1:
- すべての国を取得する(サブリージョンを空白のままにする)
- すべてのサブリージョンを取得します(国を空白のままにします)
- 各サブリージョンについて、国のリストを調べて、サブリージョンを国に、国をサブリージョンに追加します
オプション2:
- すべての国を取得する(サブリージョンを空白のままにする)
- すべてのサブリージョンを取得し、上記で取得した国リストからSubregion.Countriesを設定します
- 各サブリージョンについて、すべての国を調べて、その国に追加します
(または逆の国とサブ地域)
これらは基本的に同等の回答であり、リンクを行うと変更されるだけです。