Ich habe eine Frage, und ich werde diese subjektive tag da ist das, was ich denke, es entwickelt sich, mehr eine Diskussion. Ich hoffe auf gute Ideen oder Denker. Ich entschuldige mich für die langatmige Frage, aber Sie müssen den Kontext kennen. im GrundeServiceContainer, IoC, und Einweg-Objekte
Die Frage ist:
- Wie geht man mit konkreten Typen in Bezug auf IoC Container? Insbesondere, wer ist für die Entsorgung verantwortlich, wenn sie entsorgt werden müssen, und wie wird dieses Wissen an den aufrufenden Code weitergegeben?
Müssen Sie IDisposable? Wenn nicht, ist dieser Code zukunftssicher, oder ist die Regel, dass Sie keine Einwegobjekte verwenden können? Wenn Sie IDisposable-Anforderungen für Schnittstellen und konkrete Typen erzwingen, um zukunftssicher zu sein, wessen Verantwortung sind Objekte, die im Rahmen von Konstruktoraufrufen eingespeist werden?
bearbeiten: Ich nahm die Antwort von @Chris Ballard, da es die nächsten zu dem Ansatz ist, die wir am Ende mit.
Grundsätzlich haben wir immer eine Art zurück, die wie folgt aussieht:
public interface IService<T> : IDisposable
where T: class
{
T Instance { get; }
Boolean Success { get; }
String FailureMessage { get; } // in case Success=false
}
Wir haben dann von einem Objekt der Umsetzung dieser Schnittstelle zurückzukehren beide .Resolve und .TryResolve, so dass das, was wir in der anrufenden Code zu erhalten ist immer der gleiche Typ.
Nun ist das Objekt, das diese Schnittstelle implementiert, IService<T>
IDisposable, und sollte immer entsorgt werden. Es ist nicht Sache des Programmierers, der einen Dienst auflöst, um zu entscheiden, ob das Objekt IService<T>
entsorgt werden soll oder nicht.
Jedoch, und dies ist der entscheidende Teil, ob die Dienstinstanz entsorgt werden soll oder nicht, wird dieses Wissen in das Objekt implementiert IService<T>
, also wenn es sich um einen werksseitigen Dienst handelt (dh jeder Aufruf von Resolve endet mit einer neuen Dienstinstanz), dann wird die Dienstinstanz entsorgt, wenn das Objekt IService<T>
entsorgt wird.
Dies hat es auch möglich gemacht, andere spezielle Bereiche, wie Pooling, zu unterstützen. Wir können jetzt sagen, dass wir mindestens 2 Dienstinstanzen benötigen, maximal 15 und typischerweise 5, was bedeutet, dass jeder Aufruf von .Resolve entweder eine Dienstinstanz aus einem Pool verfügbarer Objekte abruft oder eine neue erstellt. Wenn das Objekt IService<T>
, in dem der Pool-Dienst enthalten ist, entsorgt wird, wird die Dienstinstanz wieder in ihren Pool freigegeben.
Sicher, machte dies den gesamten Code sieht wie folgt aus:
using (var service = ServiceContainer.Global.Resolve<ISomeService>())
{
service.Instance.DoSomething();
}
aber es ist ein sauberer Ansatz, und es hat die gleiche Syntax unabhängig von der Art des Dienstes oder der konkreten Gegenstand in Gebrauch, so wählten wir, dass als eine akzeptable Lösung.
Original Frage folgt, für die Nachwelt
Langwierige Frage kommt hier:
Wir haben einen IoC-Container, die wir verwenden, und vor kurzem entdeckten wir, welche Beträge zu einem Problem.
In nicht-IoC-Code, wenn wir, sagen wir, eine Datei verwenden, wollte, haben wir eine Klasse wie folgt:
using (Stream stream = new FileStream(...))
{
...
}
Es war keine Frage, ob diese Klasse war etwas, das eine begrenzte gehalten Ressource oder nicht, da wir wussten, dass Dateien geschlossen werden mussten, und die Klasse selbst IDisposable implementiert. Die Regel ist einfach, dass jede Klasse, von der wir ein Objekt konstruieren, das IDisposable implementiert, entsorgt werden muss. Keine Fragen gefragt. Es ist nicht Sache des Benutzers dieser Klasse, zu entscheiden, ob der Aufruf von Dispose optional ist oder nicht.
Ok, also auf den ersten Schritt in Richtung der IoC-Container. Nehmen wir an, wir möchten nicht, dass der Code direkt mit der Datei kommuniziert, sondern stattdessen eine Ebene der Indirektion durchläuft. Lassen Sie uns diese Klasse einen BinaryDataProvider für dieses Beispiel nennen. Intern wird die Klasse mit einem Strom, der noch ein Einweg-Objekt ist, so dass der obige Code geändert werden würde:
using (BinaryDataProvider provider = new BinaryDataProvider(...))
{
...
}
Diese ändert sich nicht viel. Das Wissen, dass die Klasse IDisposable implementiert, ist immer noch da, keine Fragen, wir müssen Dispose aufrufen.
Aber nehmen wir an, dass wir Klassen haben, die Daten bereitstellen, die im Moment keine so begrenzten Ressourcen verwenden.
BinaryDataProvider provider = new BinaryDataProvider();
...
OK, so weit so gut, aber hier kommt das Fleisch der Frage:
Der obige Code könnte dann wie folgt geschrieben werden. Nehmen wir an, wir möchten einen IoC-Container verwenden, um diesen Anbieter zu injizieren, anstatt von einem bestimmten konkreten Typ abhängig zu sein.
würde der Code dann:
IBinaryDataProvider provider =
ServiceContainer.Global.Resolve<IBinaryDataProvider>();
...
Bitte beachte, dass ich annehmen, dass es eine unabhängige Schnittstelle zur Verfügung steht, dass wir das Objekt durch zugreifen können.
Mit der obigen Änderung, was ist, wenn wir später ein Objekt verwenden möchten, das wirklich entsorgt werden sollte? Keiner der vorhandenen Codes, die diese Schnittstelle auflösen, wird geschrieben, um das Objekt zu beseitigen. Was nun?
Die Art, wie wir es sehen, haben wir eine Lösung holen:
- Laufzeitkontrolle implementieren, die überprüft, wenn ein konkreter, die IDisposable implementiert registriert wird, erfordern, dass die Schnittstelle es durch auch Geräte ausgesetzt ist IDisposable.Dies ist keine gute Lösung
- Enfore eine Einschränkung für die Schnittstellen verwendet werden, müssen sie immer von IDisposable erben, um zukunftssichere
- Erzwingen Laufzeit zu sein, dass keine konkreten Typen IDisposable sein kann, da dies insbesondere nicht Wird der Code mit dem IoC-Container behandelt
- Lassen Sie es einfach dem Programmierer überlassen, ob das Objekt IDisposable implementiert und "das Richtige tut"?
- Gibt es noch andere?
Und was ist mit dem Injizieren von Objekten in Konstruktoren? Unser Container und einige der anderen Container, die wir untersucht haben, können einem Konstrukteur eines Betontyps ein neues Objekt in einen Parameter einfügen. Zum Beispiel, wenn unsere BinaryDataProvider
ein Objekt benötigen, das die ILogging
Schnittstelle implementiert, wenn wir IDispose- "Fähigkeit" auf diesen Objekten erzwingen, deren Verantwortung es ist, das Logging-Objekt zu entsorgen?
Was denkst du? Ich will Meinungen, gute und schlechte.
Nun, das klingt wie "immer ein IDisposable Objekt aus dem IoC-Container zurückgeben" und dann wäre die einfachste Lösung, um ein Kriterium für die konkreten Typen zu machen. Die Implementierung dieser Schnittstelle könnte natürlich leer bleiben, so dass es nicht viel Code-übergreifend gibt, nur eine Methode. –
Ja, ich nehme an, es gibt wenig Unterschied. Ich nehme an, das grundlegende Problem ist, dass Sie die Verfügbarkeit des Objekts mit dem Client-Code kommunizieren müssen, also denke ich, es gibt wenig Hoffnung;) – krosenvold
Ich würde gehen, keine Einweg-Objekte zu unterstützen. – krosenvold