Fragensteller
Zirkuläre Refrenzen - Problem trotz IsReference=true

Frage
-
hallo!
Mein Dienst soll Objekte übertragen, die von der Sache her folgendem Beispiel ähneln:
[DataContract] public class Person { public Person Urahn { get; set; } // Startpunkt der Ahnenlinie public Person Grossvater { get; set; } public Person Vater { get; set; } public List<Person> Kinder { get; set; } }
Es handelt sich also um eine Klasse, die durch ihre Eigenschaften mehrere zirkuläre Refrenzen hat. Und genau deswegen gab es hier bei der Ausführung zunächst Probleme. Es wurde angemahnt, dass "Person" wegen der zirkulären Referenzen nicht serialisiert werden könne, solange es nicht als IsReference=true markiert ist.
Also hab ich dies getan:
[DataContract(IsReference=true)] public class Person { ... }
Auf diese Weise funktionierte auch erstmal alles gut. Person konnte wunderbar serialisiert und deserialisiert werden.
Ein Problem tritt nur dann auf, wenn der ObjectGraph, den Person abbildet, sehr tief ist. Das heißt, wenn eine Ahnenlinie von mehreren Tausend Personen dargestellt wird.
In diesem Fall gibts in den Trace Logs den Vermerk, dass die maxItemsInObjectGraph (Standardwert 65536) zu klein seien.
Gut erhöhe ich also sowohl in der Server- als auch in der Client-Config diesen Wert (bspw. auf 100000): Ergebnis ist eine StackOverflowException , wenn der Client die GetPerson()-Methode des Servers aufruft...Meine Frage also:
Kann mir jemand sagen, warum das Ganze bei einem relativ flachen ObjectGraph gut funktioniert, bei einem tiefen allerdings nicht?
Das hört sich, finde ich, grundsätzlich danach an, dass irgendwelche Quotas zu knapp bemessen sind. Die Logs spucken aber nichts hilfreiches mehr aus, und ich hab auch testweise mal alles mögliche hoch gesetzt.Benutze übrigens basicHttpBinding.
Die Möglichkeit mit zirkulären Refrenzen umzugehen, die hier von Sowmy Srinivasan vorgeschlagen wird, führt zum selben Ergebnis wie IsReference=true.
Gruß
MartinDienstag, 21. September 2010 05:12
Alle Antworten
-
Hallo Martin,
Wenn ich das richtig sehe, wird die StackOverflowException nicht von Deinem Service, sondern von der Runtime (mscorlib) geworfen. Die Antwort ist daher: Der Objektgraph ist einfach zu tief. Da auch die CLR das Halteproblem noch nicht gelöst hat, wird die Rekursion nach einem bestimmten Sentinel-Wert angehalten. Beim Standardwert maxItemsInObjectGraph hat man 65.536 (!) Objekte im Graphen, theoretisch könntest Du den Wert auf Int32.MaxValue (2.147.483.647) erhöhen, aber da macht die Runtime längst nicht mehr mit, ganz zu schweigen über die in diesem Szenario für maxBufferSize, maxReceivedMessageSize und readerQuotas maxDepth benötigten Werte.
Ich glaube, Du wirst die Architektur Deiner Anwendung überdenken müssen.
Gruß
MarcelDienstag, 21. September 2010 19:50 -
Hallo Marcel,
der Ablauf bis zur Exception ist folgender:
- Client ruft Methode GetPersons() des Servers auf
- Server erzeugt Person-Objekt (auch problemlos ein sehr tiefes)
- Person-Objekt wird von der Methode zurück gegeben
- Exception tritt auf
Der Server kann das Objekt also erstellen. Erst nach dem zurückgeben, also in dem Moment, in dem die Serialisierung stattfindet, kommt es zu der Exception. Genauer gesagt in dem Moment, in dem die WriteObject-Methode des DataContractSerializers aufgerufen wird.
Ich hab die Kommunikation zwischen Client und Server testweise mal per .NET Remoting stattfinden lassen. Interessanterweise funktioniert da alles wunderbar?!
Liegt das in dem Fall daran, dass die Bindung zwischen Client und Server bei .NET Remoting enger ist? Der Client hat in diesem Fall ja eine Referenz auf das gemarshallte Objekt, das der Server bereit stellt.
Oder liegt das daran, dass der BinaryFormatter, den .NET Remoting nutzt, hier "besser" funktioniert als der DataContractSerializer von WCF?Schönen Gruß
Mittwoch, 22. September 2010 18:19 -
Hallo Martin,
Das Objekt wird dienstseitig tatsächlich erstellt! Und der Fehler ereignet sich während der Serialisierung des Objekts, genauer gesagt sobald in der Methode SerializeWithoutXsiType() OnHandleIsReference() aufgerufen wird, das seinerseits mscorlib.dll!String.Format() aufruft. Irgendwo und irgendwann nach diesem letzten xxxxx-ten Aufruf von String.Format wird die StackOverflowException geworfen. Ich kann mir nicht wirklich einen Reim darauf machen. Vielleicht solltest Du diesen Fehler auf connect.microsoft.com melden.
Schönen Gruß
MarcelMittwoch, 22. September 2010 20:49