IL Weaving در AOP به معنای اتصال Aspects تعریف شده، پس از کامپایل برنامه به فایلهای باینری نهایی است. اینکار با ویرایش اسمبلیها در سطح IL یا کد میانی صورت میگیرد. بنابراین در این حالت دیگر یک محصور کننده و پروکسی، در این بین جهت مزین سازی اشیاء، در زمان اجرای برنامه تشکیل نمیشود. بلکه فراخوانی Aspects به معنای فراخوانی واقعی قطعه کدهایی است که به اسمبلیهای برنامه پس از کامپایل آنها تزریق شدهاند.
در دنیای دات نت، ابزارهای چندی امکان انجام IL Weaving را فراهم ساختهاند که تعدادی از آنها به قرار ذیل هستند:
- PostSharp
- LOOM.NET
- Wicca
و ...
در بین اینها، PostSharp معروفترین فریم ورک AOP بوده و در ادامه از آن استفاده خواهیم کرد.
پیشنیاز ادامه بحث
ابتدا یک پروژه کنسول جدید را آغاز کرده و سپس در خط فرمان پاور شل نوگت در VS.NET دستور ذیل را اجرا کنید:
به این ترتیب ارجاعی به PostSharp به پروژه جاری اضافه خواهد شد. البته حجم آن نسبتا بالا است؛ نزدیک به 20 مگ به همراه ابزارهای تزریق کد همراه با آن. مجوز استفاده از آن نیز تجاری و مدت دار است.
مراحل ایجاد یک Aspect برای پروسه IL Code Weaving
ابتدا یک کلاس پایه مشتق شده از کلاسی ویژه موجود در یکی از فریم ورکهای AOP باید تعریف شود. مرحله بعد، کار اتصال این Aspect میباشد که توسط پردازشگر ثانویه IL Code Weaving انجام میشود.
در ادامه قصد داریم همان مثال LoggingInterceptor قسمت دوم این سری را با استفاده از IL Code Weaving پیاده سازی کنیم.
کدهای برنامه همانند قبل است. اما اینبار بجای استفاده از Interceptors، با ارث بری از کلاس OnMethodBoundaryAspect کتابخانه PostSharp شروع خواهیم کرد:
نیاز است این کلاس توسط ویژگی Serializable مزین شود تا توسط PostSharp قابل استفاده گردد. همانطور که ملاحظه میکنید، مراحل مختلف اجرای یک Aspcet در اینجا با override متدهای کلاس پایه OnMethodBoundaryAspect پیاده سازی شدهاند. این مراحل را پیشتر در زمان استفاده از Interceptors توسط try/finally/catch بررسی کرده بودیم.
اکنون اگر برنامه را اجرا کنیم، اتفاق خاصی رخ نداده و همان خروجی معمول متد DoSomething در کنسول نمایش داده خواهد شد. بنابراین در مرحله بعد نیاز است تا این Aspect را به کدهای برنامه متصل کنیم.
کلاس OnMethodBoundaryAspect در کتابخانه PostSharp، از کلاس MulticastAttribute مشتق میشود. بنابراین LoggingAspect ایی را که ایجاد کردهایم نیز میتوان به صورت یک ویژگی به متدهای مورد نظر خود افزود:
اکنون اگر برنامه را اجرا کنیم، با خروجی زیر مواجه خواهیم شد:
برای اینکه بتوان عملیات رخ داده را بهتر توضیح داد میتواند از یک دیکامپایلر مانند برنامه معروف Reflector استفاده کرد:
این کدی است که به صورت پویا توسط PostSharp به اسمبلی نهایی فایل اجرایی برنامه تزریق شده است.
خوب! این یک روش اتصال Aspects به برنامه است. اما اگر همانند Interceptors بخواهیم Aspect تعریف شده را سراسری اعمال کنیم چکار باید کرد (بدون نیاز به قرار دادن ویژگی بر روی تک تک متدها)؟
برای اینکار ابتدا نیاز است میدان عملکرد Aspect تعریف شده را توسط ویژگی MulticastAttributeUsage محدود کنیم تا برای مثال به خواص اعمال نشوند:
سپس فایل AssemblyInfo.cs استاندارد پروژه را گشوده و سطر زیر را به آن اضافه کنید:
توسط AttributeTargetTypes میتوان اعمال این Aspect را به یک فضای نام خاص نیز محدود کرد.
مزیت روش IL Code Weaving نسبت به Interceptors، کارآیی و سرعت بالاتر است. از این جهت که کدهایی که قرار است اجرا شوند، پیشتر در اسمبلی برنامه قرار گرفتهاند و نیازی نیست تا در زمان اجرا، کدی به برنامه به صورت پویا تزریق گردد.
در دنیای دات نت، ابزارهای چندی امکان انجام IL Weaving را فراهم ساختهاند که تعدادی از آنها به قرار ذیل هستند:
- PostSharp
- LOOM.NET
- Wicca
و ...
در بین اینها، PostSharp معروفترین فریم ورک AOP بوده و در ادامه از آن استفاده خواهیم کرد.
پیشنیاز ادامه بحث
ابتدا یک پروژه کنسول جدید را آغاز کرده و سپس در خط فرمان پاور شل نوگت در VS.NET دستور ذیل را اجرا کنید:
PM> Install-Package PostSharp
مراحل ایجاد یک Aspect برای پروسه IL Code Weaving
ابتدا یک کلاس پایه مشتق شده از کلاسی ویژه موجود در یکی از فریم ورکهای AOP باید تعریف شود. مرحله بعد، کار اتصال این Aspect میباشد که توسط پردازشگر ثانویه IL Code Weaving انجام میشود.
در ادامه قصد داریم همان مثال LoggingInterceptor قسمت دوم این سری را با استفاده از IL Code Weaving پیاده سازی کنیم.
using System; namespace AOP03 { public class MyType { public void DoSomething(string data, int i) { Console.WriteLine("DoSomething({0}, {1});", data, i); } } class Program { static void Main(string[] args) { new MyType().DoSomething("Test", 1); } } }
using System; using PostSharp.Aspects; namespace AOP03 { [Serializable] public class LoggingAspect : OnMethodBoundaryAspect { public override void OnEntry(MethodExecutionArgs args) { Console.WriteLine("On Entry"); } public override void OnExit(MethodExecutionArgs args) { Console.WriteLine("On Exit"); } public override void OnSuccess(MethodExecutionArgs args) { Console.WriteLine("On Success"); } public override void OnException(MethodExecutionArgs args) { Console.WriteLine("On Exception"); } } }
اکنون اگر برنامه را اجرا کنیم، اتفاق خاصی رخ نداده و همان خروجی معمول متد DoSomething در کنسول نمایش داده خواهد شد. بنابراین در مرحله بعد نیاز است تا این Aspect را به کدهای برنامه متصل کنیم.
کلاس OnMethodBoundaryAspect در کتابخانه PostSharp، از کلاس MulticastAttribute مشتق میشود. بنابراین LoggingAspect ایی را که ایجاد کردهایم نیز میتوان به صورت یک ویژگی به متدهای مورد نظر خود افزود:
public class MyType { [LoggingAspect] public void DoSomething(string data, int i) { Console.WriteLine("DoSomething({0}, {1});", data, i); } }
On Entry DoSomething(Test, 1); On Success On Exit
public void DoSomething(string data, int i) { <>z__Aspects.a0.OnEntry(null); try { Console.WriteLine("DoSomething({0}, {1});", data, i); <>z__Aspects.a0.OnSuccess(null); } catch (Exception) { <>z__Aspects.a0.OnException(null); throw; } finally { <>z__Aspects.a0.OnExit(null); } }
خوب! این یک روش اتصال Aspects به برنامه است. اما اگر همانند Interceptors بخواهیم Aspect تعریف شده را سراسری اعمال کنیم چکار باید کرد (بدون نیاز به قرار دادن ویژگی بر روی تک تک متدها)؟
برای اینکار ابتدا نیاز است میدان عملکرد Aspect تعریف شده را توسط ویژگی MulticastAttributeUsage محدود کنیم تا برای مثال به خواص اعمال نشوند:
[Serializable] [MulticastAttributeUsage(MulticastTargets.Method, TargetMemberAttributes = MulticastAttributes.Instance)] public class LoggingAspect : OnMethodBoundaryAspect
[assembly: LoggingAspect(AttributeTargetTypes = "AOP03.*")]
مزیت روش IL Code Weaving نسبت به Interceptors، کارآیی و سرعت بالاتر است. از این جهت که کدهایی که قرار است اجرا شوند، پیشتر در اسمبلی برنامه قرار گرفتهاند و نیازی نیست تا در زمان اجرا، کدی به برنامه به صورت پویا تزریق گردد.