Puresharp ist eine Reihe von Funktionen für .NET 4.5.2+ / .NET Core 2.1 zur Verbesserung der Produktivität durch die Erstellung flexibler und effizienter Anwendungen.
Puresharp bietet hauptsächlich Architekturtools zum Aufbau der Grundlagen professioneller Anwendungen:
Dieses Framework ist in zwei Teile unterteilt:
IPuresharp ist ein Nuget-Paket zum Umschreiben von Assemblys (mit Mono.Cecil ), damit sie zur Laufzeit hochgradig anpassbar sind. IPuresharp fügt keine neue Bibliotheksreferenz zu Assemblys hinzu, sondern beinhaltet nur einen Post-Build-Prozess, um Assemblys direkt nach dem erfolgreichen Build automatisch neu zu schreiben.
Install-Package IPuresharp -Version 5.0.5
Es kann manuell über die Befehlszeile verwendet werden, um Assemblys von Drittanbietern zu verwalten
IPuresharp.exe "FullnameToAssembly.dll"
Puresharp ist ein Nuget-Paket, das verschiedene nützliche Funktionen für den Entwurf einer gesunden und produktiven Architektur bietet. Dieses Paket enthält auch die gesamte Artillerie, um die Elemente, die der IL-Autor IPuresharp mitbringt, problemlos zu bewältigen. Das Nuget-Paket fügt eine Bibliothek (Puresharp.dll) ohne weitere Abhängigkeiten hinzu.
Install-Package Puresharp -Version 5.0.5
Hinweis: Es wird empfohlen, das IPuresharp -Nuget-Paket in allen Projekten zu installieren und das Puresharp- Nuget-Paket nur in Projekten zu installieren, in denen Sie es ausdrücklich benötigen.
Der globale Arbeitsablauf des DI-Containers ähnelt dem anderer: Richten Sie eine Komposition ein, erstellen Sie einen Container und instanziieren Sie einige Komponenten aus dem Container.
Beispiel für zu konfigurierende Schnittstellen
public interface IA
{
}
public interface IB
{
}
public interface IC
{
}
Beispiel für Implementierungen zur Bindung an Schnittstellen
public class A : IA
{
public A(IB b, IC c)
{
}
}
public class B : IB
{
public B(IC c, int constant)
{
}
}
public class C : IC
{
}
Erstellen Sie eine Komposition
var _composition = new Composition();
Setup-Zusammensetzung für IA, IB, IC bzw. A, B, C
_composition.Setup<IA>(() => new A(Metadata<IB>.Value, Metadata<IC>.Value), Instantiation.Multiton);
_composition.Setup<IB>(() => new B(Metadata<IC>.Value, 28), Instantiation.Multiton);
_composition.Setup<IC>(() => new C(), Instantiation.Multiton);
Erstellen Sie einen Container aus der Kompositionseinrichtung
var _container = _composition.Materialize();
Instanziieren Sie ein IA-Modul aus dem Container
using (var _module = _container.Module<IA>())
{
var _ia = _module.Value;
}
Hinweis: Das Modul ist IDisposable und steuert den Lebenszyklus für alle Abhängigkeiten.
Wie wird der Lebenszyklus für Abhängigkeiten verwaltet? Wenn ein Modul in der Komposition eingerichtet wird, ist ein Instanziierungsmodus erforderlich. Dieser kann Singleton (eine einzelne Instanz mit einem Lebenszyklus in Bezug auf den Container), Multiton (eine neue Instanz für jedes Modul mit einem Lebenszyklus in Bezug auf das Modul selbst) oder Volatile (immer eine neue Instanz) sein mit Lebenszyklus im Zusammenhang mit dem Eigentümermodul). Container und Modul sind beide IDisposable, um erstellte Komponenten freizugeben.
Sollen meine Schnittstellen IDisposable implementieren, um sie an das Lebenszyklusmanagement anzupassen? Im Gegenteil, die Schnittstelle einer Komponente sollte niemals die IDisposable-Schnittstelle implementieren, die ein reines Infrastrukturproblem darstellt. Möglicherweise sind nur Implementierungen möglich. Der Container stellt sicher, dass die Implementierungen ordnungsgemäß entsorgt werden, wenn er die IDisposable-Schnittstelle implementiert.
Warum den Lambda-Ausdruck zum Konfigurieren von Komponenten anstelle klassischer generischer Parameter verwenden? Lambda-Ausdrücke bieten eine Möglichkeit, den Zielkonstruktor zu verwenden, anzugeben, wann Abhängigkeiten verwendet werden sollen, und Konstanten zu erfassen.
Wie ist die Abhängigkeit konfiguriert? Verwenden Sie einfach Metadata<T>.Value im Ausdruck, wenn Sie die Abhängigkeit vom Container wiederherstellen müssen.
Verhindert die Konstruktorinjektion die zyklische Referenz zwischen Komponenten? Nein, zyklische Referenzen sind eine Funktion. Wenn eine Instanz erstellt wird, ist dies nicht wirklich der Fall. Eine Lazy-Proxy-Instanz wird vorbereitet, um die Aufbewahrung ungenutzter Ressourcen zu minimieren und zyklische Referenzen zu ermöglichen.
In der Vorschau werden nur Konstruktoren zum Einrichten von Komponenten verwendet. Ist dies auf die Konstruktoreninjektion beschränkt? Nein, die Meinungsäußerung ist völlig offen. Sie können statische Methoden, Konstruktoren und Mitglieder einfügen und sogar verschiedene Stile mischen.
Arbeitsablauf:
Beispiel einer Schnittstelle
[AttributeUsage(AttributeTargets.Method)]
public class Read : Attribute
{
}
[AttributeUsage(AttributeTargets.Method)]
public class Operation : Attribute
{
}
public interface IService
{
[Operation]
void SaveComment(int id, string text);
[Read]
[Operation]
string GetComment(int id);
}
Beispiel für die Umsetzung
public class Service : IService
{
public void SaveComment(int id, string text)
{
}
public string GetComment(int id)
{
return null;
}
}
Angenommen, wir möchten alle schreibgeschützten Vorgänge protokollieren. Dazu müssen wir einen Pointcut definieren, der alle Methoden darstellt, bei denen es sich um schreibgeschützte Operationen handelt (wobei das Leseattribut und das Operationsattribut platziert sind).
public class ReadonlyOperation : Pointcut.And<Pointcut<Operation>, Pointcut<Read>>
{
}
Definieren Sie einen Hinweis , der vor dem Aufrufen von Methoden mit Trace.WriteLine protokolliert werden soll
public class Log : IAdvice
{
private MethodBase m_Method;
public Log(MethodBase method)
{
this.m_Method = method;
}
public void Instance<T>(T instance)
{
}
public void Argument<T>(ref T value)
{
}
public void Begin()
{
Trace.WriteLine(this.m_Method);
}
public void Await(MethodInfo method, Task task)
{
}
public void Await<T>(MethodInfo method, Task<T> task)
{
}
public void Continue()
{
}
public void Throw(ref Exception exception)
{
}
public void Throw<T>(ref Exception exception, ref T value)
{
}
public void Return()
{
}
public void Return<T>(ref T value)
{
}
public void Dispose()
{
}
}
Definieren Sie einen Aspekt , der Protokollhinweise verwendet
public class Logging : Aspect
{
override public IEnumerable<Advisor> Manage(MethodBase method)
{
yield return Advice
.For(method)
.Around(() => new Log(method));
}
}
Instanziieren Sie Aspect und verknüpfen Sie es mit unserem ReadonlyOperation Pointcut
var _logging = new Logging();
_logging.Weave<ReadonlyOperation>();
Herzlichen Glückwunsch, der Protokollierungsaspekt ist jetzt in alle schreibgeschützten Betriebsverträge eingefügt.
Hier eine Reihe von Beispielen, um verschiedene Möglichkeiten der Erstellung und Beratung zu zeigen.
public class Logging : Aspect
{
override public IEnumerable<Advisor> Manage(MethodBase method)
{
//Use classic interceptor to create an 'Around' advisor (place break points in interceptor methods to test interception).
yield return Advice
.For(method)
.Around(() => new Interceptor());
//Use linq expression to generate a 'Before' advisor.
yield return Advice
.For(method)
.Before(invocation =>
{
return Expression.Call
(
Metadata.Method(() => Console.WriteLine(Metadata<string>.Value)),
Expression.Constant($"Expression : { method.Name }")
);
});
//Use linq expression to generate a 'Before' advisor.
yield return Advice
.For(method)
.Before
(
Expression.Call
(
Metadata.Method(() => Console.WriteLine(Metadata<string>.Value)),
Expression.Constant($"Expression2 : { method.Name }")
)
);
//Use ILGeneration from reflection emit API to generate a 'Before' advisor.
yield return Advice
.For(method)
.Before(advice =>
{
advice.Emit(OpCodes.Ldstr, $"ILGenerator : { method.Name }");
advice.Emit(OpCodes.Call, Metadata.Method(() => Console.WriteLine(Metadata<string>.Value)));
});
//Use simple Action to generate a 'Before' advisor.
yield return Advice
.For(method)
.Before(() => Console.WriteLine($"Action : { method.Name }"));
//Use an expression to generate an 'After-Returning-Value' Advisor
yield return Advice
.For(method)
.After()
.Returning()
.Value(_Execution =>
{
return Expression.Call
(
Metadata.Method(() => Console.WriteLine(Metadata<string>.Value)),
Expression.Call
(
Metadata.Method(() => string.Concat(Metadata<string>.Value, Metadata<string>.Value)),
Expression.Constant("Returned Value : "),
Expression.Call(_Execution.Return, Metadata<object>.Method(_Object => _Object.ToString()))
)
);
});
//Validate an email parameter value.
yield return Advice
.For(method)
.Parameter<EmailAddressAttribute>()
.Validate((_Parameter, _Attribute, _Value) =>
{
if (_Value == null) { throw new ArgumentNullException(_Parameter.Name); }
try { new MailAddress(_Value.ToString()); }
catch (Exception exception) { throw new ArgumentException(_Parameter.Name, exception); }
});
}
}
Kann ich mehrere Aspekte in denselben Pointcut integrieren? Ja, achten Sie nur auf die Webreihenfolge.
Wie kann ich einen Aspekt aus einem Pointcut entfernen? In Aspect ist eine Release -Methode definiert, um Aspect aus Pointcut zu entfernen.
Sind Attribute erforderlich, um Pointcut zu definieren? Nein, Pointcut kann definiert werden, indem es direkt von Pointcut erbt und die abstrakte Methode Match implementiert, die eine MethodBase als einzelnes Argument verwendet und einen booleschen Wert zurückgibt, um anzugeben, ob sich eine Methode im Pointcut- Bereich befindet.
Warum muss ich IPuresharp verwenden? Das Abfangen basiert auf IPuresharp. Tatsächlich fügt IPuresharp eine Build-Aktion hinzu, um CIL neu zu schreiben, um die Assembly „architektenfreundlich“ zu machen, indem transparente und versteckte Funktionen eingefügt werden, um die vollständige Ausführungskontrolle zur Laufzeit zu gewährleisten.
Kann ich den Konstruktor abfangen? Wenn ja, wie setze ich es um? Das Abfangen von Konstruktoren wird unterstützt und wie eine andere Methode behandelt, bei der der Typ als erstes Argument und void als Rückgabetyp deklariert wird.
Werden generische Typen und Methoden unterstützt? Generische Typen und Methoden werden von der Injektion vollständig unterstützt.
Kann ich asynchrone Methoden abfangen? Asynchrone Methoden werden durch Injektion vollständig unterstützt und bieten eine Möglichkeit, alle asynchronen Schritte abzufangen.