۱۳۸۸/۱۰/۰۲

آشنايي با M.A.F - قسمت دوم


قسمت قبل بيشتر آشنايي با يك سري از اصطلاحات مرتبط با فريم ورك MAF بود و همچنين نحوه‌ي كلي استفاده از آن. در اين قسمت يك مثال ساده را با آن پياده سازي خواهيم كرد و فرض قسمت دوم بر اين است كه افزونه‌ي Visual Studio Pipeline Builder را نيز نصب كرده‌ايد.

يك نكته پيش از شروع:
- اگر افزونه‌ي Visual Studio Pipeline Builder پس از نصب به منوي Tools اضافه نشده است، يك پوشه‌ي جديد را به نام Addins در مسير Documents\Visual Studio 2008 ايجاد كرده و سپس فايل‌هاي آن‌را در اين مسير كپي كنيد.

ساختار اوليه يك پروژه MAF

- پروژ‌ه‌هايي كه از MAF استفاده مي‌كنند، نياز به ارجاعاتي به دو اسمبلي استاندارد System.AddIn.dll و System.AddIn.Contract.dll دارند (مطابق شكل زير):



- ساختار آغازين يك پروژه MAF از سه پروژه تشكيل مي‌شود كه توسط افزونه‌ي Visual Studio Pipeline Builder به 7 پروژه بسط خواهد يافت.
اين سه پروژه استاندارد آغازين شامل موارد زير هستند:



- هاست: همان برنامه‌ي اصلي كه قرار است از افزونه استفاده كند.
- قرار داد: نحو‌ه‌ي تعامل هاست و افزونه در اين پروژه تعريف مي‌شود. (يك پروژه از نوع class library)
- افزونه: كار پياده سازي قرار داد را عهده دار خواهد شد. (يك پروژه از نوع class library)

- همچنين مرسوم است جهت مديريت بهتر خروجي‌هاي حاصل شده يك پوشه Output را نيز به اين solution اضافه كنند:



اكنون با توجه به اين محل خروجي، به خواص Build سه پروژه موجود مراجعه كرده و مسير Build را اندكي اصلاح خواهيم كرد (هر سه مورد بهتر است اصلاح شوند)، براي مثال:



نكته‌ي مهم هم اينجا است كه خروجي host بايد به ريشه اين پوشه تنظيم شود و ساير پروژه‌ها هر كدام خروجي خاص خود را در پوشه‌اي داخل اين ريشه بايد ايجاد كنند.



تا اينجا قالب اصلي كار آماده شده است. قرارداد ما هم به شكل زير است (ويژگي AddInContract آن نيز نبايد فراموش شود):

using System.AddIn.Pipeline;
using System.AddIn.Contract;

namespace CalculatorConract
{
[AddInContract]
public interface ICalculatorContract : IContract
{
double Operate(string operation, double a, double b);
}
}

به عبارت ديگر برنامه‌اي محاسباتي داريم (هاست) كه دو عدد double را در اختيار افزونه‌هاي خودش قرار مي‌دهد و سپس اين افزونه‌ها يك عمليات رياضي را بر روي آن‌ها انجام داده و خروجي را بر مي‌گردانند. نوع عمليات توسط آرگومان operation مشخص مي‌شود. اين آرگومان به كليه افزونه‌هاي موجود ارسال خواهد شد و احتمالا يكي از آن‌ها اين مورد را پياده سازي كرده است. در غير اينصورت يك استثناي عمليات پياده سازي نشده صادر مي‌شود.
البته روش بهتر طراحي اين افزونه، اضافه كردن متد يا خاصيتي جهت مشخص كردن نوع و يا انواع عمليات پشتيباني شده توسط افزونه‌ است كه جهت سادگي اين مثال، به اين طراحي ساده اكتفا مي‌شود.

ايجاد pipeline

اگر قسمت قبل را مطالعه كرده باشيد، يك راه حل مبتني بر MAF از 7 پروژه تشكيل مي‌شود كه عمده‌ترين خاصيت آن‌ها مقاوم كردن سيستم در مقابل تغييرات نگارش قرارداد است. در اين حالت اگر قرار داد تغيير كند، نه هاست و نه افزونه‌ي قديمي، نيازي به تغيير در كدهاي خود نخواهند داشت و اين پروژه‌هاي مياني هستند كه كار وفق دادن (adapters) نهايي را برعهده مي‌گيرند.


براي ايجاد خودكار View ها و همچنين Adapters ، از افزونه‌ي Visual Studio Pipeline Builder كه پيشتر معرفي شد استفاده خواهيم كرد.



سه گزينه‌ي آن هم مشخص هستند. نام پروژه‌ي قرارداد، مسير پروژه‌ي هاست و مسير خروجي نهايي معرفي شده. پيش از استفاده از اين افزونه نياز است تا يكبار solution مورد نظر كامپايل شود. پس از كليك بر روي دكمه‌ي OK، پروژه‌هاي ذكر شده ايجاد خواهند شد:


پس از ايجاد اين پروژه‌ها، نياز به اصلاحات مختصري در مورد نام اسمبلي و فضاي نام هر كدام مي‌باشد؛ زيرا به صورت پيش فرض هر كدام به نام template نامگذاري شده‌اند:



پياده سازي افزونه

قالب كاري استفاده از اين فريم ورك آماده است. اكنون نوبت به پياده سازي يك افزونه مي‌باشد. به پروژه AddIn مراجعه كرده و ارجاعي را به اسمبلي AddInView خواهيم افزود. به اين صورت افزونه‌ي ما به صورت مستقيم با قرارداد سروكار نداشته و ارتباطات، در راستاي همان pipeline تعريف شده، جهت مقاوم شدن در برابر تغييرات صورت مي‌گيرد:
using System;
using CalculatorConract.AddInViews;
using System.AddIn;

namespace CalculatorAddIn
{
[AddIn]
public class MyCalculatorAddIn : ICalculator
{
public double Operate(string operation, double a, double b)
{
throw new NotImplementedException();
}
}
}

در اينجا افزونه‌ي ما بايد اينترفيس ICalculator مربوط به AddInView را پياده سازي نمايد كه براي مثال خواهيم داشت:

using System;
using CalculatorConract.AddInViews;
using System.AddIn;

namespace CalculatorAddIn
{
[AddIn("افزونه يك", Description = "توضيحات", Publisher = "نويسنده", Version = "نگارش يك")]
public class MyCalculatorAddIn : ICalculator
{
public double Operate(string operation, double a, double b)
{
switch (operation)
{
case "+":
return a + b;
case "-":
return a - b;
case "*":
return a * b;
default:
throw new NotSupportedException("عمليات مورد نظر توسط اين افزونه پشتيباني نمي‌شود");
}
}
}
}

همانطور كه در قسمت قبل نيز ذكر شد، اين كلاس بايد با ويژگي AddIn مزين شود كه توسط آن مي‌توان توضيحاتي در مورد نام ، نويسنده و نگارش افزونه ارائه داد.


استفاده از افزونه‌ي توليد شده

هاست براي استفاده از افزونه‌هايي با قرارداد ذكر شده، مطابق pipeline پروژه، نياز به ارجاعي به اسمبلي HostView دارد و در اينجا نيز هاست به صورت مستقيم با قرارداد كاري نخواهد داشت. همچنين هاست هيچ ارجاع مستقيمي به افزونه‌ها نداشته و بارگذاري و مديريت آن‌ها به صورت پويا انجام خواهد شد.

نكته‌ي مهم!
در هر دو ارجاع به HostView و يا AddInView بايد خاصيت Copy to local به false تنظيم شود، در غير اينصورت افزونه‌ي شما بارگذاري نخواهد شد.



پس از افزودن ارجاعي به HostView، نمونه‌اي از استفاده از افزونه‌ي توليد شده به صورت زير مي‌تواند باشد كه توضيحات مربوطه به صورت كامنت آورده شده است:

using System;
using System.AddIn.Hosting;
using CalculatorConract.HostViews;

namespace Calculator
{
class Program
{
private static ICalculator _calculator;

static void doOperation()
{
Console.WriteLine("1+2: {0}", _calculator.Operate("+", 1, 2));
}

static void Main(string[] args)
{
//مسير پوشه ريشه مربوطه به خط لوله افزونه‌ها
string path = Environment.CurrentDirectory;

//مشخص سازي مسير خواندن و كش كردن افزونه‌ها
AddInStore.Update(path);

//يافتن افزونه‌هايي سازگار با شرايط قرارداد پروژه
//در اينجا هيچ افزونه‌اي بارگذاري نمي‌شود
var addIns = AddInStore.FindAddIns(typeof(ICalculator), path);

//اگر افزونه‌اي يافت شد
if (addIns.Count > 0)
{
var addIn = addIns[0]; //استفاده از اولين افزونه
Console.WriteLine("1st addIn: {0}", addIn.Name);

//فعال سازي افزونه و همچنين مشخص سازي سطح دسترسي آن
_calculator = addIn.Activate<ICalculator>(AddInSecurityLevel.Intranet);

//يك نمونه از استفاده آن
doOperation();
}

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}

چند نكته جالب توجه در مورد قابليت‌هاي ارائه شده:
- مديريت load و unload پويا
- امكان تعريف سطح دسترسي و ويژگي‌هاي امنيتي اجراي يك افزونه
- امكان ايزوله سازي پروسه اجراي افزونه از هاست (در ادامه توضيح داده خواهد شد)
- مقاوم بودن پروژه به نگارش‌هاي مختلف قرارداد


اجراي افزونه در يك پروسه مجزا

حتما با امكانات مرورگر كروم و يا IE8 در مورد اجراي هر tab آن‌ها در يك پروسه‌ي مجزا از پروسه اصلي هاست مطلع هستيد. به اين صورت پروسه‌ي هاست از رفتار tab ها محافظت مي‌شود، همچنين پروسه‌ي هر tab نيز از tab ديگر ايزوله خواهد بود. يك چنين قابليتي در اين فريم ورك نيز پيش بيني شده است.

//فعال سازي افزونه و همچنين مشخص سازي سطح دسترسي آن
//همچنين جدا سازي پروسه اجرايي افزونه از هاست
_calculator = addIn.Activate<ICalculator>(
new AddInProcess(),
AddInSecurityLevel.Intranet);

در اين حالت اگر پس از فعال شدن افزونه، يك break point قرار دهيم و به task manager ويندوز مراجعه نمائيم، پروسه‌ي مجزاي افزونه قابل مشاهده است.



براي مطالعه بيشتر + ، + ، + و +