2016-10-17 4 views
0

Ich habe eine Funktion, die eine "Person" erstellt und zurückgibt. Die "Person" hat eine "Ehepartner" -Eigenschaft, die natürlich eine andere "Person" ist. Dies verursacht eine endlose Rekursion, da die "Person", die erzeugt wird, immer eine "neue" ist, wenn die Funktion aufgerufen wird. Gibt es eine Möglichkeit, die gleiche Funktion (wie unten gezeigt) zu verwenden, ohne eine Endlosschleife zu verursachen?Vermeiden Sie Zirkelverweis für Klassen

public PersonModel Load(int personID) 
{ 
    PersonModel person = new PersonModel(); 
    using (SqlConnection conn = new SqlConnection()) 
    { 
     conn.ConnectionString = Helpers.ConnectionDB; 
     SqlCommand command = new SqlCommand(); 
     command.Connection = conn; 
     command.CommandType = CommandType.StoredProcedure; 
     command.CommandText = "LoadPerson"; 
     command.Parameters.Add(new SqlParameter("@PersonID", personID)); 
     conn.Open(); 
     SqlDataReader reader = command.ExecuteReader(); 
     if (reader.Read()) 
     { 
      person.PersonID = int.Parse(reader["PersonID"].ToString()); 
      person.FirstName = reader["FirstName"].ToString(); 
      person.LastName = reader["LastName"].ToString(); 
      person.MiddleName = reader["MiddleName"].ToString(); 
      person.Age = reader["Age"] != DBNull.Value ? int.Parse(reader["Age"].ToString()) : (int?)null; 
      person.SpouseID = reader["SpouseID"] != DBNull.Value ? int.Parse(reader["SpouseID"].ToString()) : (int?)null; 
      if (person.SpouseID != null && person.Spouse == null) 
      { 
       person.Spouse = this.Load(person.SpouseID.Value); 
      } 
     } 
     conn.Close(); 
    } 
    return person; 
} 
+0

Es kann besser sein, ein rekursives CTE zu schreiben, um das Social-Graph zu laden, anstatt es in Anwendungscode zu tun, es wird weniger Netzwerk-Chatty sein und nur eine einzige Befehlsausführung erfordern. – Dai

+0

In einem Fall wie diesem, in dem A B und B auf A verweist, hätte ich nur die EhepartnerID und nicht ein Person-Objekt. Wenn Sie Details zum Ehepartner erhalten möchten, erstellen Sie einen Ehepartner aus der ID. Andernfalls treten Probleme auf, wenn Sie versuchen, diese Art von Beziehung zu serialisieren. – DeanOC

+0

Dai - Ich weiß nicht, worauf Sie sich beziehen. Schlägst du vor, dass ich den Ehepartner (wenn es einen gibt) innerhalb des Sprocs selbst lade und dann dieses Objekt von der sekundären Tabelle, die ich zurückgebe, erstelle? Ich hatte gehofft, das zu vermeiden, da ich eine ähnliche Funktion haben würde, die ALLE "Personen" zurückgibt (im Gegensatz zu nur einer bestimmten). –

Antwort

2

ein Dictionary<Int32,PersonModel> Verwenden Spur von geladenen Einheiten zu halten:

public PersonModel Load(Dictionary<Int32,PersonModel> dict, int personId) { 

    PersonModel ret; 
    if(dict.TryGetValue(personId, out ret)) return ret; 

    // load from database here, but do not call recursively just yet 
    ret = new PersonModel() { ... }; 

    dict.Add(ret.PersonId, ret); 

    ret.Spouse = this.Load(dict, person.SpouseId.Value); 
} 
+0

Das scheint so zu sein, danke. –

0

Ich kann verschiedene Optionen von ein paar denken:

  1. einen optionalen Parameter zu Load() hinzufügen, dass Sie, um anzuzeigen, sollte nicht versuchen, den Ehepartner zu laden. Dies ist ein bisschen wie ein Hack, aber sehr einfach.

    public PersonModel Load(int personID, bool loadSpouse = true) 
    { 
        ... 
        if (loadSpouse && person.SpouseID != null && person.Spouse == null) 
        { 
         person.Spouse = this.Load(person.SpouseID.Value, false); 
        } 
        ... 
    
        return person; 
    } 
    
  2. die Eigenschaften des Ehegatten Wählen Sie im gespeicherte Prozedur durch eine linke äußere Verknüpfung verwenden. Dies würde besser funktionieren und wenn Sie jemals alle Mitarbeiter auswählen wollten, wäre dies viel effizienter, da es nur ein Datenbankzugriff wäre. Sie würden Code wie folgt in der if-Anweisung fügen Sie den Ehepartner Objekt zu bauen:

    person.Spouse = new PersonModel() 
    person.Spouse.FirstName = reader["SpouseFirstName"].ToString(); 
    ... 
    
0

Nicht genau gemäß der OPs Frage, aber da ich wegen der Titel hier gelandet ist, könnte es von Vorteil sein, das Hinzufügen einer allgemeinen antworte darauf.

In meinem Szenario musste ich also eine Eigenschaft in einem ganzen Diagramm ändern, indem ich alle Untereigenschaften auf eine beliebige Tiefe einstellte.

Um mir nicht durchgeführt, eine Instanz zweimal, um sicherzustellen, (so dass also StackOverflowException), ich bin ein HashSet<T> entlang jeden Anruf vorbei, so dass wir, die durchgeführten Artikel überspringen:

//The executed parameter needn't ever be provided externally. 
static void SetState(IObject obj, State state, HashSet<IObject> executed = null) 
{ 
    if(executed == null) executed = new HashSet<IObject>(/* provide comparer if needed*/); 
    if(!executed.Add(obj)) return; 

    //safe to continue walking graph 
    obj.State = state; 
    var props = obj.GetObjectPropertyValues().OfType<IObject>(); 
    foreach (var prop in props) 
    SetState(obj, state, executed); 
} 

Hinweis , die Elemente müssen richtige Gleichheit und GetHashCode Funktionen implementieren, oder ein Vergleichsargument sollte an den Konstruktor HashSet übergeben werden.

Verwandte Themen