2008-10-26 9 views
2

éditer # 2: Question résolue à mi-chemin. Comme question de suivi, est-ce que quelqu'un connaît une façon non intrusive de résoudre ce que j'essaie de faire ci-dessous (à savoir, relier les objets les uns aux autres sans déclencher des boucles infinies)? Je tente de créer une application Web asp.net-mvc et d'obtenir une exception StackOverFlowException. Un dispositif de commande déclenche la commande suivante:StackOverflowException causée par une requête linq

public ActionResult ShowCountry(int id) 
    { 
     Country country = _gameService.GetCountry(id); 
     return View(country); 
    } 

Le GameService il gère comme cela (WithCountryId est une extension):

public Country GetCountry(int id) 
    { 
     return _gameRepository.GetCountries().WithCountryId(id).SingleOrDefault(); 
    } 

Le GameRepository Il gère la manière suivante:

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

Les GetGames() méthode provoque l'exception StackOverflowException:

public IQueryable<Game> GetGames() 
    { 
     var games = from g in _db.Games     
       select new Game 
       { 
        Id = g.Id, 
        Name = g.Name 

       }; 
     return games; 

    } 

Mes objets métier sont différents des classes linq2sql, c'est pourquoi je les remplis avec un nouveau select.

Une exception non gérée du type 'System.StackOverflowException' est produite dans mscorlib.dll


modifier # 1: J'ai trouvé le coupable, il est la méthode suivante, il déclenche la méthode GetCountries() qui en retour déclenche les GetSubRegions() à nouveau, 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)) 
       }; 
    } 

aurait peut-être penser à quelque chose d'autre ici :) Voilà ce qui se passe quand vous pensez dans un état d'esprit de OO à cause de trop de café

+0

OMG, quand j'ai vu cette question sur la première page, j'ai pensé qu'il y avait un problème sur ce site. – OregonGhost

+2

+1 pour poser une question à propos de StackOverflowExceptions sur StackOverflow – kbrinley

Répondre

1

Le problème pourrait être le suivant: les pays ont des sous-régions et les sous-régions ont des pays. Je ne sais pas comment vous implémentez la liste paresseuse, mais cela pourrait continuer à appeler GetCountries, puis GetSubRegions et ainsi de suite. Pour le savoir, je lancerais les points d'arrêt du débogueur en set sur les en-têtes de la méthode GetCountries et GetSubRegions.

J'ai essayé des modèles similaires avec LinqToSql, mais il est difficile de faire fonctionner la navigation bidirectionnelle sans trop affecter les performances. C'est l'une des raisons pour lesquelles j'utilise NHibernate en ce moment.

2

Hai! Je pense que vos modèles appellent de manière récursive une méthode involontairement, ce qui entraîne un débordement de pile. Comme, par exemple, votre objet Subregion essaie d'obtenir des objets Country, qui à leur tour doivent obtenir des sous-régions.

De toute façon, il est toujours utile de vérifier la pile dans une exception StackOverflow. Si vous voyez une propriété en cours d'exécution encore et encore, c'est probablement parce que vous faites quelque chose comme ceci:

objet public MyProperty {set {MyProperty = value; }}

Il est plus facile de repérer des situations comme la vôtre, où la méthode A appelle la méthode B qui appelle la méthode A, car vous pouvez voir les mêmes méthodes apparaître deux fois ou plus dans la pile d'appels.

1

Pour répondre à votre question modifiée, à savoir: « lier des objets à l'autre sans déclencher des boucles infinies »:

En supposant que vous avez une sorte de relation où les deux parties doivent savoir sur l'autre ... get Tenez toutes les entités pertinentes des deux côtés, puis reliez-les ensemble, plutôt que d'essayer de faire en sorte que la récupération d'un côté récupère automatiquement l'autre. Ou faites juste un côté chercher l'autre, puis réparer le restant. Donc, dans votre cas, les options seraient:

Option 1:

  • Fetch tous les pays (en laissant Sous-régions en blanc)
  • Fetch tous les Sous-régions (en laissant en blanc les pays)
  • Pour chaque Sous-région, regardez dans la liste des pays et ajouter le Sous-région au pays et le pays à la Sous-région

Option 2:

  • Fetch tous les pays (en laissant en blanc) Sous-régions
  • Fetch toutes les sous- régions, la mise en Subregion.Countries via la liste des pays ci-dessus tiré par les cheveux
  • Pour chaque sous-région, passer par tous les pays et l'ajouter à ce pays

(ou arrière pays et sous-région)

Ils sont essentiellement des réponses equialent, il change juste quand vous faites partie de la liaison.

+0

Ce sera très lent si vous avez une grande base de données. Pouvez-vous le faire avec le chargement paresseux activé? – Paco

+0

Pourquoi cela serait-il particulièrement lent? Vous ne chargez que la même quantité de données que vous chargez, puis "reliez" les bits pertinents en mémoire. Il est évident qu'une hashtable rendra les choses plus rapides que l'approche O (N * M) plus simple décrite dans la première option, mais je voulais rester simple. –

+0

Parce que vous ne chargez pas la même quantité de données que vous pourriez avoir besoin de charger. Il existe une relation plusieurs à plusieurs entre les sous-régions et les pays, donc des jointures externes ou des requêtes multiples seront nécessaires. Ce sont chers. – Paco