Puresharp は、柔軟で効率的なアプリケーションを作成することで生産性を向上させる、.NET 4.5.2+ / .NET Core 2.1 の機能セットです。
Puresharp は主に、プロフェッショナル アプリケーションの基本を構築するためのアーキテクチャ ツールを提供します。
このフレームワークは 2 つの部分に分かれています。
IPuresharp は、アセンブリを ( Mono.Cecilを使用して) 書き換えて、実行時に高度にカスタマイズできるようにすることに特化した nuget パッケージです。 IPuresharp はアセンブリに新しいライブラリ参照を追加しませんが、ビルドが成功した直後にアセンブリを自動的に書き換えるビルド後のプロセスのみを組み込みます。
Install-Package IPuresharp -Version 5.0.5
コマンドラインで手動で使用してサードパーティのアセンブリを管理できます
IPuresharp.exe "FullnameToAssembly.dll"
Puresharp は、健全で生産的なアーキテクチャの設計に役立つさまざまな機能を提供する nuget パッケージです。このパッケージには、IL ライター IPuresharp をもたらす要素を簡単に処理するためのすべての大砲も含まれています。 nuget パッケージは、他の依存関係なしでライブラリ (Puresharp.dll) を追加します。
Install-Package Puresharp -Version 5.0.5
注: IPuresharp nuget パッケージをすべてのプロジェクトにインストールし、 Puresharp nuget パッケージを明示的に必要とするプロジェクトにのみインストールすることをお勧めします。
DI コンテナのグローバル ワークフローは他のものと似ています。コンポジションをセットアップし、コンテナを作成し、コンテナからいくつかのコンポーネントをインスタンス化します。
設定するインターフェースの例
public interface IA
{
}
public interface IB
{
}
public interface IC
{
}
インターフェースにバインドする実装例
public class A : IA
{
public A(IB b, IC c)
{
}
}
public class B : IB
{
public B(IC c, int constant)
{
}
}
public class C : IC
{
}
コンポジションを作成する
var _composition = new Composition();
IA、IB、ICの構成をそれぞれ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);
構成設定からコンテナを作成する
var _container = _composition.Materialize();
コンテナからIAのモジュールをインスタンス化する
using (var _module = _container.Module<IA>())
{
var _ia = _module.Value;
}
注: モジュールは IDisposable であり、すべての依存関係のライフサイクルを制御します。
依存関係のライフサイクルはどのように管理されますか?モジュールを構成にセットアップする場合、インスタンス化モードが必要で、シングルトン(コンテナに関連するライフサイクルを持つ単一のインスタンス)、マルチトン(モジュール自体に関連するライフサイクルを持つ各モジュールの新しいインスタンス)、または揮発性(常に新しいインスタンス) のいずれかになります。オーナーモジュールに関連するライフサイクルを含む)。コンテナとモジュールは両方とも、作成されたコンポーネントをリリースするための IDisposable です。
ライフサイクル管理に合わせてインターフェイスに IDisposable を実装する必要がありますか?逆に、コンポーネントのインターフェイスは、純粋にインフラストラクチャの問題である IDisposable インターフェイスを決して実装すべきではありません。実現できるのは実装だけです。コンテナーは、IDisposable インターフェイスを実装するときに実装を適切に破棄します。
従来のジェネリック パラメーターではなく、ラムダ式を使用してコンポーネントを構成するのはなぜですか?ラムダ式は、使用するコンストラクターをターゲットにし、依存関係をいつ使用するかを指定し、定数をキャプチャする方法を提供します。
依存関係はどのように構成されますか?コンテナーから依存関係を取得する必要がある場合は、式でMetadata<T>.Valueを使用するだけです。
コンストラクター注入はコンポーネント間の循環参照を防止しますか?いいえ、循環参照は機能です。インスタンスが作成されるとき、実際にはそうではありません。未使用のリソースの保持を最小限に抑え、循環参照を許可するために、遅延プロキシ インスタンスが準備されます。
プレビューでは、コンポーネントのセットアップにコンストラクターのみが使用されますが、コンストラクター インジェクションに限定されますか?いいえ、表現は完全にオープンです。静的メソッド、コンストラクター、メンバーを挿入したり、異なるスタイルを混合したりすることもできます。
ワークフロー:
インターフェースの例
[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);
}
導入例
public class Service : IService
{
public void SaveComment(int id, string text)
{
}
public string GetComment(int id)
{
return null;
}
}
すべての読み取り専用操作をログに記録したいとします。そのためには、読み取り専用操作 (Read 属性と Operation 属性が配置される場所) であるすべてのメソッドを表すPointcutを定義する必要があります。
public class ReadonlyOperation : Pointcut.And<Pointcut<Operation>, Pointcut<Read>>
{
}
メソッド呼び出し時の例として、Trace.WriteLine の前に記録するアドバイスを定義します。
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()
{
}
}
ログアドバイスを使用するアスペクトを定義する
public class Logging : Aspect
{
override public IEnumerable<Advisor> Manage(MethodBase method)
{
yield return Advice
.For(method)
.Around(() => new Log(method));
}
}
アスペクトをインスタンス化し、それを ReadonlyOperationポイントカットに織り込みます
var _logging = new Logging();
_logging.Weave<ReadonlyOperation>();
おめでとうございます。ロギング アスペクトがすべての読み取り専用操作コントラクトに挿入されました。
ここでは、さまざまな作成方法とアドバイスを確認するためのサンプルのセットを示します。
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); }
});
}
}
複数のアスペクトを同じポイントカットに織り込むことはできますか?はい、織りの順序に注意してください。
ポイントカットからアスペクトを削除するにはどうすればよいですか? Aspectには、 PointcutからAspectを削除するReleaseメソッドが定義されています。
ポイントカットを定義するには属性が必要ですか?いいえ、 Pointcut は、 Pointcutから直接継承し、 MethodBase を1 つの引数として受け取り、メソッドがPointcutスコープ内にあるかどうかを示すブール値を返す抽象メソッドMatchを実装することで定義できます。
なぜ IPuresharp を使用しなければならないのですか?インターセプトは IPuresharp に基づいています。実際、IPuresharp は CIL を書き換えるビルド アクションを追加し、透明機能と非表示機能を注入することでアセンブリを「アーキテクト フレンドリー」にし、実行時に完全な実行制御を付与します。
コンストラクターをインターセプトできますか? 「はい」の場合、どのように実装すればよいですか?コンストラクターのインターセプトがサポートされており、最初の引数として型を宣言し、戻り値の型として void を使用する別のメソッドのように扱われます。
ジェネリック型とメソッドはサポートされていますか?ジェネリック型とメソッドはインジェクションによって完全にサポートされています。
非同期メソッドをインターセプトできますか?非同期メソッドはインジェクションによって完全にサポートされており、各非同期ステップをインターセプトする方法を提供します。