Puresharp คือชุดคุณลักษณะสำหรับ .NET 4.5.2+ / .NET Core 2.1 เพื่อปรับปรุงประสิทธิภาพการทำงานโดยการสร้างแอปพลิเคชันที่ยืดหยุ่นและมีประสิทธิภาพ
Puresharp นำเสนอเครื่องมือทางสถาปัตยกรรมเป็นหลักเพื่อสร้างพื้นฐานของการใช้งานระดับมืออาชีพ :
กรอบนี้แบ่งออกเป็น 2 ส่วน:
IPuresharp เป็นแพ็คเกจ nuget ที่ออกแบบมาเพื่อเขียนแอสเซมบลีใหม่ (โดยใช้ Mono.Cecil ) เพื่อให้สามารถปรับแต่งได้สูงในขณะรันไทม์ IPuresharp จะไม่เพิ่มการอ้างอิงไลบรารีใหม่ให้กับแอสเซมบลี แต่จะรวมเฉพาะกระบวนการหลังสร้างเพื่อเขียนแอสเซมบลีใหม่โดยอัตโนมัติหลังจากสร้างสำเร็จ
Install-Package IPuresharp -Version 5.0.5
สามารถใช้ด้วยตนเองกับบรรทัดคำสั่งเพื่อจัดการแอสเซมบลีของบุคคลที่สาม
IPuresharp.exe "FullnameToAssembly.dll"
Puresharp เป็นแพ็คเกจ nuget ที่นำเสนอคุณสมบัติต่างๆ ที่เป็นประโยชน์สำหรับการออกแบบสถาปัตยกรรมที่ดีและมีประสิทธิภาพ แพ็คเกจนี้ยังรวมปืนใหญ่ทั้งหมดไว้ด้วยเพื่อให้ง่ายต่อการจัดการองค์ประกอบที่นำ IPuresharp นักเขียน IL มาใช้ แพ็คเกจ nuget เพิ่มไลบรารี (Puresharp.dll) โดยไม่มีการอ้างอิงอื่นใด
Install-Package Puresharp -Version 5.0.5
หมายเหตุ : ขอแนะนำให้ติดตั้งแพ็คเกจ IPuresharp nuget ในทุกโปรเจ็กต์ และติดตั้งแพ็คเกจ Puresharp nuget เฉพาะในโปรเจ็กต์ที่คุณต้องการอย่างชัดเจนเท่านั้น
ขั้นตอนการทำงานทั่วโลกของ DI Container นั้นคล้ายคลึงกับขั้นตอนอื่น ๆ ได้แก่ ตั้งค่าองค์ประกอบ สร้างคอนเทนเนอร์ และสร้างอินสแตนซ์จากคอนเทนเนอร์บางส่วน
ตัวอย่างอินเทอร์เฟซที่จะกำหนดค่า
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 lifecycle สำหรับการขึ้นต่อกันทั้งหมด
วงจรชีวิตที่ได้รับการจัดการสำหรับการขึ้นต่อกันเป็นอย่างไร เมื่อโมดูลถูกตั้งค่าเป็นองค์ประกอบ จำเป็นต้องมีโหมดการสร้างอินสแตนซ์และสามารถเป็น ซิงเกิลตัน (อินสแตนซ์เดียวที่มีวงจรการใช้งานที่เกี่ยวข้องกับคอนเทนเนอร์), มัลติตัน (อินสแตนซ์ใหม่สำหรับแต่ละโมดูลที่มีวงจรการใช้งานที่เกี่ยวข้องกับโมดูลเอง) หรือ ระเหย (เป็นอินสแตนซ์ใหม่เสมอ ที่มีวงจรชีวิตที่เกี่ยวข้องกับโมดูลเจ้าของ) คอนเทนเนอร์และโมดูลมีทั้ง IDisposable เพื่อปล่อยส่วนประกอบที่สร้างขึ้น
อินเทอร์เฟซของฉันใช้ IDisposable เพื่อให้ตรงกับการจัดการวงจรการใช้งานหรือไม่ ในทางตรงกันข้าม อินเทอร์เฟซของส่วนประกอบไม่ควรใช้อินเทอร์เฟซ IDisposable ซึ่งเป็นข้อกังวลด้านโครงสร้างพื้นฐานเพียงอย่างเดียว การใช้งานที่เป็นไปได้เท่านั้น คอนเทนเนอร์ทำให้แน่ใจว่าจะกำจัดการใช้งานอย่างถูกต้องเมื่อใช้อินเทอร์เฟซ IDisposable
เหตุใดจึงใช้ lambda expression เพื่อกำหนดค่าส่วนประกอบแทนพารามิเตอร์ทั่วไปแบบคลาสสิก นิพจน์ Lambda นำเสนอวิธีการกำหนดเป้าหมาย Constructor ที่จะใช้ ระบุว่าเมื่อใดควรใช้การขึ้นต่อกัน และบันทึกค่าคงที่
มีการกำหนดค่าการพึ่งพาอย่างไร? เพียงใช้ Metadata<T>.Value ในนิพจน์เมื่อคุณต้องการรับการพึ่งพากลับจากคอนเทนเนอร์
การฉีดคอนสตรัคเตอร์ป้องกันการอ้างอิงแบบวนระหว่างส่วนประกอบหรือไม่ ไม่ การอ้างอิงแบบวนเป็นคุณลักษณะ เมื่อสร้างอินสแตนซ์ มันไม่ได้เป็นเช่นนั้นจริงๆ อินสแตนซ์ Lazy Proxy ได้รับการจัดเตรียมเพื่อลดการเก็บรักษาทรัพยากรที่ไม่ได้ใช้และอนุญาตการอ้างอิงแบบวนรอบ
ในหน้าตัวอย่าง มีเพียงคอนสตรัคเตอร์เท่านั้นที่ใช้ในการตั้งค่าส่วนประกอบ มันจำกัดแค่คอนสตรัคเตอร์อินเจคชั่นหรือไม่ ไม่ สำนวนเปิดกว้างโดยสิ้นเชิง คุณสามารถฉีดเมธอดแบบสแตติก ตัวสร้าง สมาชิก และแม้กระทั่งผสมผสานสไตล์ที่แตกต่างกันได้
ขั้นตอนการทำงาน :
ตัวอย่างของอินเทอร์เฟซ
[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;
}
}
สมมติว่าเราต้องการบันทึกการดำเนินการแบบอ่านอย่างเดียวทั้งหมด เพื่อสิ่งนั้น เราต้องกำหนด Pointcut ที่แสดงถึงวิธีการทั้งหมดที่เป็นการดำเนินการแบบอ่านอย่างเดียว (โดยที่แอตทริบิวต์ Read และคุณลักษณะ Operation ถูกวางไว้)
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>();
ขอแสดงความยินดี ขณะนี้ Log Aspect ได้ถูกแทรกลงในสัญญาการดำเนินการแบบอ่านอย่างเดียวทั้งหมดแล้ว
นี่คือชุดตัวอย่างเพื่อให้เห็นวิธีการสร้างและที่ปรึกษาที่แตกต่างกัน
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); }
});
}
}
ฉันสามารถรวมหลายแง่มุมให้เป็น Pointcut เดียวกันได้หรือไม่ ใช่ครับ แค่ระวังเรื่องการสั่งทอด้วย
ฉันจะลบ Aspect ออกจาก Pointcut ได้อย่างไร มีวิธี การ Release ที่กำหนดไว้ใน Aspect เพื่อกำจัด Aspect ออกจาก Pointcut
จำเป็นต้องมีแอตทริบิวต์เพื่อกำหนด Pointcut หรือไม่ ไม่ Pointcut สามารถกำหนดได้โดยการสืบทอดโดยตรงจาก Pointcut และใช้วิธีนามธรรม Match ที่ใช้ MethodBase เป็นอาร์กิวเมนต์เดียวและส่งคืนค่าบูลีนเพื่อระบุว่าวิธีการนั้นอยู่ในขอบเขต Pointcut หรือไม่
เหตุใดฉันจึงต้องใช้ IPuresharp? การสกัดกั้นขึ้นอยู่กับ IPuresharp แท้จริงแล้ว IPuresharp เพิ่มการดำเนินการ build เพื่อเขียน CIL ใหม่เพื่อให้แอสเซมบลี "เป็นมิตรกับสถาปนิก" โดยการฉีดคุณสมบัติโปร่งใสและคุณสมบัติที่ซ่อนอยู่เพื่อให้การควบคุมการดำเนินการเต็มรูปแบบที่รันไทม์
ฉันสามารถสกัดกั้นคอนสตรัคเตอร์ได้หรือไม่? ถ้าใช่ ฉันจะนำไปใช้ได้อย่างไร? รองรับการสกัดกั้นคอนสตรัคเตอร์และได้รับการปฏิบัติเหมือนวิธีอื่นที่มีการประกาศประเภทเป็นอาร์กิวเมนต์แรกและเป็นโมฆะสำหรับประเภทส่งคืน
รองรับประเภทและวิธีการทั่วไปหรือไม่ ประเภทและวิธีการทั่วไปได้รับการสนับสนุนอย่างเต็มที่โดยการฉีด
ฉันสามารถสกัดกั้นวิธี async ได้หรือไม่ วิธีการแบบอะซิงโครนัสได้รับการสนับสนุนอย่างเต็มที่โดยการฉีด และเสนอวิธีการสกัดกั้นแต่ละขั้นตอนแบบอะซิงโครนัส