8.ORM Lazy Load
List<Customer> customers = context.Customers.ToList(); foreach (Customer cust in context.Customers){ Console.WriteLine("Customer {0}, Account {1}", cust.Person.LastName.Trim() + ", " + cust.Person.FirstName, cust.AccountNumber); }
9.استفاده از MiniProfiler
#if DEBUG then // فعال سازی MiniProfiler #endif
10. Data Paging در بانک اطلاعاتی
11. بررسی تعداد کوئریهای صادر شده در یک صفحه و تعداد رکوردهای بازگشت داده شده توسط آنها
HttpHandler
public class MyHttpHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { } public bool IsReusable { get { return false; } } }
public class MyHttpHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { HttpResponse response = context.Response; HttpRequest request = context.Request; response.Write("Every Page has a some text like this"); } public bool IsReusable { get { return false; } } }
<system.web> <httpHandlers> <add verb="*" path="*.aspx" type="MyHttpHandler"/> </httpHandlers> </system.web>
<configuration> <system.web> <httpHandlers> <add name="myhttphandler" verb="*" path="*.aspx" type="MyHttpHandler"/> </httpHandlers> </system.web> </configuration>
گزینه Type که نام کلاس میباشد و اگر کلاس داخل یک فضای نام قرار گرفته باشد، باید اینطور نوشت : namespace.ClassName |
گزینه verb شامل مقادیری چون Get,Post,Head,Putو Delete میباشد و httphandler را فقط برای این نوع درخواستها اجرا میکند و در صورتیکه بخواهید چندتا از آنها را استفاده کنید، با , از هم جدا میشوند. مثلا Get,post و درصورتیکه همهی گزینهها را بخواهید علامت * را میتوان استفاده کرد. |
گزینهی path این امکان را به شما میدهد که مسیر و نوع فایلهایی را که قصد دارید روی آنها فقط اجرا شود، مشخص کنید و ما در قطعه کد بالا گفتهایم که تنها روی فایلهایی با پسوند aspx اجرا شود و چون مسیری هم ذکر نکردیم برای همهی مسیرها قابل اجراست. یکی از مزیتهای دادن پسوند این است که میتوانید پسوندهای اختصاصی داشته باشید. مثلا پسوند RSS برای فیدهای وب سایتتان. بسیاری از برنامه نویسان به جای استفاده از صفحات aspx از ashx استفاده میکنند که به مراتب سبکتر از aspx هست و شامل بخش ui نمیشود و نتیجه خروجی آن بر اساس کدی که مینویسید مشخص میشود که میتواند صفحه متنی یا عکس یا xml یا ... باشد. در اینجا در مورد ساخت صفحات ashx توضیح داده شده است. |
public class MyHttpHandler1 :IHttpHandler { public void ProcessRequest(HttpContext context) { HttpResponse response = context.Response; response.Write("this is httphandler1"); } public bool IsReusable { get { return false; } } } public class MyHttpHandler2 : IHttpHandler { public void ProcessRequest(HttpContext context) { HttpResponse response = context.Response; response.Write("this is httphandler2"); } public bool IsReusable { get { return false; } } }
public class MyFactory : IHttpHandlerFactory { public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTrasnlated) { } public void ReleaseHandler(IHttpHandler handler) { } }
Context | یک شی از کلاس httpcontext که دسترسی ما را برای اشیاء سروری چون response,request,session و... فراهم میکند. |
RequestType | مشخص میکند که درخواست صفحه به چه صورتی است. این گزینه برای مواردی است که verb بیش از یک مورد را حمایت میکند. برای مثال دوست دارید یک هندلر را برای درخواستهای Get ارسال کنید و هندلر دیگر را برای درخواستهای نوع Post |
URL | مسیر مجازی virtual Path صفحه صدا زده شده |
PathTranslated | مسیر فیزیکی صفحه درخواست کننده را ارسال میکند. |
public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTrasnlated) { string handlername = "MyHttpHandler1"; if(url.Substring(url.LastIndexOf("/")+1).StartsWith("t")) { handlername = "MyHttpHandler2"; } try { return (IHttpHandler) Activator.CreateInstance(Type.GetType(handlername)); } catch (Exception e) { throw new HttpException("Error: " + handlername, e); } } public void ReleaseHandler(IHttpHandler handler) { } }
موقعی که اینترفیس IHttpAsyncHandler را ارث بری کنید (این اینترفیس نیز از IHttpHandler ارث بری کرده است و دو متد اضافهتر دارد)، باید دو متد دیگر را نیز پیاده سازی کنید:در اعمالی که disk I/O و یا network I/O دارند، پردازش موازی و اعمال async به شدت مقیاس پذیری سیستم را بالا میبرند. به این ترتیب worker thread جاری (که تعداد آنها محدود است)، سریعتر آزاد شده و به worker pool بازگشت داده میشود تا بتواند به یک درخواست دیگر رسیده سرویس دهد. در این حالت میتوان با منابع کمتری، درخواستهای بیشتری را پردازش کرد.
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback callback, object obj) { } public void EndProcessRequest(IAsyncResult result) { }
public class AsynchOperation : IAsyncResult { private bool _completed; private Object _state; private AsyncCallback _callback; private HttpContext _context; bool IAsyncResult.IsCompleted { get { return _completed; } } WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } } Object IAsyncResult.AsyncState { get { return _state; } } bool IAsyncResult.CompletedSynchronously { get { return false; } } }
public AsynchOperation(AsyncCallback callback, HttpContext context, Object state) { _callback = callback; _context = context; _state = state; _completed = false; }
public void StartAsyncWork() { ThreadPool.QueueUserWorkItem(new WaitCallback(StartAsyncTask),null); }
private void StartAsyncTask(Object workItemState) { _context.Response.Write("<p>Completion IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n"); _context.Response.Write("Hello World from Async Handler!"); _completed = true; _callback(this); }
context.Response.Write("<p>Begin IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n"); AsynchOperation asynch = new AsynchOperation(callback, context, obj); asynch.StartAsyncWork(); return asynch;
public class MyHttpHandler : IHttpAsyncHandler { public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback callback, object obj) { context.Response.Write("<p>Begin IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n"); AsynchOperation asynch = new AsynchOperation(callback, context, obj); asynch.StartAsyncWork(); return asynch; } public void EndProcessRequest(IAsyncResult result) { } public void ProcessRequest(HttpContext context) { throw new InvalidOperationException(); } public bool IsReusable { get { return false; } } } public class AsynchOperation : IAsyncResult { private bool _completed; private Object _state; private AsyncCallback _callback; private HttpContext _context; bool IAsyncResult.IsCompleted { get { return _completed; } } WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } } Object IAsyncResult.AsyncState { get { return _state; } } bool IAsyncResult.CompletedSynchronously { get { return false; } } public AsynchOperation(AsyncCallback callback, HttpContext context, Object state) { _callback = callback; _context = context; _state = state; _completed = false; } public void StartAsyncWork() { ThreadPool.QueueUserWorkItem(new WaitCallback(StartAsyncTask),null); } private void StartAsyncTask(Object workItemState) { _context.Response.Write("<p>Completion IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n"); _context.Response.Write("Hello World from Async Handler!"); _completed = true; _callback(this); }
مایکروسافت در MSDN نوشته است که httphandlerها در واقع فرآیندهایی هستند (به این فرایندها بیشتر End Point میگویند) که در پاسخ به درخواستهای رسیده شده توسط asp.net application اجرا میشوند و بیشترین درخواست هایی هم که میرسد از نوع صفحات Aspx میباشد و موقعی که کاربری درخواست صفحهی aspx میکند هندلرهای مربوط به page اجرا میشوند.
در طی چند مقاله قصد بررسی نحوهی تولید برنامههای توسعه پذیر (extensible) را با استفاده از plug-ins و یا add-ins داریم.
افزونهها عموما در سه گروه قرار میگیرند:
الف) افزونه، سرویسی را به هاست ارائه میدهد. برای مثال یک میل سرور نیاز به افزونههایی برای ویروس یابی یا فیلتر کردن هرزنامهها دارد؛ یا یک برنامه پردازش متنی نیاز به افزونهای جهت بررسی غلطهای املایی میتواند داشته باشد و یا یک مرورگر وب میتواند با کمک افزونهها قابلیتهای پیش فرض خود را به شدت توسعه و افزایش دهد (نمونهی بارز آن فایرکس است که عمدهترین دلیل اقبال عمومی به آن سهولت توسعه پذیری آن میباشد).
ب) در گروه دوم، هاست، رفتار مشخصی را ارائه داده و سپس افزونه بر اساس آن، نحوهی عملکرد هاست را مشخص میکند. در این حالت هاست است که سرویسی را به افزونه ارائه میدهد. نمونهی بازر آن افزونههای آفیس هستند که امکان اتوماسیون فرآیندهای مختلف آنرا میسر میسازند. به این صورت امکان توسعهی یک برنامه به شکلی که در طراحی اولیه آن اصلا انتظار آن نمیرفته وجود خواهد داشت. همچنین در اینجا نیازی به داشتن سورس کد برنامهی اصلی نیز نمیباشد.
ج) گروه سوم افزونهها تنها از هاست جهت نمایش خود استفاده کرده و عملا استفادهی خاصی از هاست ندارد. برای مثال نوار ابزاری که خود را به windows explorer متصل میکند و تنها از آن جهت نمایش خود بهره میجوید.
در حال حاضر حداقل دو فریم ورک عمده جهت انجام اینکار و تولید افزونهها برای دات نت فریم ورک مهیا است:
الف) managed addin framework یا MAF
ب) managed extensibility framework یا MEF
فضای نام جدیدی به دات نت فریم ورک سه و نیم به نام System.AddIn اضافه شده است که به آن Managed AddIn Framework یا MAF نیز اطلاق میشود. از این فریم ورک در VSTO (تولید افزونه برای مجموعهی آفیس) توسط خود مایکروسافت استفاده شده است.
فریم ورک توسعهی افزونههای مدیریت شده در دات نت فریم ورک سه و نیم، مزایای زیر را در اختیار ما خواهد گذاشت:
- امکانات load و unload افزونههای تولید شده
- امکان تغییر افزونهها در زمان اجرای برنامه اصلی بدون نیاز به بستن آن
- ارائهی محیطی ایزوله با ترسیم مرزی بین افزونه و برنامه اصلی
- مدیریت طول عمر افزونه
- مدیریت سازگاری با نگارشهای قبلی و یا بعدی یک افزونه
- امکانات به اشتراک گذاری افزونهها با برنامههای دیگر
- تنظیمات امنیتی و مشخص سازی سطح دسترسی افزونهها
و ...
یک راه حل مبتنی بر MAF میتواند شامل 7 پروژه باشد (که به روابط تعریف شده در آن pipeline هم گفته میشود):
Host : همان برنامهی اصلی است که توسط یک سری افزونه، توسعه یافته است.
Host View : بیانگر انتظارات هاست از افزونهها است. به عبارت دیگر افزونهها باید موارد لیست شده در این پروژه را پیاده سازی کنند.
Host Side Adapter : پل ارتباطی Host View و پروژهی Contract است.
Contract: اینترفیسی است که کار برقراری ارتباط بین Host و افزونهها را برعهده دارد.
Add-In Side Adapter : پل ارتباطی بین Add-In View و Contract است.
Add-In View : حاوی متدها و اشیایی است که جهت برقراری ارتباط با هاست از آنها استفاده میشود.
Add-In : اسمبلی است که توسط هاست جهت توسعهی قابلیتهای خود بارگذاری میشود (به آن Add-On ، Extension ، Plug-In و Snap-In هم گفته میشود).
هدف از این جدا سازیها ارائهی راه حل loosely-coupledایی است که امکان ایزوله سازی، اعمال شرایط امنیتی ویژه و همچنین کنترل نگارشهای مختلف را تسهیل میبخشد و این امر با استفاده از interface های معرفی شده میسر گردیده است. این pipeline از قسمتهای ذیل تشکیل میشود:
قرار داد یا Contract
برای تولید یک افزونه نیاز است تا بین هاست و افزونه قراردادی بسته شود. با توجه به استفاده از MAF ، روش تعریف این قرار داد برای مثال در یک افزونهی مترجم به صورت زیر باید باشد:
[AddInContract]
public interface ITranslator : IContract
{
string Translate(string input);
}
استفاده از ویژگی AddInContract و پیاده سازی اینترفیس IContract جزو مراحل کاری استفاده از MAF است. MAF هنگام تولید پویای pipeline ذکر شده به دنبال ویژگی AddInContract میگردد. این موارد در فضای نام System.AddIn.Pipeline تعریف شدهاند.
دیدگاهها یا Views
دیدگاهها کدهایی هستند که کار تعامل مستقیم بین افزونه و هاست را بر عهده دارند. هاست یا افزونه هر کدام میتوانند دیدگاه خود را نسبت به قرار داد بسته شده داشته باشند. این موارد نیز همانند قرار داد در اسمبلیهای مجزایی نگهداری میشوند.
دیدگاه هاست نسبت به قرار داد:
public abstract class TranslatorHostView
{
public abstract string Translate(string input);
}
[AddInBase]
public abstract class TranslatorHostView
{
public abstract string Translate(string input);
}
هر دو کلاس فوق بر اساس قرار موجود بنا میشوند اما وابسته به آن نیستند. به همین جهت به صورت کلاسهایی abstract تعریف شدهاند. در سمت افزونه، کلاس تعریف شده دیدگاه آن با کلاس دیدگاه سمت هاست تقریبا یکسان میباشد؛ اما با ویژگی AddInBase تعریف شده در فضای نام System.AddIn.Pipeline مزین گردیده است.
وفق دهندهها یا Adapters
آخرین قسمت pipeline ، وفق دهندهها هستند که کار آنها اتصال قرار داد به دیدگاهها است و توسط آن مدیریت طول عمر افزونه و همچنین تبدیل اطلاعات بین قسمتهای مختلف انجام میشود. شاید در نگاه اول وجود آنها زائد به نظر برسد اما این جدا سازی کدها سبب تولید افزونههایی خواهد شد که به نگارش هاست و برنامه اصلی وابسته نبوده و بر عکس (version tolerance). به دو کلاس زیر دقت نمائید:
کلاس زیر با ویژگی [HostAdapter] تعریف شده در فضای نام System.AddIn.Pipeline، مزین شده است و کار آن اتصال HostView به Contract میباشد. برای این منظور TranslatorHostView ایی را که پیشتر معرفی کردیم باید پیاده سازی نماید. علاوه بر این با ایجاد وهلهای از کلاس ContractHandle ، کار مدیریت طول عمر افزونه را نیز میتوان انجام داد.
[HostAdapter]
public class TranslatorHostViewToContract : TranslatorHostView
{
ITranslator _contract;
ContractHandle _lifetime;
public TranslatorHostViewToContract(ITranslator contract)
{
_contract = contract;
_lifetime = new ContractHandle(contract);
}
public override string Translate (string inp)
{
return _contract.Translate(inp);
}
}
[AddInAdapter]
public class TranslatorAddInViewToContract : ContractBase, ITranslator
{
TranslatorAddInView _view;
public TranslatorAddInViewToContract(TranslatorView view)
{
_view = view;
}
public string Translate(string inp)
{
return _view.Translate(inp);
}
}
قسمت عمدهای از این کدها تکراری است. جهت سهولت تولید این کلاسها و پروژههای مرتبط، تیم مربوطه برنامهای را به نام pipeline builder ارائه داده است که از آدرس زیر قابل دریافت است:
این برنامه با دریافت اسمبلی مربوط بهcontract ، کار ساخت خودکار کلاسهای adapters و views را انجام خواهد داد.
ایجاد افزونه
پس از ساخت قسمتهای مختلف pipeline ، اکنون میتوان افزونه را ایجاد نمود. هر افزونه باید add-in view را پیاده سازی کرده و با ویژگی AddIn مزین شود. برای مثال:
[AddIn("GoogleTranslator", Description="Universal translator",
Version="1.0.0.0", Publisher="YourName")]
public class GoogleAddIn : TranslatorAddInView
{
public string Translate(string input)
{
...
}
}
ادامه دارد ....
معماری لایه بندی شده، یک معماری بسیار همه گیر میباشد. به این خاطر که به راحتی SOC ، decoupling و قدرت درک کد را بسیار بالا میبرد. امروزه کمتر برنامه نویس و فعال حوضهی نرم افزاری است که با لایههای کلی و وظایف آنها آشنا نباشد ( UI layer آنچه که ما میبینیم، middle layer برای مقاصد منطق کاری، data access layer برای هندل کردن دسترسی به دادهها). اما مسئله ای که بیشتر برنامه نویسان و توسعه دهندگان نرم افزار با استانداردهای آن آشنا نیستند، راههای تبادل دادهها مابین layer ها میباشد. در این مقاله سعی داریم راههای تبادل دادهها را مابین لایهها، تشریح کنیم.
Layers و Tiers با هم متفاوت هستند
Layer با Tier متفاوت است. هنگامیکه در مورد مفهوم layer و Tier دچار شک شدید،
دیاگرام ذیل میتواند به شما بسیار کمک کند. layer به مجزاسازی
منطقی کد و Tier هم به مجزا سازی فیزیکی در ماشینهای مختلف اطلاق میشود. توجه داشته
باشید که این نکته یک شفاف سازی کلی در مورد یک مسئله مهم بود.
دادههای وارد شونده( incoming ) و خارج شونده( outgoing )
ما باید تبادل دادهها را از دو جنبه مورد بررسی قرار دهیم؛ اول اینکه دادهها چگونه به سمت لایه Data Access میروند، دوم اینکه دادهها چگونه به لایه UI پاس میشوند، در ادامه شما دلیل این مجزا سازی را درک خواهید کرد.
روش اول: Non-uniform
این روش اولین روش و احتمالا عمومیترین روش میباشد. خوب، اجازه دهید از لایهی UI به لایه DAL شروع کنیم. دادهها از لایه UI به Middle با استفاده از getter ها و setter ها ارسال خواهد شد. کد ذیل این مسئله را به روشنی نمایش میدهد.
Customer objCust = new Customer(); objCust.CustomerCode = "c001"; objCust.CustomerName = "Shivprasad";
بعد از آن، از لایه Middle به لایه Data Access دادهها با استفاده از مجزاسازی به وسیله comma و آرایهها و سایر روشهای non-uniform پاس داده میشوند. در کد ذیل به متد Add دقت کنید که چگونه فراخوانی به لایه Data Access را با استفاده از پارامترهای ورودی انجام میدهد.
public class Customer { private string _CustomerName = ""; private string _CustomerCode = ""; public string CustomerCode { get { return _CustomerCode; } set { _CustomerCode = value; } } public string CustomerName { get { return _CustomerName; } set { _CustomerName = value; } } public void Add() { CustomerDal obj = new CustomerDal(); obj.Add(_CustomerName,_CustomerCode); } }
کد ذیل، متد add در لایه Data Access را با استفاده از دو متد نمایش میدهد.
public class CustomerDal { public bool Add(string CustomerName,string CustomerCode) { // Insert data in to DB } }
بنابراین اگر بخواهیم به صورت خلاصه نحوه پاس دادن دادهها را در روش non-uniform بیان کنیم، شکل ذیل به زیبایی این مسئله را نشان میدهد.
· از لایه UI به لایه Middle با استفاده از setter و getter
· از لایه Middle به لایه data access با استفاده از comma ، input ، array
حال
نوبت این است بررسی کنیم که چگونه دادهها از DAL به UI در روش non-uniform پاس
خواهند شد. بنابراین اجازه دهید که اول از UI شروع
کنیم. از لایه UI دادهها با استفاده از object های لایه Middle واکشی میشوند.
Customer obj = new Customer(); List<Customer> oCustomers = obj.getCustomers();
از لایه Middle هم دادهها با استفاده از dataset ، datatable و xml پاس خواهند شد. مهمترین مسئله برای لایه middle ، loop بر روی dataset و تبدیل آن به strong type object ها میباشد. برای مثال میتوانید کد تابع getCustomers که بر روی dataset ، loop میزند و یک لیست از Customer ها را آماده میکند در ذیل مشاهده کنید. این تبدیل باید انجام شود، به این دلیل که UI به کلاسهای strongly typed دسترسی دارد.
public class Customer { private string _CustomerName = ""; private string _CustomerCode = ""; public string CustomerCode { get { return _CustomerCode; } set { _CustomerCode = value; } } public string CustomerName { get { return _CustomerName; } set { _CustomerName = value; } } public List<Customer> getCustomers() { CustomerDal obj = new CustomerDal(); DataSet ds = obj.getCustomers(); List<Customer> oCustomers = new List<Customer>(); foreach (DataRow orow in ds.Tables[0].Rows) { // Fill the list } return oCustomers; } }
با انجام این تبدیل به یکی از بزرگترین اهداف معماری لایه بندی شده میرسیم؛ یعنی اینکه « UI نمیتواند به طور مستقیم به کامپوننتهای لایه Data Access مانند ADO.NET ، OLEDB و غیره دستیابی داشته باشد. با این روش اگر ما در ادامه متدولوژی Data Access را تغییر دهیم تاثیری بر روی لایه UI نمیگذارد.» آخرین مسئله اینکه کلاس CustomerDal یک Dataset را با استفاده از ADO.NET بر میگرداند و Middle از آن استفاده میکند.
public class CustomerDal { public DataSet getCustomers() { // fetch customer records return new DataSet(); } }
حال اگر بخواهیم حرکت دادهها را به لایه UI، به صورت خلاصه بیان کنیم، شکل ذیل کامل این مسئله را نشان میدهد.
· دادهها از لایه DAL به لایه Middle با استفاده از Dataset ، Datareader ، XML ارسال خواهند شد.
· از لایه Middle به UI از strongly typed classes استفاده میشود.
مزایا و معایب روش non-uniform
یکی از مزایای non-uniform
· به راحتی قابل پیاده سازی میباشد، در مواردی که روش data access تغییر نمیکند این روش کارآیی لازم را دارد.
تعدادی از معایب این روش
· به خاطر اینکه یک ساختار uniform نداریم، بنابراین نیاز داریم که همیشه در هنگام عبور از یک لایه به یک لایهی دیگر از یک ساختار به یک ساختار دیگر تبدیل را انجام دهیم.
· برنامه نویسان از روشهای خودشان برای پاس دیتا استفاده میکنند؛ بنابراین این مسئله خود باعث پیچیدگی میشود.
· اگر برای مثال شما بخواهید متدولوژی Data Access خود را تغییر دهید، تغییرات بر تمام لایهها تاثیر میگذارد.