Es gibt diesen Rahmen, den ich mitgestalte. Es gibt einige allgemeine Aufgaben, die mit einigen allgemeinen Komponenten ausgeführt werden sollten: Protokollieren, Zwischenspeichern und Auslösen von Ereignissen im Besonderen.
Ich bin mir nicht sicher, ob es besser ist, die Abhängigkeitsinjektion zu verwenden und alle diese Komponenten für jeden Dienst einzuführen (z. B. als Eigenschaften), oder ob ich über jede Methode meiner Dienste Metadaten platzieren und diese allgemeinen Aufgaben mithilfe von Interception ausführen soll ?
Hier ist ein Beispiel für beide:
Injektion:
public class MyService
{
public ILoggingService Logger { get; set; }
public IEventBroker EventBroker { get; set; }
public ICacheService Cache { get; set; }
public void DoSomething()
{
Logger.Log(myMessage);
EventBroker.Publish<EventType>();
Cache.Add(myObject);
}
}
und hier ist die andere Version:
Abfangen:
public class MyService
{
[Log("My message")]
[PublishEvent(typeof(EventType))]
public void DoSomething()
{
}
}
Hier sind meine Fragen:
- Welche Lösung eignet sich am besten für ein kompliziertes Framework?
- Welche Möglichkeiten habe ich, um mit den internen Werten einer Methode zu interagieren, wenn das Abfangen erfolgreich ist (z. B. mit dem Cache-Service)? Kann ich dieses Verhalten auf andere Weise als mit Attributen implementieren?
- Oder gibt es vielleicht andere Lösungen, um das Problem zu lösen?
c#
dependency-injection
Beatles1692
quelle
quelle
Antworten:
Querschnittsthemen wie Protokollierung, Zwischenspeicherung usw. sind keine Abhängigkeiten und sollten daher nicht in Dienste eingebunden werden. Während die meisten Leute dann nach einem vollständigen Interleaving-AOP-Framework zu greifen scheinen, gibt es dafür ein schönes Entwurfsmuster: Decorator .
Lassen Sie im obigen Beispiel MyService die IMyService-Schnittstelle implementieren:
Dies hält die MyService-Klasse vollständig frei von übergreifenden Bedenken und folgt somit dem Single Responsibility Principle (SRP).
Um die Protokollierung anzuwenden, können Sie einen Protokollierungs-Decorator hinzufügen:
Sie können Caching, Metering, Eventing usw. auf die gleiche Weise implementieren. Jeder Decorator macht genau eins, folgt also auch dem SRP, und Sie können sie auf beliebig komplexe Weise zusammenstellen. Z.B
quelle
Für eine Handvoll Services finde ich Marks Antwort gut: Sie müssen keine neuen Abhängigkeiten von Drittanbietern lernen oder einführen und folgen weiterhin guten SOLID-Grundsätzen.
Für eine große Anzahl von Diensten würde ich ein AOP-Tool wie PostSharp oder Castle DynamicProxy empfehlen. PostSharp hat eine kostenlose (wie in Bier) Version, und sie haben kürzlich das PostSharp Toolkit for Diagnostics veröffentlicht (kostenlos wie in Bier UND Sprache), mit dem Sie sofort einige Protokollierungsfunktionen nutzen können.
quelle
Ich finde das Design eines Frameworks weitgehend orthogonal zu dieser Frage - Sie sollten sich zuerst auf die Schnittstelle Ihres Frameworks konzentrieren und als Hintergrund den mentalen Prozess überlegen, wie jemand es tatsächlich konsumieren könnte. Sie möchten nicht etwas tun, das verhindert , dass es auf clevere Weise verwendet wird, sondern es sollte nur eine Eingabe in Ihr Framework-Design sein. einer unter vielen.
quelle
Ich habe mich oft mit diesem Problem auseinandergesetzt und denke, dass ich eine einfache Lösung gefunden habe.
Anfangs habe ich mich für das Dekorationsmuster entschieden und jede Methode manuell implementiert. Wenn Sie Hunderte von Methoden haben, wird dies sehr mühsam.
Ich habe mich dann für PostSharp entschieden, aber mir gefiel die Idee nicht, eine ganze Bibliothek einzubinden, um etwas zu tun, das ich mit (viel) einfachem Code erreichen konnte.
Ich bin dann die Route des transparenten Proxys gegangen, was Spaß machte, aber beinhaltete, IL zur Laufzeit dynamisch zu emittieren und nicht etwas zu sein, was ich in einer Produktionsumgebung tun möchte.
Ich habe vor kurzem beschlossen, T4-Vorlagen zu verwenden, um das Dekorationsmuster zur Entwurfszeit automatisch zu implementieren. Es hat sich herausgestellt, dass T4-Vorlagen tatsächlich sehr schwer zu bearbeiten sind, und ich musste dies schnell erledigen, sodass ich den folgenden Code erstellte. Es ist schnell und schmutzig (und es unterstützt keine Eigenschaften), aber hoffentlich findet es jemand nützlich.
Hier ist der Code:
Hier ist ein Beispiel:
Erstellen Sie dann eine Klasse mit dem Namen LoggingTestAdapter, die ITestAdapter implementiert. Lassen Sie Visual Studio alle Methoden automatisch implementieren und führen Sie sie dann über den obigen Code aus. Du solltest dann so etwas haben:
Dies ist es mit dem unterstützenden Code:
quelle