Puresharp 是 .NET 4.5.2+ / .NET Core 2.1 的一組功能,旨在透過產生靈活且高效的應用程式來提高生產力。
Puresharp 主要提供建立專業應用程式基礎的架構工具:
此框架分為兩部分:
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 和 crontrol 生命週期。
如何管理依賴項的生命週期?當模組設定為組合時,需要實例化模式,可以是Singleton (生命週期與容器相關的單一實例)、 Multiton (每個模組的新實例,其生命週期與模組本身相關)或Volatile (總是新實例)與所有者模組相關的生命週期)。 Container 和 Module 都是 IDisposable 來釋放所建立的元件。
我的介面是否可以實作 IDisposable 以與生命週期管理相符?相反,組件的接口永遠不應該實作 IDisposable 接口,這純粹是基礎設施問題。只有實現才是可能的。容器在實作 IDisposable 介面時確保正確處置實作。
為什麼要使用 lambda 表達式來設定元件而不是經典的泛型參數? Lambda 表達式提供了一種方法來定位要使用的建構函式、指定何時使用相依性並擷取常數。
依賴如何配置?當您需要從容器取得依賴項時,只需在表達式中使用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;
}
}
假設我們要記錄所有唯讀操作。為此,我們必須定義一個切入點來表示所有唯讀操作的方法(其中放置讀取屬性和操作屬性)
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));
}
}
實例化Aspect並將其編織到我們的 ReadonlyOperation Pointcut
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中定義了一個Release方法,用於從Pointcut中刪除Aspect 。
定義切入點是否需要屬性?不, Pointcut可以透過直接繼承Pointcut並實作抽象方法Match來定義,該方法將MethodBase作為單一參數並傳回一個布林值以指示方法是否在Pointcut範圍內。
為什麼我必須使用 IPuresharp?攔截基於 IPuresharp。事實上,IPuresharp 增加了一個建置操作來重寫 CIL,透過注入透明和隱藏功能來在運行時授予完全執行控制,從而使組件「架構師友好」。
我可以攔截構造函數嗎?如果是,我該如何實施?支援建構函式攔截,並將其視為另一種方法,將型別宣告為第一個參數,並將傳回型別宣告為 void。
是否支援泛型類型和方法?注入完全支援泛型類型和方法。
我可以攔截非同步方法嗎?注入完全支援非同步方法,並提供攔截每個非同步步驟的方法。