اشتراکها
به روز رسانی خودکار برنامه توسط خود آن (self-update) با بررسی اطلاعات آخرین به روز رسانی، از وبسایت ما.
کاربر نگارشهای جدید دریافتی را (از طریق ایمیل و یا وب سایت ما) بر روی نگارشهای فعلی کپی میکند.
کاربر یک برنامهی به روز رسانی نصاب مانند را اجرا میکند.
کاربران به علت پیچیدگی برنامه نمیتوانند برنامه را به روز رسانی کنند؛ ما برای آنها اینکار را انجام میدهیم.
از ClickOnce استفاده میکنیم.
توسط سیستم به روز رسانی App Storeها.
از Docker استفاده میکنیم.
کاربر نگارشهای جدید دریافتی را (از طریق ایمیل و یا وب سایت ما) بر روی نگارشهای فعلی کپی میکند.
کاربر یک برنامهی به روز رسانی نصاب مانند را اجرا میکند.
کاربران به علت پیچیدگی برنامه نمیتوانند برنامه را به روز رسانی کنند؛ ما برای آنها اینکار را انجام میدهیم.
از ClickOnce استفاده میکنیم.
توسط سیستم به روز رسانی App Storeها.
از Docker استفاده میکنیم.
Document Object Model و یا به اختصار DOM به ظهور زبان JavaScript گره خوردهاست. این مدل به همراه یک API پیاده سازی شدهی با JavaScript است که امکان دسترسی به اسناد HTML را مسیر میکند. علاوه بر امکاناتی مانند انتخاب عناصر، کار با ویژگیها و ذخیرهی اطلاعات که تاکنون بررسی کردیم، DOM API به همراه روشهایی برای ایجاد عناصر جدید، حذف عناصر موجود و جابجایی آنها در صفحه میباشد. یکی از مهمترین اهداف jQuery کار سادهتر با DOM است و تعداد متدهایی را که برای کار با DOM ارائه میکند، تاکنون کمتر از 20 درصد کل DOM API اصلی را پوشش میدهند.
حرکت دادن المانها در صفحه
ابتدا قطعه کد HTML زیر را درنظر بگیرید:
میخواهیم با تغییر DOM، به خروجی زیر برسیم که در آن لیستها جابجا، تکمیل و یا خالی شدهاند:
حرکت دادن المانها توسط jQuery
به این ترتیب vanilla به بعد از chocolate در لیست flavors منتقل میشود.
در ادامه میخواهیم لیست types را به همراه عنوان آن، به قبل از لیست flavors منتقل کنیم:
متد prependTo سبب درج عنوان types دقیقا پس از تگ body میشود. سپس لیست types را پس از این عنصر جابجا شده اضافه میکنیم.
سپس در لیست unassigned ابتدا rocky road آنرا یافته و به بالای strawberry در لیست flavors اضافه میکنیم. همچنین gelato آنرا نیز یافته و به انتهای لیست types اضافه خواهیم کرد:
حرکت دادن المانها توسط جاوا اسکریپت خالص (سازگار با IE 8.0 به بعد)
در ابتدا میخواهیم المان vanilla را به قبل از المان strawberry حرکت دهیم. برای اینکار میتوان از متد استاندارد insertBefore استفاده کرد:
flavors در اینجا والد نودی است که قرار است جابجا شود. اولین پارامتری که به متد insertBefore ارسال میشود، المانی است که قرار است جابجا شود. دومین پارامتر آن «نود مرجع» است. چون میخواهیم vanilla را قبل از strawberry درج کنیم، المان strawberry نود مرجع خواهد بود.
سپس کار انتقال عنوان لیست types و خود آن به قبل از لیست flavors صورت میگیرد:
در اینجا ابتدا عنوان types، به ابتدای document.body منتقل میشود (چون والد این عنوان document.body است، متد insertBefore بر روی آن فراخوانی میشود). سپس میخواهیم خود typesList را نیز حرکت دهیم. به همین جهت نیاز به نود مرجع عنوان flavors است که به عنوان پارامتر دوم متد insertBefore ذکر شدهاست تا این لیست، پیش از آن درج شود.
در آخر میخواهیم آیتمهای لیست unassigned را به لیستهای مرتبط با آنها انتقال دهیم:
در اولین سطر، querySelector تعریف شده، اولین المان لیست یا همان rocky road را بازگشت میدهد. به این ترتیب المان rocky road لیست unassigned به لیست flavors منتقل میشود . به همین جهت flavors به عنوان والد متد insertBefore تعریف شدهاست. نود مرجع نیز strawberry است؛ زیرا میخواهیم rocky road را به پیش از آن منتقل کنیم.
در سطر دوم، چون هم اکنون المان rocky road از لیست unassigned حذف شدهاست، متد querySelector فراخوانی شده، اولین عنصر لیست یا همان gelato را بازگشت میدهد. این المان را توسط متد appendChild به انتهای لیست types اضافه خواهیم کرد. متد appendChild نیز همانند متد insertBefore نیاز به یک والد دارد که همان عنصری است که قرار است المانها به آن افزوده شوند.
کپی کردن المانها
در جیکوئری برای تهیهی یک کپی از این المان خواهیم داشت:
اگر به این متد پارامتر true نیز ارسال شود، اطلاعات و همچنین رخدادهای منتسب به آن نیز کپی میشوند. البته این کپی فقط شامل اطلاعات تدارک دیده شدهی توسط jQuery API است و نه خارج از آن.
و در جاوا اسکریپت خالص (سازگار با IE 8.0 به بعد) برای کپی کردن المانها دو روش shallow و deep وجود دارد:
Shallow clone به معنای کپی المان ol بدون فرزندان آن است. در حالت deep clone المان ol و تمام فرزندان آن با هم کپی میشوند.
باید دقت داشت که متد cloneNode آنچه را که مشاهده میکنید یا همان اصل markup را کپی میکند. بنابراین اگر از طریق جاوا اسکریپت تغییراتی را در آن شیء داده باشید در متد cloneNode لحاظ نمیشود.
بدیهی است المانهای clone شده تا زمانیکه با متدهایی مانند insertBefore و یا appendChild به صفحه اضافه نشوند، در صفحه نمایان نخواهند شد.
ایجاد و حذف المانها
فرض کنید میخواهیم به لیست flavors مثال ابتدای بحث، دو مورد جدید را اضافه کنیم.
روش افزودن المانهای جدید توسط جیکوئری:
و یا حذف یک آیتم موجود توسط جیکوئری:
در اینجا last: اصطلاحا یک pseduo-class ابداعی توسط jQuery است که آنچنان کارآیی بالایی هم ندارد.
روش افزودن المانهای جدید توسط جاوا اسکریپت خالص:
و برای حذف آخرین آیتم یک لیست توسط جاوا اسکریپت خالص:
در اینجا last-child: یک CSS3 pseudo-class selector استاندارد است.
روش دیگر انجام اینکار به صورت زیر توسط متد removeChild است:
کار با المانهای متنی
در جیکوئری متد ()text آن امکان دریافت محتوای متنی و همچنین به روز رسانی آنرا میسر میکند:
در اینجا متن دومین المان لیست types به italian ice با i کوچک به روز رسانی میشود.
در جاوا اسکریپت خالص، دو خاصیت textContent و همچنین innerText برای خواندن و یا به روز رسانی محتوای متنی عناصر مورد استفاده قرار میگیرند. برای مثال معادل قطعه کد جیکوئری فوق که از متد text استفاده میکند با جاوا اسکریپت خالص به صورت زیر است:
توسط querySelectorAll تمام liهای types یافت شده و سپس خاصیت textContent دومین عنصر آن با italian ice به روز رسانی شدهاست.
خاصیت innerText هرچند بر روی اینترفیس HTMLElement تعریف شدهاست، اما جزء هیچکدام از استانداردهای وب نیست؛ ولی توسط تمام مرورگرهای امروزی پشتیبانی میشود. در این حالت به روز رسانی متن توسط آن با خاصیت textContent دقیقا یکی است؛ اما خروجی آن برعکس حالتهای قبل، متن رندر شدهی المانها را بازگشت میدهد. برای مثال در اینجا شامل فاصلههای پیش از این المانها در markup نمیشود.
برای مثال این قسمتی از خروجی خاصیت textContent است:
اما در این همین حالت خروجی innerText به این صورت است:
کار با محتوای HTML ایی رشتهای
گاهی از اوقات از سرور قطعهای کد HTML ایی را دریافت میکنیم (که هنوز به صورت المان یا المانهای DOM در نیامدهاست) و در سمت کلاینت میخواهیم آنرا به قسمتی از صفحه اضافه کنیم. روش انجام اینکار در jQuery به صورت زیر است:
ابتدا یک المان div جدید را ایجاد کردهایم. سپس محتوای این div را با اطلاعات دریافتی از سرور مقدار دهی و در آخر آنرا به انتهای body اضافه میکنیم.
روش دریافت محتوای رشتهای HTML قابل ارسال به سرور نیز به صورت زیر است:
روش انجام اینکار با جاوا اسکریپت خالص به صورت زیر است:
در اینجا با استفاده از متد استاندارد createElement یک div جدید منقطع از DOM را ایجاد و سپس محتوای آنرا توسط خاصیت innerHTML به HTML دریافتی از سرور تنظیم کردهایم. در آخر این المان منقطع را توسط متد appendChild به انتهای document.body افزودهایم.
روش خواندن این محتوای نهایی نیز به صورت زیر است:
در حالت کار با جاوا اسکریپت خالص به خاصیت outerHTML یک المان نیز دسترسی داریم که خواندن و یا به روز رسانی آن، صرفا بر روی خود المان اصلی تاثیر میگذارد؛ اما innerHTML بر روی المانهای فرزند این المان (محتوای آن) تاثیر گذار است.
حرکت دادن المانها در صفحه
ابتدا قطعه کد HTML زیر را درنظر بگیرید:
<body> <h2>Flavors</h2> <ul class="flavors"> <li>chocolate</li> <li>strawberry</li> <li>vanilla</li> </ul> <h2>Types</h2> <ul class="types"> <li>frozen yogurt</li> <li>custard</li> <li>Italian ice</li> </ul> <ul class="unassigned"> <li>rocky road</li> <li>gelato</li> </ul> </body>
<body> <h2>Types</h2> <ul class="types"> <li>frozen yogurt</li> <li>Italian ice</li> <li>custard</li> <li>gelato</li> </ul> <h2>Flavors</h2> <ul class="flavors"> <li>chocolate</li> <li>vanilla</li> <li>rocky road</li> <li>strawberry</li> </ul> <ul class="unassigned"> </ul> </body>
حرکت دادن المانها توسط jQuery
var $flavors = $('.flavors'); var $chocolate = $flavors.find('li').eq(0); var $vanilla = $flavors.find('li').eq(2); $chocolate.after($vanilla);
در ادامه میخواهیم لیست types را به همراه عنوان آن، به قبل از لیست flavors منتقل کنیم:
var $typesHeading = $('h2').eq(1); $typesHeading.prependTo('body'); $typesHeading.after($('.types'));
سپس در لیست unassigned ابتدا rocky road آنرا یافته و به بالای strawberry در لیست flavors اضافه میکنیم. همچنین gelato آنرا نیز یافته و به انتهای لیست types اضافه خواهیم کرد:
var $unassigned = $('.unassigned'); var $rockyRoad = $unassigned.find('li').eq(0); var $gelato = $unassigned.find('li').eq(1); $vanilla.after($rockyRoad); $gelato.appendTo($('.types'));
حرکت دادن المانها توسط جاوا اسکریپت خالص (سازگار با IE 8.0 به بعد)
در ابتدا میخواهیم المان vanilla را به قبل از المان strawberry حرکت دهیم. برای اینکار میتوان از متد استاندارد insertBefore استفاده کرد:
var flavors = document.querySelector('.flavors'); var strawberry = flavors.children[1]; var vanilla = flavors.children[2]; flavors.insertBefore(vanilla, strawberry);
سپس کار انتقال عنوان لیست types و خود آن به قبل از لیست flavors صورت میگیرد:
var headings = document.querySelectorAll('h2'); var flavorsHeading = headings[0]; var typesHeading = headings[1]; var typesList = document.querySelector('.types'); document.body.insertBefore(typesHeading, flavorsHeading); document.body.insertBefore(typesList, flavorsHeading);
در آخر میخواهیم آیتمهای لیست unassigned را به لیستهای مرتبط با آنها انتقال دهیم:
flavors.insertBefore(document.querySelector('.unassigned > li'), strawberry); document.querySelector('.types').appendChild(document.querySelector('.unassigned > li'));
در سطر دوم، چون هم اکنون المان rocky road از لیست unassigned حذف شدهاست، متد querySelector فراخوانی شده، اولین عنصر لیست یا همان gelato را بازگشت میدهد. این المان را توسط متد appendChild به انتهای لیست types اضافه خواهیم کرد. متد appendChild نیز همانند متد insertBefore نیاز به یک والد دارد که همان عنصری است که قرار است المانها به آن افزوده شوند.
کپی کردن المانها
<ol class="numbers"> <li>one</li> <li>two</li> </ol>
// deep clone: return value is an exact copy $('.numbers').clone();
و در جاوا اسکریپت خالص (سازگار با IE 8.0 به بعد) برای کپی کردن المانها دو روش shallow و deep وجود دارد:
// shallow clone: return value is an empty <ol> document.querySelector('.numbers').cloneNode(); // deep clone: return value is an exact copy of the tree document.querySelector('.numbers').cloneNode(true);
باید دقت داشت که متد cloneNode آنچه را که مشاهده میکنید یا همان اصل markup را کپی میکند. بنابراین اگر از طریق جاوا اسکریپت تغییراتی را در آن شیء داده باشید در متد cloneNode لحاظ نمیشود.
بدیهی است المانهای clone شده تا زمانیکه با متدهایی مانند insertBefore و یا appendChild به صفحه اضافه نشوند، در صفحه نمایان نخواهند شد.
ایجاد و حذف المانها
فرض کنید میخواهیم به لیست flavors مثال ابتدای بحث، دو مورد جدید را اضافه کنیم.
روش افزودن المانهای جدید توسط جیکوئری:
var $flavors = $('.flavors'); // add two new flavors $('<li>pistachio</li>').appendTo($flavors); $('<li>neapolitan</li>').appendTo($flavors);
// remove the "gelato" type $('.types li:last').remove();
روش افزودن المانهای جدید توسط جاوا اسکریپت خالص:
var flavors = document.querySelector('.flavors'); // add two new flavors flavors.insertAdjacentHTML('beforeend', '<li>pistachio</li>') flavors.insertAdjacentHTML('beforeend', '<li>neapolitan</li>')
// remove the "gelato" type document.querySelector('.types li:last-child').remove();
روش دیگر انجام اینکار به صورت زیر توسط متد removeChild است:
var gelato = document.querySelector('.types li:last-child'); // remove the "gelato" type gelato.parentNode.removeChild(gelato);
کار با المانهای متنی
در جیکوئری متد ()text آن امکان دریافت محتوای متنی و همچنین به روز رسانی آنرا میسر میکند:
$('.types li').eq(1).text('italian ice');
در جاوا اسکریپت خالص، دو خاصیت textContent و همچنین innerText برای خواندن و یا به روز رسانی محتوای متنی عناصر مورد استفاده قرار میگیرند. برای مثال معادل قطعه کد جیکوئری فوق که از متد text استفاده میکند با جاوا اسکریپت خالص به صورت زیر است:
document.querySelectorAll('.types li')[1].textContent = 'italian ice';
خاصیت innerText هرچند بر روی اینترفیس HTMLElement تعریف شدهاست، اما جزء هیچکدام از استانداردهای وب نیست؛ ولی توسط تمام مرورگرهای امروزی پشتیبانی میشود. در این حالت به روز رسانی متن توسط آن با خاصیت textContent دقیقا یکی است؛ اما خروجی آن برعکس حالتهای قبل، متن رندر شدهی المانها را بازگشت میدهد. برای مثال در اینجا شامل فاصلههای پیش از این المانها در markup نمیشود.
برای مثال این قسمتی از خروجی خاصیت textContent است:
Flavors chocolate vanilla rocky road strawberry
Flavors chocolate vanilla rocky road strawberry
گاهی از اوقات از سرور قطعهای کد HTML ایی را دریافت میکنیم (که هنوز به صورت المان یا المانهای DOM در نیامدهاست) و در سمت کلاینت میخواهیم آنرا به قسمتی از صفحه اضافه کنیم. روش انجام اینکار در jQuery به صورت زیر است:
var container = '<h2>Containers</h2><ul><li>cone</li><li>cup</li></ul>'; $('<div>').html(container).appendTo('body');
روش دریافت محتوای رشتهای HTML قابل ارسال به سرور نیز به صورت زیر است:
var contents = $('body').html();
var div = document.createElement('div'); div.innerHTML = container; document.body.appendChild(div);
روش خواندن این محتوای نهایی نیز به صورت زیر است:
var contents = document.body.innerHTML;
بازخوردهای دوره
نگاهی به SignalR Hubs
با سلام و تشکر از آموزشهای روان شما
ممکن است دوستان در استفاده از این آموزش دچار اشکالی شوند که ناشی از بروز رسانی SignalR از ورژن 1 به 2 است.
در ورژنهای جدیدتر SignalR از Owin برای ارتباط خود استفاده میکند بنابراین در صورت استفاده از دستور
RouteTable.Routes.MapHubs();
در Application Start به خطا میخوریم جهت حل این مشکل ابتدا باید یک فایل OwinStartUp.cs به برنامه اضافه شود و به صورت کد زیر SignalR را مپ کنیم:
public void Configuration(IAppBuilder app) { app.MapSignalR(); }
همانطور که در قسمتهای قبل عنوان شد، دو نوع متداول تزریق وابستگیها وجود دارند:
الف) تزریق وابستگیها در سازنده کلاس
ب) تزریق وابستگیها در خواص عمومی کلاسها یا Setters injection
حالت الف متداولترین است و بیشتر زمانی کاربرد دارد که کار وهله سازی یک کلاس را میتوان راسا انجام داد. اما در فرمها یا یوزرکنترلهای ASP.NET Web forms به صورت پیش فرض کار وهله سازی فرمها و یوزرکنترلها توسط موتور ASP.NET انجام میشود و در این حالت اگر بخواهیم از تزریق وابستگیها استفاده کنیم، مدام به همان روش معروف Service locator و استفاده از container.Resolve در تمام قسمتهای برنامه میرسیم که آنچنان روش مطلوبی نیست.
اما ... در ASP.NET Web forms میتوان وهله سازی فرمها را نیز تحت کنترل قرار داد، که برای آن دو روش زیر وجود دارند:
الف) یک کلاس مشتق شده را از کلاس پایه PageHandlerFactory تهیه کنیم. این کلاس را پیاده سازی کرده و نهایتا بجای وهله ساز پیش فرض فرمهای موتور داخلی ASP.NET، در فایل وب کانفیگ برنامه استفاده کنیم. یک نمونه از پیاده سازی آنرا در اینجا میتوانید مشاهده کنید.
مشکلی که این روش دارد سازگاری آن با حالت Full trust است. یعنی برنامه شما در یک هاست Medium trust (اغلب هاستهای خوب) اجرا نخواهد شد.
ب) روش دوم، استفاده از یک Http Module است برای اعمال Setter injectionها، به صورت خودکار. اکنون که حالت الف را همه جا نمیتوان بکار برد یا به عبارتی نمیتوان وهله سازی فرمها را راسا در دست گرفت، حداقل میتوان خواص عمومی اشیاء صفحه تولید شده را مقدار دهی کرد که در ادامه، این روش را بررسی میکنیم.
تهیه ماژول انجام Setters injection به صورت خودکار در برنامههای ASP.NET Web forms به کمک StructureMap
پیشنیاز این بحث، مطلب «استفاده از StructureMap به عنوان یک IoC Container» میباشد که پیشتر مطالعه کردید (در حد نحوه نصب StructureMap و آشنایی با تنظیمات اولیه آن)
در این ماژول، کار با HttpContext.Current.Handler شروع میشود که دقیقا معادل با وهلهای از یک صفحه یا فرم میباشد. اکنون که این وهله را داریم، فقط کافی است متد ObjectFactory.BuildUp مربوط به StructureMap را روی آن فراخوانی کنیم تا کار Setter injection را انجام دهد. مرحله بعد یافتن یوزر کنترلهای احتمالی قرار گرفته بر روی صفحه و همچنین فراخوانی متد ObjectFactory.BuildUp، بر روی آنها میباشد.
پس از تهیه ماژول فوق، باید آنرا در فایل وب کانفیگ برنامه معرفی کرد:
مثالی از نحوه استفاده از StructureMapModule تهیه شده
فرض کنید لایه سرویس برنامه دارای اینترفیسها و کلاسهای زیر است:
کار تنظیمات اولیه آنها را در فایل global.asax.cs برنامه انجام خواهیم داد:
در اینجا فقط باید دقت داشت که ذکر SetAllProperties الزامی است. از این جهت که از روش Setter injection در حال استفاده هستیم.
مرحله آخر هم استفاده از سرویسهای برنامه به شکل زیر است:
همانطور که ملاحظه میکنید در این فرم، هیچ خبری از وجود IoC Container مورد استفاده نیست و کار وهله سازی و مقدار دهی سرویس مورد استفاده به صورت خودکار توسط Http Module تهیه شده انجام میشود.
دریافت مثال کامل قسمت جاری
DI05.zip
یک نکتهی تکمیلی
برای ارتقاء نکات مطلب جاری به نگارش سوم StructureMap نیاز است موارد ذیل را لحاظ کنید:
الف) نصب بستهی وب آن
ب) ReleaseAndDisposeAllHttpScopedObjects حذف شده را به متد جدید ()HttpContextLifecycle.DisposeAndClearAll تغییر دهید.
ج) x.SetAllProperties را به x.Policies.SetAllProperties ویرایش کنید.
الف) تزریق وابستگیها در سازنده کلاس
ب) تزریق وابستگیها در خواص عمومی کلاسها یا Setters injection
حالت الف متداولترین است و بیشتر زمانی کاربرد دارد که کار وهله سازی یک کلاس را میتوان راسا انجام داد. اما در فرمها یا یوزرکنترلهای ASP.NET Web forms به صورت پیش فرض کار وهله سازی فرمها و یوزرکنترلها توسط موتور ASP.NET انجام میشود و در این حالت اگر بخواهیم از تزریق وابستگیها استفاده کنیم، مدام به همان روش معروف Service locator و استفاده از container.Resolve در تمام قسمتهای برنامه میرسیم که آنچنان روش مطلوبی نیست.
اما ... در ASP.NET Web forms میتوان وهله سازی فرمها را نیز تحت کنترل قرار داد، که برای آن دو روش زیر وجود دارند:
الف) یک کلاس مشتق شده را از کلاس پایه PageHandlerFactory تهیه کنیم. این کلاس را پیاده سازی کرده و نهایتا بجای وهله ساز پیش فرض فرمهای موتور داخلی ASP.NET، در فایل وب کانفیگ برنامه استفاده کنیم. یک نمونه از پیاده سازی آنرا در اینجا میتوانید مشاهده کنید.
مشکلی که این روش دارد سازگاری آن با حالت Full trust است. یعنی برنامه شما در یک هاست Medium trust (اغلب هاستهای خوب) اجرا نخواهد شد.
ب) روش دوم، استفاده از یک Http Module است برای اعمال Setter injectionها، به صورت خودکار. اکنون که حالت الف را همه جا نمیتوان بکار برد یا به عبارتی نمیتوان وهله سازی فرمها را راسا در دست گرفت، حداقل میتوان خواص عمومی اشیاء صفحه تولید شده را مقدار دهی کرد که در ادامه، این روش را بررسی میکنیم.
تهیه ماژول انجام Setters injection به صورت خودکار در برنامههای ASP.NET Web forms به کمک StructureMap
پیشنیاز این بحث، مطلب «استفاده از StructureMap به عنوان یک IoC Container» میباشد که پیشتر مطالعه کردید (در حد نحوه نصب StructureMap و آشنایی با تنظیمات اولیه آن)
using System.Collections; using System.Web; using System.Web.UI; using StructureMap; namespace DI05.Core { /// <summary> /// تسهیل در کار تزریق خودکار وابستگیها در سطح فرمها و یوزرکنترلها /// </summary> public class StructureMapModule : IHttpModule { public void Dispose() { } public void Init(HttpApplication app) { app.PreRequestHandlerExecute += (sender, e) => { var page = HttpContext.Current.Handler as Page; // The Page handler if (page == null) return; WireUpThePage(page); WireUpAllUserControls(page); }; } private static void WireUpAllUserControls(Page page) { // در اینجا هم کار سیم کشی یوزر کنترلها انجام میشود page.InitComplete += (initSender, evt) => { var thisPage = (Page)initSender; foreach (Control ctrl in getControlTree(thisPage)) { // فقط یوزر کنترلها بررسی شدند // اگر نیاز است سایر کنترلهای قرار گرفته روی فرم هم بررسی شوند شرط را حذف کنید if (ctrl is UserControl) { ObjectFactory.BuildUp(ctrl); } } }; } private static void WireUpThePage(Page page) { ObjectFactory.BuildUp(page); // برقراری خودکار سیم کشیها در سطح صفحات } private static IEnumerable getControlTree(Control root) { foreach (Control child in root.Controls) { yield return child; foreach (Control ctrl in getControlTree(child)) { yield return ctrl; } } } } }
پس از تهیه ماژول فوق، باید آنرا در فایل وب کانفیگ برنامه معرفی کرد:
<?xml version="1.0"?> <configuration> <system.web> <compilation debug="true" targetFramework="4.0" /> <httpModules> <add name="StructureMapModule" type="DI05.Core.StructureMapModule"/> </httpModules> </system.web> <system.webServer> <modules runAllManagedModulesForAllRequests="true"> <add name="StructureMapModule" type="DI05.Core.StructureMapModule"/> </modules> <validation validateIntegratedModeConfiguration="false" /> </system.webServer> </configuration>
مثالی از نحوه استفاده از StructureMapModule تهیه شده
فرض کنید لایه سرویس برنامه دارای اینترفیسها و کلاسهای زیر است:
namespace DI05.Services { public interface IUsersService { string GetUserEmail(int id); } } namespace DI05.Services { public class UsersService: IUsersService { public string GetUserEmail(int id) { //فقط جهت بررسی تزریق وابستگیها return "test@test.com"; } } }
using System; using StructureMap; using DI05.Services; namespace DI05 { public class Global : System.Web.HttpApplication { private static void initStructureMap() { ObjectFactory.Initialize(x => { x.For<IUsersService>().Use<UsersService>(); x.SetAllProperties(y => { y.OfType<IUsersService>(); }); }); } protected void Application_Start(object sender, EventArgs e) { initStructureMap(); } void Application_EndRequest(object sender, EventArgs e) { ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects(); } } }
مرحله آخر هم استفاده از سرویسهای برنامه به شکل زیر است:
using System; using DI05.Services; namespace DI05 { public partial class Default : System.Web.UI.Page { public IUsersService UsersService { set; get; } protected void Page_Load(object sender, EventArgs e) { lblEmail1.Text = string.Format("From Default Page: {0}", UsersService.GetUserEmail(1)); } } }
دریافت مثال کامل قسمت جاری
DI05.zip
یک نکتهی تکمیلی
برای ارتقاء نکات مطلب جاری به نگارش سوم StructureMap نیاز است موارد ذیل را لحاظ کنید:
الف) نصب بستهی وب آن
PM> Install-Package structuremap.web
ج) x.SetAllProperties را به x.Policies.SetAllProperties ویرایش کنید.
نظرات مطالب
ساخت ربات تلگرامی با #C
سلام و با سپاس مجدد
SSL سایت از طریق cloudflare دریافت کردم
دو تا سوال داشتم
1- با توضیحات شما احتیاجی به اجرای دستور netsh بر روی سرور نیست. ولی وقتی میخوام روی visual studio برنامه رو اجرا کنم احتیاجی به دستور netsh هست ممنون میشم یه نمونه بنویسید
2- جایگزین عبارت "https://+:8443" برای اجرای برنامه با visual studio و همین طور وقتی قرار کد تو سرور قرار بدیم
ممنون میشم علاوه بر توضیح برای هر دو یه نمونه هم بنویسید
SSL سایت از طریق cloudflare دریافت کردم
دو تا سوال داشتم
1- با توضیحات شما احتیاجی به اجرای دستور netsh بر روی سرور نیست. ولی وقتی میخوام روی visual studio برنامه رو اجرا کنم احتیاجی به دستور netsh هست ممنون میشم یه نمونه بنویسید
2- جایگزین عبارت "https://+:8443" برای اجرای برنامه با visual studio و همین طور وقتی قرار کد تو سرور قرار بدیم
using (WebApp. Start<Startup> ( "https://+:8443" ) )
در کنار کتابخانه elmah که وظیفه ثبت تمامی خطاهای برنامه را دارد کتابخانه MiniProfiler امکان یافتن مشکلات کارایی و تنگناهای وب سایت را در اختیارمان قرار میدهد. دو قابلیت عمده که این ابزار فراهم مینمایید
در ASP.NET MVC در صفحه Layout_ قبل از بسته شدن تگ body تابع RenderIncludes را مانند زیر صدا بزنید تا در همه صفحات نمایش داده شود
در کلاس global کد زیر را برای اجرای MiniProfiler اضافه نمایید
یا کتابخانه MiniProfiler.MVC را از nuget دریافت نمایید
با اضافه شدن این کتابخانه همه پیکربندی بصورت صورت خودکار انجام میگیرد. حال وب سایت را اجرا کنید در بالای صفحه مانند شکل زیر مدت زمان بارگذاری صفحه نمایش داده میشود که با کلیک بر روی آن اطلاعات بیشتری را مشاهده مینمایید
- امکان مشاهده و بررسی کوئریهای خام ADO.NET از قبیل SQL Server,Oracle و LINQ-to-SQL و EF/First Code و...
- نمایش زمان اجرای عملی صفحات
برای استفاده از این ابزار کافیست تا آن را از nuget دریافت نمایید
PM> Install-Package MiniProfiler
@using StackExchange.Profiling; <head> .. </head> <body> ... @MiniProfiler.RenderIncludes() </body>
protected void Application_BeginRequest() { if (Request.IsLocal) { MiniProfiler.Start(); } } protected void Application_EndRequest() { MiniProfiler.Stop(); }
برای پیکربندی MiniProfiler در web.config کد زیر را اضافه نمایید
<system.webServer> ... <handlers> <add name="MiniProfiler" path="mini-profiler-resources/*" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" /> </handlers> </system.webServer>
PM> Install-Package MiniProfiler.MVC
اگر در اکشن اجرا شده کوئری اجرا شد باشد ستونی به نام query times نمایش داده میشود که تعداد کوئریها و مدت زمان آن را نمایش میدهد
حال بر روی گزینه sql کلیک کنید که صفحه دیگری باز شود و کوئری خام آن را مشاهد نمایید اگر کوئری تکرار شده باشد در کنار آن با DUPLICATE متمایز شده است
برای مشاهده کوئریهای Entity Framework/First Code کتابخانه MiniProfiler.EF را اضافه نمایید
PM> Install-Package MiniProfiler.EF
اگر بصورت دستی MiniProfiler را پیکربندی کرده باشید میبایست در Application_Start دستور زیر را اجرا نمایید
protected void Application_BeginRequest() { if (Request.IsLocal) { MiniProfiler.Start(); MiniProfilerEF.Initialize(); } }
در حالت پبشرفتهتر اگر قصد داشته باشید زمان یک قطعه کد را جداگانه محاسبه نمایید بصورت زیر عمل نمایید
public ActionResult Index() { var profiler = MiniProfiler.Current; using (profiler.Step("Step 1")) { //code 1 } using (profiler.Step("Step 2")) { //code 2 } return View(); }
با این کار زمان هر step را بصورت جداگانه محاسبه مینماید. در ASP.NET Webforms دقیقا به همین صورت استفاده میشود فقط کافیست در masterpage اصلی یا اگر از masterpage استفاده نمیکنیم در صفحه مورد نظر تابع RenderIncludes را بصورت زیر صدا بزنیم
<%= StackExchange.Profiling.MiniProfiler.RenderIncludes() %>
امیدوارم مفید واقع شده باشد.
مطالب
بررسی اجمالی Redis
نام Redis از Remote Dictionary server گرفته شدهاست. Redis یکی از محبوبترین key-value storeها میباشد و هم چنین توسط برندهای بزرگ IT جهان استفاده میشود. لازم به ذکر است Amazon Elastic Cache از Redis پشتیبانی میکند. Redis یک دیتابیس No SQL است و بر روی مفهوم زوج کلید-مقدار (key-value ) کار میکند. key-value store امکانی را برای ذخیره دادهها که Value نامیده میشود، در یک Key فراهم میکند. شما میتوانید بعدا این دادهها را دریافت کنید، تنها اگر نام دقیق کلیدی را که برای ذخیره داده استفاده کردهاید، بدانید.
What Is In-Memory, Key-Value Store
Key-Value store یک سیستم ذخیره سازی است؛ جایی که دادهها به صورت زوج کلید-مقدار ذخیره میشوند. وقتی که میگوییم in-memory key-value store (زوج کلید-مقدار مقیم در حافظه)، منظور این است که زوج کلید-مقدار در حافظه اصلی RAM ذخیره میشوند. بنابراین میتوانیم بگوییم Redis دادهها را در حافظه به شکل زوج کلید-مقدار ذخیره کرده است.
در Redis کلیدها باید string باشند؛ ولی value ها میتوانند یک string ، list ، set ، sorted set یا hash باشند.
Advantage And Disadvantage of Redis over DBMS
Database Management systems همه چیز را در حافظه ثانویه ذخیره میکند که باعث میشود خواندن و نوشتن عملیات، تا اندازهای کند باشد. این در حالی است که Redis همه چیز را در حافظه اصلی ذخیره میکند و همین موضوع باعث میشود که خواندن و نوشتن دادهها توسط آن خیلی سریع باشند.
حافظه اصلی محدود است. بنابراین Redis نمیتواند فایلهای بزرگ یا binary data را ذخیره کند و تنها اطلاعات متنی کوچک را ذخیره میکند که نیاز است قابل دسترسی و اصلاح باشند و با نرخ خیلی سریعی قابل درج باشند. اگر تلاش کنیم که دادههای بیشتری را نسبت به حافظه موجود بنویسیم، در این حالت خطا دریافت خواهیم کرد.
Redis | RDBMS |
Redis همه چیز را در حافظه اصلی ذخیره میکند. | RDBMS همه چیز را در حافظه ثانویه ذخیره میکند. |
در Redis بخاطر ذخیره سازی دادهها در حافظه اصلی، خواندن و نوشتن عملیات به شدت سریع میباشد. | در RDBMS بخاطر ذخیره سازی دادهها در حافظه ثانویه، خواندن و نوشتن عملیات کند است. |
حافظه اصلی از نظر size کوچکتر و از لحاظ قیمت نسبت به حافظه ثانویه گرانتر میباشد. Redis نمیتواند دادههای بزرگ یا binary data را ذخیره کند. | حافظه ثانویه از نظر size بزرگتر و از لحاظ قیمت نسبت به حافظه اصلی ارزانتر میباشد. RDBMS به آسانی میتواند با انواع فایلها کار کند. |
Redis Advantages
- Redis : Exceptionally fast خیلی سریع است و میتواند حدود 110000 ، SET و 81000 ، GET را به ازای هر ثانیه انجام دهد.
- Redis : Supports rich data type بیشتر دیتا تایپها را که توسعه دهندگان قبلا آنها را شناختهاند، پشتیبانی میکند؛ از قبیل string ، list ، set ، sorted set یا hash .
- Operations are atomic : تمام عملیات Redis اتمیک میباشند که این اطمینان خاطر را میدهد اگر دو کلاینت به صورت همزمان به آن دسترسی داشته باشند، Redis server مقدار update شده را دریافت خواهد کرد.
- Redis : Multi-utility tool یک ابزار چند منظوره است که میتواند در برخی از سناریوها استفاده شود از قبیل: Redis ) messaging-queues , caching به صورت بومی از Publish/Subscribe پشتیبانی میکند ) , هر داده ای با طول عمر کوتاه در Application مانند web application sessions , ... .
Redis Single Instance Architecture
معماری Redis شامل دو پروسه اصلی است:
1- Redis client
2- Redis Server
Redis client و Redis Server هر دو میتوانند در یک کامپیوتر یا کامپیوترهای متفاوت باشند. Redis server مسئول ذخیره سازی دادهها در حافظه میباشد. همانطور که متوجه هستیم، Redis همه چیز را در حافظه اصلی ذخیره میکند و حافظه اصلی فرار است؛ از این رو زمانیکه Redis server یا کامپیوتر را راه اندازی مجدد (restart) میکنیم، همه دادههای ذخیره شده را از دست خواهیم داد. بنابراین نیازمند یک راهحل، جهت ماندگاری datastore میباشیم.
Redis Persistance
سه راه متفاوت وجود دارد که Redis را پایدار میکند : RDB ، AOF و دستور SAVE
1- RDB : RDB Mechanism یک نمونه از تمام دادههای در حافظه را تهیه و آنها را در حافظه ثانویه ذخیره میکند (ذخیره سازی ماندگار) که در یک وقفه مشخص اتفاق میافتد. بنابراین این شانس وجود دارد که شما دادههایی را از دست بدهید که بعد از آخرین Set , RDB’s snapshot شدهاند .
2-AOF : AOF همه عملیات نوشتن دریافت شده توسط سرور را ثبت میکند. بنابراین همه چیز پایدار است. مشکل استفاده از AOF این است که برای هر عملیات، شروع به نوشتن در دیسک میکند و این یک کار هزینهبر است و هم چنین اندازه فایل AOF بزرگتر از RDB میباشد.
3-SAVE Command : شما میتوانید Redis server را مجبور کنید که یک RDB snapshot را ایجاد کند؛ هر زمانکه Redis console client از دستور SAVE استفاده میکند.
در ضمن میتوانید از AOF و RDB با هم استفاده کنید تا بهترین نتیجه ماندگاری را داشته باشید.
Redis Replication
Replication یک تکنیک است که کامپیوترها را درگیر میکند تا دسترسی پذیری دادهها و تحمل خطا را با ضریب بیشتری امکان پذیر کنند. در یک محیط Replication، کامپیوترها، دادههای یکسانی را با یکدیگر به اشتراک میگذارند؛ حتی اگر چندین کامپیوتر دچار مشکل شوند، باز هم، همه دادهها در دسترس خواهند بود که به صورت Master/Slaves میباشند.
تمام slaveها شامل دادههای یکسانی همانند master میباشند. وقتیکه یک slave جدید در محیط Replication ایجاد میشود، master به صورت خودکار همه دادهها را با sync ، slave میکند.
تمام Query ها به سرور master هدایت میشوند و سپس سرور master عملیات را اجرا میکند. وقتیکه یک عملیات نوشتن اتفاق میافتد، سرور master دادههایی را که بهتازگی نوشته شدهاند، در تمام slaveها تکثیر میکند.
اگر اتفاقی در سرور master رخ دهد، تمام دادهها از بین میروند؛ در این حالت باید یک slave را به master تبدیل کنیم.
Clustering In Redis
Clustering یک تکنیک میباشد که توسط آن میتوان دادهها را در چندین کامپیوتر تقسیم بندی کرد. فرض کنید که یک سرور Redis را با 64GB حافظه در اختیار داریم. در این حالت میتوانیم 64GB داده داشته باشیم. اگر 10 تا clustered computer را که هر کدام 64GB حافظه اصلی دارند، داشته باشیم، در این حالت میتوان 640GB داده را ذخیره کرد.
در تصویر بالا میتوانیم ببینیم که دادهها در چهار node، ذخیره شدهاند. هر node یک Redis Server پیکربندی شده میباشد؛ به عنوان یک cluster node. اگر یکی از node ها دچار مشکل شوند، سپس کل cluster متوقف میشود.
Redis Client
وب سایت Try Redis ، یک Redis console client آنلاین است و به شما کمک میکند تا یاد بگیرید چگونه از Redis console client استفاده کنید.
در قسمت بعد در رابطه با نصب Redis بر روی سیستم عامل ویندوز و دیتا تایپها در Redis صحبت خواهیم کرد.
مطالب
OpenCVSharp #16
در قسمت قبل با نحوهی استفاده از یک trained data از پیش آماده شدهی تشخیص چهره، آشنا شدیم. در این قسمت قصد داریم با نحوهی تولید این فایلهای XML آشنا شویم و یک تشخیص دهندهی سفارشی را طراحی کنیم.
طراحی classifier سفارشی تشخیص خودروها
برای طراحی یک تشخیص دهندهی سفارشی مبتنی بر الگوریتمهای Machine learning، نیاز به تعداد زیادی تصویر داریم. در اینجا از بانک تصاویر خودروهای «UIUC Image Database for Car Detection» استفاده خواهیم کرد. در این بسته، یک سری تصویر positive و negative را میتوان ملاحظه کرد. تصاویر مثبت، تصاویر انواع و اقسام خودروها هستند (550 عدد) و تصاویر منفی، تصاویر غیر خودرویی (500 عدد)؛ یا به عبارتی، هر تصویری، منهای تصاویر خودرو میتواند تصویر منفی باشد.
ایجاد فایل برداری از تصاویر خودروها
در ادامه یک فایل متنی را به نام carImages.txt ایجاد میکنیم. هر سطر این فایل چنین فرمتی را خواهد داشت:
ابتدا مسیر تصویر مشخص میشود. سپس عدد 1 به این معنا است که در این تصویر فقط یک عدد خودرو وجود دارد. 4 عدد بعدی، ابعاد مستطیلی تصویر هستند.
در ادامه فایل متنی دیگری را به نام negativeImages.txt جهت درج اطلاعات تصاویر منفی، ایجاد میکنیم. اینبار هر سطر این فایل تنها حاوی مسیر تصویر مدنظر است:
این دو فایل را میتوان با استفاده از دو متد ذیل، به سرعت تولید کرد:
برای کامپایل اطلاعات فایلهای تولید شده، نیاز به فایل opencv_createsamples.exe است. این فایل را در پوشهی opencv\build\x86\vc12\bin بستهی اصلی OpenCV میتوانید پیدا کنید.
پارامترهای این دستور شامل سوئیچ info است؛ به معنای مشخص سازی فایل اطلاعات تصاویر مثبت. سوئیچ num تعداد تصاویر آنرا تعیین میکند و سوئیچهای w و h، طول و عرض تصاویر هستند. سوئیچ vec نیز جهت تولید یک فایل vector از این اطلاعات بکار میرود.
پس از اجرای این دستور، فایل cars.vec تولید خواهد شد؛ با این خروجی:
اگر علاقمند هستید که محتویات فایل باینری cars.vec را مشاهده کنید، دستور ذیل را صادر نمائید:
در این پنجرهی باز شده، تصاویر بعدی و قبلی را میتوان با دکمههای arrow صفحه کلید، مشاهده کرد.
تبدیل فایل برداری تصاویر خودروها به trained data
تا اینجا موفق شدیم بیش از 500 تصویر خودرو را تبدیل به یک فایل برداری سازگار با OpenCV کنیم. اکنون نیاز است، این اطلاعات پردازش شده و trained data مخصوص الگوریتمهای machine learning تولید شود. اینکار را توسط برنامهی opencv_traincascade.exe انجام خواهیم داد. این فایل نیز در پوشهی opencv\build\x86\vc12\bin بستهی اصلی OpenCV موجود است.
دستور ذیل در پوشهی data، بر اساس اطلاعات برداری cars.vec و همچنین تصاویر منفی مشخص شدهی در فایل negativeImages.txt، با تعداد هر کدام 500 عدد (این عدد را توصیه شدهاست که اندکی کمتر از تعداد max موجود مشخص کنیم) و تعداد مراحل 2 (هر چقدر این تعداد مراحل بیشتر باشد، فایل نهایی تولید شده دقت بالاتری خواهد داشت؛ اما تولید آن به زمان بیشتری نیاز دارد) اجرا میشود. در اینجا featureType به LBP یا Local binary Pattern، تنظیم شدهاست. این الگوریتم از Haar cascade سریعتر است.
خروجی اجرای این دستور را میتوانید در پوشهی data با نام cascade.xml، پیدا کنید. پس از آن، روش استفادهی از این فایل، با مطلب تشخیص چهره تفاوتی ندارد.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
طراحی classifier سفارشی تشخیص خودروها
برای طراحی یک تشخیص دهندهی سفارشی مبتنی بر الگوریتمهای Machine learning، نیاز به تعداد زیادی تصویر داریم. در اینجا از بانک تصاویر خودروهای «UIUC Image Database for Car Detection» استفاده خواهیم کرد. در این بسته، یک سری تصویر positive و negative را میتوان ملاحظه کرد. تصاویر مثبت، تصاویر انواع و اقسام خودروها هستند (550 عدد) و تصاویر منفی، تصاویر غیر خودرویی (500 عدد)؛ یا به عبارتی، هر تصویری، منهای تصاویر خودرو میتواند تصویر منفی باشد.
ایجاد فایل برداری از تصاویر خودروها
در ادامه یک فایل متنی را به نام carImages.txt ایجاد میکنیم. هر سطر این فایل چنین فرمتی را خواهد داشت:
pos/pos-177.pgm 1 0 0 100 40
در ادامه فایل متنی دیگری را به نام negativeImages.txt جهت درج اطلاعات تصاویر منفی، ایجاد میکنیم. اینبار هر سطر این فایل تنها حاوی مسیر تصویر مدنظر است:
neg/neg-274.pgm
private static void createCarImagesFile() { var sb = new StringBuilder(); foreach (var file in new DirectoryInfo(@"..\..\CarData\CarData\TrainImages").GetFiles("*pos-*.pgm")) { sb.AppendFormat("{0} {1} {2} {3} {4} {5}{6}", file.FullName, 1, 0, 0, 100, 40, Environment.NewLine); } File.WriteAllText(@"..\..\CarsInfo\carImages.txt", sb.ToString()); } private static void createNegativeImagesFile() { var sb = new StringBuilder(); foreach (var file in new DirectoryInfo(@"..\..\CarData\CarData\TrainImages").GetFiles("*neg-*.pgm")) { sb.AppendFormat("{0}{1}", file.FullName,Environment.NewLine); } File.WriteAllText(@"..\..\CarsInfo\negativeImages.txt", sb.ToString()); }
opencv_createsamples.exe -info carImages.txt -num 550 -w 48 -h 24 -vec cars.vec
پس از اجرای این دستور، فایل cars.vec تولید خواهد شد؛ با این خروجی:
Info file name: carImages.txt Img file name: (NULL) Vec file name: cars.vec BG file name: (NULL) Num: 550 BG color: 0 BG threshold: 80 Invert: FALSE Max intensity deviation: 40 Max x angle: 1.1 Max y angle: 1.1 Max z angle: 0.5 Show samples: FALSE Original image will be scaled to: Width: $backgroundWidth / 48 Height: $backgroundHeight / 24 Create training samples from images collection... Done. Created 550 samples
"c:\opencv\build\x86\vc12\bin\opencv_createsamples.exe" -vec cars.vec -w 48 -h 24
در این پنجرهی باز شده، تصاویر بعدی و قبلی را میتوان با دکمههای arrow صفحه کلید، مشاهده کرد.
تبدیل فایل برداری تصاویر خودروها به trained data
تا اینجا موفق شدیم بیش از 500 تصویر خودرو را تبدیل به یک فایل برداری سازگار با OpenCV کنیم. اکنون نیاز است، این اطلاعات پردازش شده و trained data مخصوص الگوریتمهای machine learning تولید شود. اینکار را توسط برنامهی opencv_traincascade.exe انجام خواهیم داد. این فایل نیز در پوشهی opencv\build\x86\vc12\bin بستهی اصلی OpenCV موجود است.
دستور ذیل در پوشهی data، بر اساس اطلاعات برداری cars.vec و همچنین تصاویر منفی مشخص شدهی در فایل negativeImages.txt، با تعداد هر کدام 500 عدد (این عدد را توصیه شدهاست که اندکی کمتر از تعداد max موجود مشخص کنیم) و تعداد مراحل 2 (هر چقدر این تعداد مراحل بیشتر باشد، فایل نهایی تولید شده دقت بالاتری خواهد داشت؛ اما تولید آن به زمان بیشتری نیاز دارد) اجرا میشود. در اینجا featureType به LBP یا Local binary Pattern، تنظیم شدهاست. این الگوریتم از Haar cascade سریعتر است.
"E:\opencv\bin\opencv_traincascade.exe" -data data -vec cars.vec -bg negativeImages.txt -numPos 500 -numNeg 500 -numStages 2 -w 48 -h 24 -featureType LBP
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.