Gerne, und sorry an haarrrgh für die "feindliche Übernahme" des Threads.
Die CommandBus Sache ist natürlich nicht auf meinem Mist gewachsen, aber ich habe sie auf meinen Abenteuern im Bereich CQRS aufgegabelt und finde es eine super Idee, auch unabhängig von CQRS, d.h. als eleganter Ersatz für den typischen Servicelayer.
Mich würde allerdings schon interessieren, was das für eine Anwendung ist, die solche wahnwitzig großen Mengen an Objekten im Speicher hält.
Hi,
Ich habe mal ein etwas komplexeres Beispiel zusammengebastelt, siehe Anhang. Der erwähnte Reflection-lastige Teil befindet sich in Messaging\Registration des CommandBusApp.Core Projekts, insbesondere in MessageHandlerDiscovery. Siehe dazu auch die readme.txt.
Ansonsten zeigt der Code noch einige Möglichkeiten, wie man Systeme erweiterbar halten kann (Open-Closed Principle) sowie die Anwendung eines DI-Containers (hier: Ninject). Es ist übrigens zu erwähnen, dass das Core-Projekt nichts von einem DI-Container weiß.
Achtung: das ist kein Produktionscode, sondern einfach aus der Hüfte geschossen.
Grüße,
Andre
int.MaxValue ist 2147483647, also 2,14 Milliarden.
Es geht ja nicht um Flusskontrolle. Aber wenn FooLoader.Load null zurückgibt, was sagt es dir über die Ursache, warum das Laden fehlgeschlagen ist? Zugriffsrechte? Fehlendes Verzeichnis? Kosmische Strahlung?
Das bedeutet ja nicht, dass man nicht mal kurz vor dem Aufruf von FooLoader.Load checken kann, ob die Datei besteht etc.
Noch einen Kommentar hierzu:
internal class FooSaver { internal bool Save(Foo foo, string targetPath) { // tatsächlich speichern und return true wenn fertig ohne fehler } } internal class FooLoader { internal Foo Load(string sourcePath) { // tatsächliches laden, null wenn nicht möglich } }
Beide Methoden sollten meiner Meinung nach eine Exception werfen, wenn etwas schiefgeht, nicht null oder false. Sonst muss die aufrufende Methode die Rückgabewerte kontrollieren usw. Das ist mit Exceptions deutlich übersichtlicher.
Ich habe mir mittlerweile angeeignet, dass Argumente und Rückgabewerte standardmäßig nie null sein können. Die wenigen Ausnahmen, welche idealerweise auf private Methoden beschränkt sein sollten, müssen entsprechend eindeutig dokumentiert sein.
Grüße,
Andre
An sich finde ich die Verwendung von Streams bei so etwas eine gute Sache, eben weil sie so abstrakt sind. Eine Überladung mit Pfaden kann man immer noch anbieten. Wenn du das nicht möchtest - wie wärs mit so etwas:
interface IStreamProvider {
Stream OpenRead(string name);
Stream OpenWrite(string name);
void Close(Stream stream);
}
class FileStreamProvider : IStreamProvider {
public Stream OpenRead(string name){ return File.OpenRead(name); }
public Stream OpenWrite(string name){ return File.OpenRead(name); }
public void Close(Stream stream){ stream.Close(); }
}
class MemoryStreamProvider : IStreamProvider {
private MemoryStream stream;
public MemoryStreamProvider(MemoryStream stream){
this.stream = stream;
}
public MemoryStreamProvider():this(new MemoryStream()){
}
public MemoryStream Stream{get {return stream;}}
public Stream OpenRead(string name){ return stream; }
public Stream OpenWrite(string name){ return stream; }
public void Close(Stream stream) { /* nichts schließen */ }
}
class FooManager {
private IStreamProvider streamProvider;
public FooManager(IStreamProvider streamProvider){
this.streamProvider = streamProvider;
}
public void Save(Foo foo, string path){
var stream = streamProvider.OpenWrite(path);
try {
new FooSaver().Save(foo, stream);
} finally {
streamProvider.Close(stream);
}
}
public Foo Load(string path){
var stream = streamProvider.OpenRead(path);
try {
return new FooSaver().Load(stream);
} finally {
streamProvider.Close(stream);
}
}
}
Dann hast du nach außen hin noch immer Pfadangaben, kannst aber für die Tests einen MemoryStreamPovider verwenden, um zu überprüfen, was geschrieben wird.
Grüße,
Andre
Hi,
Welches Load/Save willst du denn testen? Das vom Manager oder das von FooLoader/FooSaver.
Allgemein wäre es evtl. ganz nützlich, wenn du statt eines Dateinamens einen Stream verwendest, dann kanst du leicht auch besagten MemoryStream übergeben.
Nur dann habe ich den Fall nicht abgedeckt das die Datei wegen fehlenden Rechten nicht gespeichert werden konnte, der Benutzer wählt das Zielverzeichnis aus, und wenn er unter Vista/Seven "Programme" aus wählt muss ich das ja auch entsprechend handeln.
Auf jeden fall kann ich das, so denk ich, in zwei Teile Splitten.
Das Problem wird doch eher sein, dass die Methode, die die Datei schreibt, eine solche Exception gar nicht abhandeln kann sondern sie einfach nach oben durchreicht.
Grüße,
Andre
Vor allem ist das erste etwas sinnbefreit, da der Formatstring gar keinen Platzhalter mehr enthält ({0}), trotzdem aber ein Wert dafür übergeben wird. Dann kann man auch direkt ein normales String-Literal verwenden.