مطالب
ASP.NET MVC #2

MVC‌ چیست و اساس کار آن چگونه است؟

الگوی MVC در سال‌های اول دهه 70 میلادی در شرکت زیراکس توسط خالقین زبان اسمال‌تاک که جزو اولین زبان‌های شیءگرا محسوب می‌شود، ارائه گردید. نام MVC از الگوی Model-View-Controller گرفته شده و چندین دهه است که در صنعت تولید نرم افزار مورد استفاده می‌باشد. هدف اصلی آن جدا سازی مسئولیت‌های اجزای تشکیل دهنده «لایه نمایشی» برنامه است.
این الگو در سال 2004 برای اولین بار در سکویی به نام Rails به کمک زبان روبی جهت ساخت یک فریم ورک وب MVC مورد استفاده قرار گرفت و پس از آن به سایر سکوها مانند جاوا، دات نت (در سال 2007)، PHP و غیره راه یافت.
تصاویری را از این تاریخچه در ادامه ملاحظه می‌کنید؛ از دکتر Trygve Reenskaug تا شرکت زیراکس و معرفی آن در Rails به عنوان اولین فریم ورک وب MVC.


C در MVC معادل Controller است. کنترلر قسمتی است که کار دریافت ورودی‌های دنیای خارج را به عهده دارد؛ مانند پردازش یک درخواست HTTP ورودی.


زمانیکه کنترلر این درخواست را دریافت می‌کند، کار وهله سازی Model را عهده دار خواهد شد و حاوی اطلاعاتی است که نهایتا در اختیار کاربر قرار خواهد گرفت تا فرآیند پردازش درخواست رسیده را تکمیل نماید. برای مثال اگر کاربری جهت دریافت آخرین اخبار به سایت شما مراجعه کرده است،‌ در اینجا کار تهیه لیست اخبار بر اساس مدل مرتبط به آن صورت خواهد گرفت. بنابراین کنترلرها، پایه اصلی و مدیر ارکستر الگوی MVC محسوب می‌شوند.
در ادامه، کنترلر یک View را جهت نمایش Model انتخاب خواهد کرد. View در الگوی MVC یک شیء ساده است. به آن می‌توان به شکل یک قالب که اطلاعاتی را از Model دریافت نموده و سپس آن‌ها را در مکان‌های مناسبی در صفحه قرار می‌دهد، نگاه کرد.
نتیجه استفاده از این الگو، ایزوله سازی سه جزء یاد شده از یکدیگر است. برای مثال View نمی‌داند و نیازی ندارد که بداند چگونه باید از لایه دسترسی به اطلاعات کوئری بگیرد. یا برای مثال کنترلر نیازی ندارد بداند که چگونه و در کجا باید خطایی را با رنگی مشخص نمایش دهد. به این ترتیب انجام تغییرات در لایه رابط کاربری برنامه در طول توسعه کلی سیستم، ساده‌تر خواهد شد.
همچنین در اینجا باید اشاره کرد که این الگو مشخص نمی‌کند که از چه نوع فناوری دسترسی به اطلاعاتی باید استفاده شود. می‌توان از بانک‌های اطلاعاتی، وب سرویس‌ها، صف‌ها و یا هر نوع دیگری از اطلاعات استفاده کرد. به علاوه در اینجا در مورد نحوه طراحی Model نیز قیدی قرار داده نشده است. این الگو تنها جهت ساخت بهتر و اصولی «رابط کاربری» طراحی شده است و بس.



تفاوت مهم پردازشی ASP.NET MVC با ASP.NET Web forms

اگر پیشتر با ASP.NET Web forms کار کرده باشید اکنون شاید این سؤال برایتان وجود داشته باشد که این سیستم جدید در مقایسه با نمونه قبلی، چگونه درخواست‌ها را پردازش می‌کند.


همانطور که مشاهده می‌کنید، در وب فرم‌ها زمانیکه درخواستی دریافت می‌شود، این درخواست به یک فایل موجود در سیستم مثلا default.aspx ارسال می‌گردد. سپس ASP.NET یک کلاس وهله سازی شده معرف آن صفحه را ایجاد کرده و آن‌را اجرا می‌کند. در اینجا چرخه طول عمر صفحه مانند page_load و غیره رخ خواهد داد. جهت انجام این وهله سازی، View به فایل code behind خود گره خورده است و جدا سازی خاصی بین این دو وجود ندارد. منطق صفحه به markup آن که معادل است با یک فایل فیزیکی بر روی سیستم، کاملا مقید است. در ادامه، این پردازش صورت گرفته و HTML نهایی تولیدی به مرورگر کاربر ارسال خواهد شد.
در ASP.NET MVC این نحوه پردازش تغییر کرده است. در اینجا ابتدا درخواست رسیده به یک کنترلر هدایت می‌شود و این کنترلر چیزی نیست جز یک کلاس مجزا و مستقل از هر نوع فایل ASPX ایی در سیستم. سپس این کنترلر کار پردازش درخواست رسیده را شروع کرده، اطلاعات مورد نیاز را جمع آوری و سپس به View ایی که انتخاب می‌کند، جهت نمایش نهایی ارسال خواهد کرد. در اینجا View این اطلاعات را دریافت کرده و نهایتا در اختیار کاربر قرار خواهد داد.



آشنایی با قرارداد یافتن کنترلرهای مرتبط

تا اینجا دریافتیم که نحوه پردازش درخواست‌ها در ASP.NET MVC بر مبنای کلاس‌ها و متدها است و نه بر مبنای فایل‌های فیزیکی موجود در سیستم. اگر درخواستی به سیستم ارسال می‌شود، در ابتدا، این درخواست جهت پردازش، به یک متد عمومی موجود در یک کلاس کنترلر هدایت خواهد شد و نه به یک فایل فیزیکی ASPX (برخلاف وب فرم‌ها).


همانطور که در تصویر مشاهده می‌کنید، در ابتدای پردازش یک درخواست، آدرسی به سیستم ارسال خواهد شد. بر مبنای این آدرس، نام کنترلر که در اینجا زیر آن خط قرمز کشیده شده است، استخراج می‌گردد (برای مثال در اینجا نام این کنترلرProducts است). سپس فریم ورک به دنبال کلاس این کنترلر خواهد گشت. اگر آن‌را در اسمبلی پروژه بیابد، از آن خواهد خواست تا درخواست رسیده را پردازش کند؛ در غیراینصورت پیغام 404 یا یافت نشد، به کاربر نمایش داده می‌شود.
اما فریم ورک چگونه این کلاس کنترلر درخواستی را پیدا می‌کند؟
در زمان اجرا، اسمبلی اصلی پروژه به همراه تمام اسمبلی‌هایی که به آن ارجاعی دارند جهت یافتن کلاسی با این مشخصات اسکن خواهند شد:
1- این کلاس باید عمومی باشد.
2- این کلاس نباید abstract باشد (تا بتوان آن‌را به صورت خودکار وهله سازی کرد).
3- این کلاس باید اینترفیس استاندارد IController را پیاده سازی کرده باشد.
4- و نام آن باید مختوم به کلمه Controller باشد (همان مبحث Convention over configuration یا کار کردن با یک سری قرار داد از پیش تعیین شده).

برای مثال در اینجا فریم ورک به دنبال کلاسی به نام ProductsController خواهد گشت.
شاید تعدادی از برنامه نویس‌های ASP.NET MVC تصور ‌کنند که فریم ورک در پوشه‌ی استانداردی به نام Controllers به دنبال این کلاس خواهد گشت؛ اما در عمل زمانیکه برنامه کامپایل می‌شود، پوشه‌ای در این اسمبلی وجود نخواهد داشت و همه چیز از طریق Reflection مدیریت خواهد شد.

مطالب
ضبط تصاویر Webcam در پروژه‌های Angular
هدف ما از این مقاله این است که از طریق وب کم، تصاویر را ضبط کنیم (یک نمونه ). برای انجام این کار فایل app.component.html  را باز کرده و مطابق زیر ویرایش کنید: 
<div id="app">
  <div><video #video id="video" width="640" height="480" autoplay></video></div>
  <div><button id="snap" (click)="capture()">ضبط تصویر</button></div>
  <canvas #canvas id="canvas" width="640" height="480"></canvas>
  <ul>
    <li *ngFor="let capture of captures">
        <img src="{{ capture }}" height="50" />
    </li>
 </ul>
</div>
کد‌های HTML زیادی در اینجا وجود ندارند. به تگ <video> و <canvas> توجه کنید. برای هر کدام از این تگ‌ها، یک local variable وجود دارد که با سمبل # مشخص شده‌اند. به عبارت دیگر، دو متغیر video# و canvas# وجود دارند . این گونه است که ما می‌توانیم به این المنت‌ها، در کد‌های تایپ اسکریپت دسترسی داشته باشیم.  
المنت <button>، در رویداد کلیک آن، متد capture را فراخوانی می‌کند که کارش ضبط تصویر می‌باشد. 
و در پایان یک حلقه بر روی آرایه‌ی captures داریم که کارش نمایش تصاویر ضبط شده است. چون هر بار که بر روی دکمه ضبط تصویر کلیک کنیم، یک تصویر به آرایه‌ی captures  اضافه می‌شود .
 
فایل app.component.ts :
export class AppComponent implements OnInit, AfterViewInit {
  @ViewChild('video')
  public video: ElementRef;

  @ViewChild('canvas')
  public canvas: ElementRef;

  public captures: Array<any>;

  public constructor() {
    this.captures = [];
  }

  public ngOnInit() { }

  public capture() {
    this.canvas.nativeElement.getContext('2d').drawImage(this.video.nativeElement, 0, 0, 640, 480);
    this.captures.push(this.canvas.nativeElement.toDataURL('image/png'));
  }

  public ngAfterViewInit() {
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
        this.video.nativeElement.src = window.URL.createObjectURL(stream);
        this.video.nativeElement.play();
      });
    }
  }
}

توضیحات :

با استفاده از local variable ‌هایی که در کد‌های HTML تعریف کردیم و ViewChild@، می‌توانیم المنت‌ها را در متغیر‌ها، load کنیم. در این حالت این امکان وجود دارد تا المنت‌های DOM را دستکاری کنیم.
  public ngAfterViewInit() {
    if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
        this.video.nativeElement.src = window.URL.createObjectURL(stream);
        this.video.nativeElement.play();
      });
    }
  }
  1. MediaDevices : این اینترفیس دستیابی به دستگاه‌های ورودی متصل شده، مثلا دوربین، میکروفن و ... را فراهم می‌سازد. 
  2. MediaDevices.getUserMedia: با گرفتن مجوزی از کاربر از طریق یک هشدار، دوربین کاربر را روشن می‌کند و هم چنین این متد یک promise را بازگشت می‌دهد؛ در صورتیکه کاربر اجازه دسترسی بدهد.
  3. URL.createObjectURL: یک URL را برای یک BLOB  مشخص شده ایجاد می‌کند ( BLOB: Binary large object ) که می‌تواند به متدی که انتظار یک URL را دارد، پاس داده شود. بعد از برگشت URL، متد ()revokeObjectURL فراخوانی میشود که کارش آزاد سازی منابع مرتبط با url ایجاد شده‌ی توسط createObjectURL  می‌باشد. در ضمن طول عمر url ایجاد شده برابر با بستن سند (document) در پنجره‌ای (window) که در آن ایجاد شده‌است، می‌باشد. 
 
عملیات ضبط تصویر در این متد انجام میشود:
  public capture() {
    this.canvas.nativeElement.getContext('2d').drawImage(this.video.nativeElement, 0, 0, 640, 480);
    this.captures.push(this.canvas.nativeElement.toDataURL('image/png'));
  }
  1. ()getContext : این متد یک شیء را برگشت می‌دهد که فراهم کننده‌ی متد‌ها و خصوصیت‌ها، برای رسم در Canvas میباشد. 
  2. ()drawImage: این متددر Canvas رسم را انجام می‌دهد و همچنین این متد می‌تواند بخش‌هایی از یک تصویر را رسم کند یا سایز یک تصویر را افزایش یا کاهش دهد.
  3.  () toDataURL: این متد یک data URI  را بازگشت می‌دهد که یک تصویر در فرمت مشخص شده را بر اساس پارامتر type، برگشت می‌دهد (پیش فرض آن png می‌باشد). 
تمام !
DEMO  
مطالب
چگونه یک اسکریپت گریس مانکی بنویسیم؟

گریس مانکی یکی از افزونه‌های فایرفاکس است که توسط آن می‌توان اسکریپت‌هایی را بر روی صفحات وب در حال مشاهده، جهت تغییر آن‌ها اجرا کرد. این نوع تغییرات بیشتر در جهت بالا بردن کارآیی یا خوانایی سایت‌ها صورت می‌گیرد. مثلا بررسی وجود لینک‌های ارائه شده در یک صفحه وب (قبل از اینکه به آن‌ها رجوع کنیم، مشخص شود که آیا وجود دارند یا خیر) و هزاران مثال شبیه به این که در سایت اسکریپت‌های آن قابل دریافت هستند.
گریس مانکی به خودی خود کار خاصی را انجام نمی‌دهد و فقط میزبان اجرایی اسکریپت‌هایی است که برای آن تهیه شده اند. به این اسکریپت‌ها user scripts گفته می‌شود و جهت تهیه آن‌ها از زبان جاوا اسکریپت استفاده می‌گردد.
مطابق اصول نامگذاری آن، فایل این اسکریپت‌ها حتما باید به .user.js ختم شود تا توسط افزونه گریس مانکی قابل شناسایی باشد.

اسکریپت سلام دنیای گریس مانکی!
// ==UserScript==
// @name Hello World
// @namespace http://diveintogreasemonkey.org/download/
// @description example script to alert "Hello world!" on every page
// @include *
// @exclude http://diveintogreasemonkey.org/*
// @exclude http://www.diveintogreasemonkey.org/*
// ==/UserScript==
alert('Hello world!');

فرمت کلی و آناتومی یک اسکریپت گریس مانکی مطابق چند سطر فوق است. در ابتدا نامی را که برای اسکریپت مشخص کرده‌اید، ذکر خواهید نمود. سپس فضای نام آن مشخص می‌گردد. این فضای نام یک آدرس وب خواهد بود (مثلا سایت شخصی شما) . به این طریق گریس مانکی می‌تواند اسکریپت‌های هم نام را بر اساس این فضاهای نام مختلف مدیریت کند. سپس توضیحات مربوط به اسکریپت ارائه می‌شود. در قسمت include مشخص خواهید کرد که این اسکریپت بر روی چه سایت‌ها و آدرس‌هایی اجرا شود و در قسمت exclude سایت‌های صرفنظر شونده را تعیین خواهید نمود. در مثال فوق، اسکریپت بر روی تمامی سایت‌ها اجرا خواهد شد، منهای دو سایت و زیر سایت‌هایی که در قسمت exclude مشخص شده‌اند.
در پایان مثلا نام آن‌را hello.user.js خواهیم گذاشت (همانطور که ذکر شد قسمت user.js آن باید رعایت شود). برای نصب آن فقط کافی است این فایل را به درون پنجره فایر فاکس کشیده و رها کنیم (drag & drop).

استفاده از jQuery در اسکریپت‌های گریس مانکی

عمده کاربردهای اسکریپت‌های گریس مانکی در جهت اعمال تغییرات بر روی document object model صفحه (DOM) هستند. کتابخانه jQuery اساسا برای این منظور تهیه و بهینه سازی شده است.

مثال:
فرض کنید قصد داریم به نتایج خروجی جستجوی گوگل، fav icon سایت‌های یافت شده را اضافه کنیم (در کنار هر لینک، آیکون سایت مربوطه را نمایش دهیم). گوگل این‌کار را انجام نداده است. اما ما علاقمند هستیم که این قابلیت را اضافه کنیم!
// ==UserScript==
// @name Google FavIcon
// @namespace http://userscripts.org
// @description Shows favicon for Google searches results
// @include http://*.google.*/search?*

// ==/UserScript==

loadJquery();

$(document).ready(function(){
$("h3.r > a.l").each(function(){
var $a = $(this);
var href = $a.attr("href");
var domain = href.replace(/<\S[^><]*>/g, "").split('/')[2];
var image = '<img src="http://' + domain + '/favicon.ico" style="border:0;padding-right:4px;" />';
//GM_log(">image:> " + image);
$(this).prepend(image);
});

});

نحوه پیاده سازی تابع loadJquery را در سورس مربوطه می‌توانید مشاهده نمائید.
دریافت سورس این مثال

در اینجا ابتدا لینک‌های حاصل از جستجو پیدا می‌شوند. سپس نام دومین مربوطه استخراج می‌گردد (با استفاده از regular expressions) و در ادامه از این نام دومین یک آدرس استاندارد http://domain/favico.ico ساخته شده و از آن یک تگ img درست می‌شود. در آخر این تگ به قبل از لینک‌های گوگل اضافه می‌شود.

شاید سؤال بپرسید از کجا مشخص شد که باید به دنبال h3.r > a.l گشت؟ به تصویر زیر دقت نمائید (نمایی است از یکی از توانایی‌های افزونه fireBug فایرفاکس).



هنگامیکه صفحه بارگذاری شد، بر روی آیکون سوسک موجود در status bar فایرفاکس کلیک کنید تا FireBug ظاهر شود (البته من اینجا سوسک دیدم شاید موجود دیگری باشد :) )
سپس بر روی دکمه inspect در نوار ابزار آن کلیک کنید. در همین حال اشاره‌گر ماوس را به یکی از لینک‌های نتیجه جستجوی گوگل نزدیک کنید. بلافاصله تگ‌ها و کلاس‌های مورد استفاده آن به شکل زیبایی ظاهر خواهند شد. به این صورت صرفه جویی قابل ملاحظه‌ای در وقت صورت خواهد گرفت.

نتیجه اجرای اسکریپت فوق (پس از نصب) به صورت زیر است:



نکته: نحوه دیباگ کردن اسکریپت‌های گریس مانکی

اگر نیاز به مشاهده مقدار متغیرها در لحظه اجرای اسکریپت داشتید، یکی از راه‌حل‌های موجود استفاده از تابع GM_log مربوط به API گریس مانکی است که خروجی آن‌را در قسمت messages مربوط به error console فایرفاکس می‌توان دید.



مطالب
بررسی Microsoft Anti-Cross Site Scripting Library

هنگام نمایش اطلاعات در وب باید اطلاعات خام دریافتی از کاربر را encode کرده و سپس نمایش داد تا از حملات XSS یا cross site scripting attacks در امان ماند. مثلا وبلاگی را طراحی کرده‌اید و یک نفر اطلاعات زیر را بجای توضیحات ارسال کرده است:
<SCRIPT>alert('XSS')</SCRIPT>

اگر اطلاعات به همین شکل دریافت و بدون تغییر هم نمایش داده شود، یک ضعف امنیتی برای سایت شما به‌حساب خواهد آمد. (بحث دزدیدن اطلاعات کوکی و امثال آن از این طریق با معرفی HttpOnly cookies در IE‌های جدید و فایرفاکس 3 به بعد تقریبا منتفی شده است اما می‌توانند با ارسال انبوهی اسکریپت، مشاهده صفحه را با crash‌ کردن مرورگر کاربران همراه کنند)
مایکروسافت برای این منظور Microsoft Anti-Cross Site Scripting Library را ارائه داده است. نمونه بهبود یافته HttpUtility.HtmlEncode که در فضای نام System.Web موجود است.

در اینجا قصد داریم این کتابخانه را با لیست زیر آزمایش کنیم:
http://ha.ckers.org/xss.html
در همان صفحه اگر دقت کنید، لیست حملات را به صورت یک فایل xml هم ارائه داده است:
http://ha.ckers.org/xssAttacks.xml
برای خواندن این فایل xml در دات نت روش‌های زیادی وجود دارد منجمله XML serialization .

ساختار این فایل به شکل زیر است:
<?xml version="1.0" encoding="UTF-8"?>
<xss>
<attack>
<name>x1</name>
<code>x2</code>
<desc>x3</desc>
<label>x4</label>
<browser>x5</browser>
</attack>
.
.
.

بنابراین شیء‌ نمایانگر آن می‌تواند به صورت لیستی از کلاس زیر باشد:
    public class attack{
public string name { get; set; }
public string code { get; set; }
public string desc { get; set; }
public string label { get; set; }
public string browser { get; set; }
}

برای دریافت این لیست و بارگذاری فایل xml مربوطه با استفاده از روش XML serialization خواهیم داشت:
      
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

public static List<attack> DeserializeFromXML(string path)
{
XmlRootAttribute root = new XmlRootAttribute("xss");
XmlSerializer deserializer =
new XmlSerializer(typeof (List<attack>),root);
using (TextReader textReader = new StreamReader(path))
{
return (List<attack>)deserializer.Deserialize(textReader);
}
}

در ادامه فرض بر این است که ارجاعی از اسمبلی AntiXssLibrary.dll به پروژه اضافه شده است، همچنین فایل xssAttacks.xml فوق نیز در کنار فایل اجرایی برنامه ، مثلا یک برنامه کنسول قرار گرفته است:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Security.Application;

private static void testMethod()
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("<html>{0}", Environment.NewLine);
sb.AppendFormat("<body>{0}", Environment.NewLine);

List<attack> data = XMLParser.DeserializeFromXML("xssAttacks.xml");
foreach (attack atk in data)
{
string cleanSafeHtmlInput = AntiXss.HtmlEncode(atk.code);
sb.AppendFormat("{0}<br>{1}", cleanSafeHtmlInput, Environment.NewLine);
}

sb.AppendFormat("</body>{0}", Environment.NewLine);
sb.AppendFormat("</html>");

File.WriteAllText("out.htm", sb.ToString());
}

پس از اجرای تابع فوق، خروجی ما یک فایل html خواهد بود به نام out.htm . آنرا در مرورگر خود باز کنید. بدون هیچ مشکلی باز خواهد شد و خروجی امنی را مشاهده خواهید کرد. برای مشاهده اثر واقعی این کتابخانه، قسمت AntiXss.HtmlEncode را از کد فوق حذف کنید و یکبار دیگر برنامه را اجرا کنید. اکنون فایل نهایی را در مرورگر باز کنید. با انبوهی از alert های جاوا اسکریپتی مواجه خواهید شد که اهمیت کتابخانه فوق را جهت ارائه خروجی امن در صفحات وب مشخص می‌سازد.

مطالب
بهینه سازی پروژه‌های Angular
Angular یکی از محبوب‌ترین فریم ورک‌ها، برای ساختن برنامه‌های تک صفحه‌ای می‌باشد. اگرچه گفتیم تک صفحه‌ای، اما ضرورتا منظور این نیست که پروژه‌ی شما تنها شامل یک صفحه باشد. شما می‌توانید با Angular یک وب سایت را با هزاران صفحه نیز ایجاد کنید. با این حال وقتی بحث از کارآیی باشد، بهتر است همیشه در رابطه با تعدادی فکر کنیم که باعث می‌شوند پروژه، نرم و سریع اجرا شود. 


#1 Optimize main bundle with Lazy Loading
وقتی که پروژه را برای حالت ارائه‌ی نهایی (Production ) بدون در نظر گرفتن Lazy Load، بیلد می‌کنیم احتمالا فایل‌های تولید شده زیر را در پوشه‌ی dist خواهیم دید: 

  1. polyfills.js :  برای ساختن برنامه‌های سازگار با انواع مرورگر‌ها می‌باشد. به دلیل اینکه وقتی کد‌ها را با جدیدترین ویژگی‌ها می‌نویسیم، ممکن است که همه‌ی مرورگر‌ها توانایی پشتیبانی از آن ویژگی‌ها را نداشته باشند.
  2. scripts.js : شامل اسکریپت‌هایی می‌باشد که در بخش scripts، در فایل angular.json تعریف کرده‌ایم.
  3.  webpack loader : runtime.js می‌باشد. این فایل شامل webpack utilities‌هایی می‌باشد که برای بارگذاری دیگر فایل‌ها مورد نیاز است.
  4. styles.css : شامل style ‌هایی است که در بخش styles، در فایل angular.json تعریف کرده‌ایم.
  5. main.js : شامل تمامی کد‌ها از قبیل کامپوننت‌ها ( کد‌های مربوط به css ، html ، ts) ، دایرکتیو‌ها، pipes و سرویس‌ها و ماژول‌های ایمپورت شده از جمله third party‌های می‌باشد.

همانطور که متوجه هستید، فایل main.js در طول زمان بزرگتر و بزرگتر خواهد شد که این یک مشکل است. در این حالت برای مشاهده‌ی وب سایت، مرورگر نیاز دارد که فایل main.js را دانلود کرده و سپس در صفحه،  آن را اجرا و رندر کند که این یک چالش برای کاربران موبایل با اینترنت ضعیف و هم چنین کاربران دسکتاپ می‌باشد. 
آسان‌ترین راه برای برطرف کردن این مشکل این است که پروژه را به چندین ماژول lazy load، تقسیم کنیم. وقتی که از lazy module‌ها استفاده می‌کنیم، انگیولار chunk مربوط به آن را تولید می‌کند که در ابتدا بارگذاری نخواهد شد؛ مگر اینکه مورد نیاز باشند (معمولا با فعال سازی یک مسیر اتفاق می‌افتد). 
وقتی که از lazy loading استفاده می‌کنیم، بعد از فرایند build، فایل‌های جدیدی تولید خواهند شد، مثل  4.386205799sfghe4.js که یک  چانک از  یک lazy module می‌باشد و در زمان راه اندازی صفحه اول اجرا نخواهد شد که نتیجه‌ی آن داشتن فایل main.js ای با حجم کم می‌باشد. بنابراین بارگذاری صفحه‌ی اول، خیلی سریع انجام خواهد شد. 

با این حال، بارگذاری هر قسمت می‌تواند بر روی کارآیی تاثیر داشته باشد (بارگذاری ممکن است کند باشد). خوشبختانه انگیولار یک راه را برای برطرف کردن این مشکل فراهم کرده است ( PreloadingStrategy ) . بعد از اینکه فایل main.js  به صورت کامل بارگذاری و اجرا شد، کار پیش واکشی ماژول‌ها را انجام می‌دهد و زمانیکه کاربری مسیری را درخواست می‌دهد، آْن مسیر را بدون درنگ مشاهده خواهد کرد.

#2 Debug bundles with Webpack Bundle Analyzer 
حتی ممکن است بعد از تقسیم کردن منطق برنامه به چند ماژول lazy load، شما یک فایل main.js بزرگ داشته باشید. در این حالت می‌توانید بهینه سازی بیشتری را با استفاده از Webpack Bundle Analyzer انجام دهید. با استفاده از این پکیج می‌توانید آمار‌هایی را در رابطه با هر باندل داشته باشید. در ابتدا با استفاده از دستور زیر پکیج آن‌را نصب کنید:
npm install --save-dev webpack-bundle-analyzer
سپس فایل package.json را باز کرده و در بخش scripts، مدخل زیر را اضافه کنید:
"bundle-report": "ng build --prod --stats-json && webpack-bundle-analyzer dist/stats.json"
توجه کنید که اگر خروجی برنامه شما مستقیما در dist می‌باشد، به صورت بالا عمل کنید؛ ولی اگر خروجی برنامه شما در پوشه‌ی dist/YourApplicationName باشد، آن را به حالت زیر ویرایش کنید:
"bundle-report": "ng build --prod --stats-json && webpack-bundle-analyzer dist/YourApplicationName/stats.json"
در نهایت دستور زیر را اجرا کنید:
npm run bundle-report
دستور بالا یک بیلد را برای پروژه برای حالت ارائه‌ی نهایی  (Production) همراه با  آمار‌هایی در رابطه با هر باندل ایجاد می‌کند. در اینجا می‌توانیم ببینیم که چه ماژولها/فایل‌هایی در هر باندل استفاده شده‌است. این مورد فوق العاده کمک می‌کند. هم چنین می‌توانیم به صورت بصری ببینیم که چه چیز‌هایی در هر ماژول شامل شده‌اند که بهتر بود آنجا نباشند:


#3 Use Lazy Loading for images that are not visible in page 
وقتی که صفحه اصلی را در اولین بار، بارگذاری می‌کنیم، می‌توانیم تصاویری را داشته باشیم که در صفحه‌ی نمایش، نمایان نباشند (منظور viewport می‌باشد) و کاربر برای دیدن آن تصاویر باید صفحه را به پایین اسکرول کند. با این وجود وقتی که صفحه بارگذاری می‌شود، تصاویر هم بلافاصله دانلود می‌شوند. اگر تعداد تصاویر زیاد باشند این مورد می‌تواند بر روی performance تاثیر منفی داشته باشد. برای برطرف کردن این مشکل می‌توان از lazy loading تصاویر استفاده کرد. در این حالت تصاویر زمانی بارگذاری می‌شوند که کاربر به آن‌ها می‌رسد. یک JavaScript API به نام Intersection Observer API وجود دارد که باعث می‌شود پیاده سازی lazy load خیلی آسان شود. علاوه بر این می‌توان یک دایرکتیو را با قابلیت استفاده مجدد طراحی کرد ( lazy loading برای تصاویر با استفاده از Intersection Observer).

#4 Use virtual scrolling for large lists  
با استفاده از virtual scrolling  می‌توان المنت‌ها را در Dom بر اساس بخش‌های قابل مشاهده‌ای از یک لیست، Load  یا unload کرد که برنامه را فوق العاده سریع می‌کند.
 
#5 Use FOUT instead of FOIT for fonts  
در بیشتر وب سایت‌ها می‌توان فونت‌های سفارشی زیبایی را به جای فونت‌های معمول دید. با این حال برای استفاده از فونت‌های فراهم شده توسط سرویس‌های دیگر لازم است که مرورگر آن‌ها را دانلود و parse  کند. اگر از فونت‌های سفارشی استفاده کنیم، مثل  Google Fonts، چه اتفاقی می‌افتد؟ در اینجا دو سناریو وجود دارد: 
  • در این حالت مرورگر منتظر می‌ماند تا فونت دانلود شود و آن را parse  کند و تنها بعد از آن متن را بر روی صفحه نمایش می‌دهد. متن روی صفحه تا زمانیکه فونت دانلود و parse  نشده باشد، قابل مشاهده نیست؛ این FOIT است (Flash of invisible text) .
  • مرورگر در ابتدا متن را با استفاده از فونت معمول نمایش می‌دهد و بعد از آن سعی می‌کند که ساختار‌های فونت خارجی را دریافت کند. وقتی که دانلود انجام شد و سپس آن را parse  کرد، فونت سفارشی دانلود شده، با فونت معمول جایگزین خواهد شد؛ این FOUT است ( Flash of unstyled text ).

بیشتر مرورگر‌ها از FOIT  استفاده می‌کنند و تنها Internet Explorer از FOUT استفاده می‌کند. برای برطرف کردن این مشکل می‌توان از توصیف‌گر font-display استفاده کرد و به مرورگر بگوییم که می‌خواهیم در ابتدا متن را با فونت معمول نمایش دهیم و جایگزینی فونت، بعد از دانلود باشد (بیشتر).
مطالب
بارگذاری بدون وقفه فایل‌های جاوااسکریپت به کمک async و defer
امروزه در برنامه‌های تحت وب، بارگذاری فایل‌های جاوااسکریپت صفحات، یکی از چالش‌های بزرگ در عملکرد، کارآیی و سرعت اجرای صفحات وب به شمار می‌‌آید. حال اینکه توسعه اپلیکیشن‌های single page و استفاده از کتابخانه‌های حجیم جاوااسکریپتی، حجم این سری فایل‌ها را بیشتر و بیشتر نیز می‌کند.

در شرایطی خاص، تگ script باعث می‌شود که مرورگر برای مدت زمانی متوقف شود و فایل‌های جاوااسکریپت را بارگذاری و اجرا نماید. در این مواقع مرورگر از انجام عملیات دیگری منع شده و شروع به اعمال فایل جاوااسکریپت در حال بارگذاری در محتوای صفحه‌ی بار شده می‌کند. این نگارش و اعمال می‌تواند سبب ویرایش یک element و یا تغییر DOM یا ارجاعی به یک URL دیگر شود. به این منظور، بهترین و رایج‌ترین کار این است که تگ‌های script را به انتهای صفحه و پیش از تگ body منتقل کنیم. با این حال نیز ممکن است که مرورگر برای چند ثانیه منتظر دریافت پاسخ‌های اسکریپت‌های انتهای صفحه بماند، اما این امر چندان قابل توجه نیست؛ به این دلیل که غالب استایل‌ها و content صفحه بارگذاری شده‌اند. با این وجود، این راهکار برای صفحات وب کنونی که ممکن است شامل محتوای یک یا چندین مگابایتی باشند، کافی نیست.  
در این مواقع برخی از کد نویس‌ها از روش‌هایی مانند inject کردن تگ script به صورت Ajax استفاده می‌کنند که این روش‌ها از block شدن صفحه جلوگیری می‌کند، اما با این حال این روش‌ها نیز چالش‌های خاص خود مانند نوشتن کد‌های اضافی برای هر اسکریپت و تست‌های مرتبط برای اطمینان از بار شدن و اجرای صحیح اسکریپت‌ها در موقعیت مناسب را دارند که ممکن است روند کد نویسی با استفاده از کتابخانه‌های third-party را بسیار طولانی کند.
برای حل این چالش خرسندم که تکنیکی را که به صورت built-in در HTML5 وجود دارد، به شما معرفی کنم.

استفاده از defer Attribute

 این Attribute اگر درون هر تگ script ایی نوشته شود، آن فایل در هنگام بارگذاری فایل‌ها و ساختن صفحه، نادیده گرفته می‌شود و پس از اتمام پروسه، این فایل شروع به بارگذاری شدن می‌کند. در حقیقت در این حین در مرورگر همانند تمامی عملیات‌های Lazy Loading به صورت ضمنی یک promise ساخته می‌شود. البته گفتنی است که این نوع بارگذاری نباید شامل فایل‌های جاوا اسکریپتی باشد که شامل عملیاتی مانند document.write و یا تغییرات DOM هستند.
<script src="file.js" defer></script>
مرورگر تمامی فایل‌های شامل defer Attribute را بدون توقف پردازش‌های صفحه، به صورت موازی بارگذاری می‌نماید. اما اینکه این ویژگی از چه زمانی در مرورگر‌ها وجود داشته؟ باید گفت که تقریبا 12 سال از عمر آن می‌گذرد. تقریبا از نسخه‌ی IE4 ( ^_^ ). البته این نکته گفتنی است هنگامی که شما از این Attribute استفاده می‌کنید، این تضمین وجود دارد که تمامی این اسکریپت‌ها اجرا می‌شوند، اما اینکه کدامیک در چه زمانی بارگذاری و اجرا می‌شوند، کاملا نامشخص است. از نظر تئوری، عملیات بارگذاری پس از ویرایش DOM و کمی پیش از فراخوانی رخداد DOMContentLodaded صورت می‌گیرد. اما در عمل، این عملیات به مرورگر و سیستم‌عامل نیز وابسته است.

استفاده از async Attribute

این Attribute در HTML5 معرفی شد و به نوعی کامل کننده و منسوخ کننده‌ی defer می‌باشد.
<script src="file.js" async></script>
به طور کلی می‌توان گفت که عملکرد async و defer یکسان می‌باشند. البته تفاوت اصلی این دو سینتکس، در زمان اجرای اسکریپت‌های بار شده می‌باشد. در async عملیات execution، در اولین فرصتی که پس از تکمیل دریافت فایل ممکن است، صورت می‌گیرد. به سه تصویر زیر توجه کنید:

تصویر فوق یک سناریوی ساده‌ی بارگذاری صفحه‌ی html را به همراه یک فایل جاوااسکریپت با تگ script ساده را نمایش می‌دهد. همانطور که ملاحظه می‌کنید، در لحظه‌ای که فایل اسکریپت شروع به دانلود شدن می‌کند (قسمت بنفش)، عملیات parse صفحه متوقف شده تا زمانی که فایل دانلود و اجرا شود (قسمت قرمز). پس از آن دوباره عملیات parsing از سر گرفته می‌شود. این عملیات به همراه تگ script ساده صورت گرفته است. حال در تصویر بعد قصد داریم که همین سناریو را به کمک defer Attribute بررسی کنیم:

همانطور که ملاحظه می‌کنید به کمک defer، شروع عملیات دانلود فایل اسکریپت، خللی در عملیات html parse ایجاد نمی‌کند و این دو عملیات به صورت موازی می‌توانند اجرا شوند. پس از دریافت فایل اسکریپت، این فایل مستقیما اجرا نمی‌شود و لزوما باید پس از عملیات parsing صورت پذیرد.

و اما تصویر زیر، سناریو‌ی فوق را به همراه async attribute نمایش می‌دهد:

در این حالت عملیات دریافت فایل اسکریپت همانند defer به صورت موازی اجرا می‌شود. اما تفاوتی که در این قسمت و در async وجود دارد، زمان اجرا شدن فایل‌های اسکریپت است. همانطور که ملاحظه می‌کنید دقیقا پس از دریافت فایل، اسکریپت ما در اجراست و پس از اجرای کامل، دوباره عملیات parsing ادامه می‌یابد. در حقیقت اسکریپت‌های با async attribute با فراخوانی رخداد onload شروع به اجرا می‌کنند. جهت استفاده‌ی از async، خیالتان راحت باشد؛ با توجه به این منبع، تقریبا تمام مرورگرها از این ویژگی پشتیبانی می‌کنند.

مطالب
Globalization در ASP.NET MVC - قسمت هفتم
در قسمت قبل مطالب تکمیلی تولید پرووایدر سفارشی منابع دیتابیسی ارائه شد. در این قسمت نحوه بروزرسانی ورودی‌های منابع در زمان اجرا بحث می‌شود.

.

تولید یک پرووایدر منابع دیتابیسی - بخش سوم

برای پیاده‌سازی ویژگی به‌روزرسانی ورودی‌های منابع در زمان اجرا راه‌حل‌های مخنلفی ممکن است به ذهن برنامه‌نویس خطور کند که هر کدام معایب و مزایای خودش را دارد. اما درنهایت بسته به شرایط موجود انتخاب روش مناسب برعهده خود برنامه‌نویس است.

مثلا برای پرووایدر سفارشی دیتابیسی تهیه‌شده در مطالب قبلی، تنها کافی است ابزاری تهیه شود تا به کاربران اجازه به‌روزرسانی مقادیر موردنظرشان در دیتابیس را بدهد که کاری بسیار ساده است. بدین ترتیب به‌روزرسانی این مقادیر در زمان اجرا کاری بسیار ابتدایی به نظر می‌رسد. اما در قسمت قبل نشان داده شد که برای بالا بردن بازدهی بهتر است که مقادیر موجود در دیتابیس در حافظه سرور کش شوند. استراتژی اولیه و ساده‌ای نیز برای نحوه پیاده‌سازی این فرایند کشینگ ارائه شد. بنابراین باید امکاناتی فراهم شود تا درصورت تغییر مقادیر کش‌شده در سمت دیتابیس، برنامه از این تغییرات آگاه شده و نسبت به به‌روزرسانی این مقادیر در متغیر کشینگ اقدامات لازم را انجام دهد.

اما همان‌طور که در قسمت قبل نیز اشاره شد، نکته‌ای که باید درنظر داشت این است که مدیریت تمامی نمونه‌های تولیدشده از کلاس‌های موردبحث کاملا برعهده ASP.NET است، بنابراین دسترسی مستقیمی به این نمونه‌ها در بیرون و در زمان اجرا وجود ندارد تا این ویژگی را بتوان در مورد آن‌ها پیاده کرد.

یکی از روش‌های موجود برای حل این مشکل این است که مکانیزمی پیاده شود تا بتوان به تمامی نمونه‌های تولیدی از کلاس DbResourceManager در بیرون از محیط سیستم مدیریت منابع ASP.NET دسترسی داشت. مثلا یک کلاس حاول متغیری استاتیک جهت ذخیره نمونه‌های تولیدی از کلاس DbResourceManager، به کتابخانه خود اضافه کرد تا با استفاده از یکسری امکانات بتوان این نمونه‌های تولیدی را از تغییرات رخداده در سمت دیتابیس آگاه کرد. در این قسمت پیاده‌سازی این راه‌حل شرح داده می‌شود.


نکته: قبل از هرچیز برای مناسب شدن طراحی کتابخانه تولیدی و افزایش امنیت آن بهتر است تا سطح دسترسی تمامی کلاس‌های پیاده‌سازی شده تا این مرحله به internal تغییر کند. ازآنجاکه سیستم مدیریت منابع ASP.NET از ریفلکشن برای تولید نمونه‌های موردنیاز خود استفاده می‌کند، بنابراین این تغییر تاثیری بر روند کاری آن نخواهد گذاشت.


نکته: با توجه به شرایط خاص موجود، ممکن است نام‌های استفاده شده برای کلاس‌های این کتابخانه کمی گیج‌کننده باشد. پس با دقت بیشتری به مطلب توجه کنید.


پیاده‌سازی امکان پاک‌سازی مقادیر کش‌شده

برای این‌کار باید تغییراتی در کلاس DbResourceManager داده شود تا بتوان این کلاس را از تغییرات بوجود آمده آگاه ساخت. روشی که من برای این کار درنظر گرفتم استفاده از یک اینترفیس حاوی اعضای موردنیاز برای پیاده‌سازی این امکان است تا مدیریت این ویژگی در ادامه راحت‌تر شود.


اینترفیس IDbCachedResourceManager

این اینترفیس به صورت زیر تعریف شده است:

namespace DbResourceProvider
{
  internal interface IDbCachedResourceManager
  {
    string ResourceName { get; }

    void ClearAll();
    void Clear(string culture);
    void Clear(string culture, string resourceKey);
  }
}

در پراپرتی فقط خواندنی ResourceName نام منبع کش شده ذخیره خواهد شد.

متد ClearAll برای پاک‌سازی تمامی ورودی‌های کش‌شده استفاده می‌شود.

متدهای Clear برای پاک‌سازی ورودی‌های کش‌شده یک کالچر به خصوص و یا یک ورودی خاص استفاده می‌شود.

با استفاده از این اینترفیس، پیاده‌سازی کلاس DbResourceManager به صورت زیر تغییر می‌کند:

using System.Collections.Generic;
using System.Globalization;
using DbResourceProvider.Data;
namespace DbResourceProvider
{
  internal class DbResourceManager : IDbCachedResourceManager
  {
    private readonly string _resourceName;
    private readonly Dictionary<string, Dictionary<string, object>> _resourceCacheByCulture;
    public DbResourceManager(string resourceName)
    {
      _resourceName = resourceName;
      _resourceCacheByCulture = new Dictionary<string, Dictionary<string, object>>();
    }
    public object GetObject(string resourceKey, CultureInfo culture) { ... }
    private object GetCachedObject(string resourceKey, string cultureName) { ... }

    #region Implementation of IDbCachedResourceManager
    public string ResourceName
    {
      get { return _resourceName; }
    }
    public void ClearAll()
    {
      lock (this)
      {
        _resourceCacheByCulture.Clear(); 
      }
    }
    public void Clear(string culture)
    {
      lock (this)
      {
        if (!_resourceCacheByCulture.ContainsKey(culture)) return;
        _resourceCacheByCulture[culture].Clear(); 
      }
    }
    public void Clear(string culture, string resourceKey)
    {
      lock (this)
      {
        if (!_resourceCacheByCulture.ContainsKey(culture)) return;
        _resourceCacheByCulture[culture].Remove(resourceKey); 
      }
    }
    #endregion
  }
}

اعضای اینترفیس IDbCachedResourceManager به صورت مناسبی در کد بالا پیاده‌سازی شدند. در تمام این پیاده‌سازی‌ها مقادیر مربوطه از درون متغیر کشینگ پاک می‌شوند تا پس از اولین درخواست، بلافاصله از دیتابیس خوانده شوند. برای جلوگیری از دسترسی هم‌زمان نیز از بلاک lock استفاده شده است.

برای استفاده از این امکانات جدید همان‌طور که در بالا نیز اشاره شد باید بتوان نمونه‌های تولیدی از کلاس DbResourceManager توسط ASP.NET درون متغیری استاتیک ذخیره شوند. برای اینکار از کلاس جدیدی با عنوان DbResourceCacheManager استفاده می‌شود که برخلاف تمام کلاس‌های تعریف‌شده تا اینجا با سطح دسترسی public تعریف می‌شود.


کلاس DbResourceCacheManager

مدیریت نمونه‌های تولیدی از کلاس DbResourceManager در این کلاس انجام می‌شود. این کلاس پیاده‌سازی ساده‌ای به‌صورت زیر دارد:

using System.Collections.Generic;
using System.Linq;
namespace DbResourceProvider
{
  public static class DbResourceCacheManager
  {
    internal static List<IDbCachedResourceManager> ResourceManagers { get; private set; }
    static DbResourceCacheManager()
    {
      ResourceManagers = new List<IDbCachedResourceManager>();
    }
    public static void ClearAll()
    {
      ResourceManagers.ForEach(r => r.ClearAll());
    }
    public static void Clear(string resourceName)
    {
      GetResouceManagers(resourceName).ForEach(r => r.ClearAll());
    }
    public static void Clear(string resourceName, string culture)
    {
      GetResouceManagers(resourceName).ForEach(r => r.Clear(culture));
    }
    public static void Clear(string resourceName, string culture, string resourceKey)
    {
      GetResouceManagers(resourceName).ForEach(r => r.Clear(culture, resourceKey));
    }

    private static List<IDbCachedResourceManager> GetResouceManagers(string resourceName)
    {
      return ResourceManagers.Where(r => r.ResourceName.ToLower() == resourceName.ToLower()).ToList();
    }
  }
}

ازآنجاکه نیازی به تولید نمونه ای از این کلاس وجود ندارد، این کلاس به صورت استاتیک تعریف شده است. بنابراین تمام اعضای درون آن نیز استاتیک هستند.

از پراپرتی ResourceManagers برای نگهداری لیستی از نمونه‌های تولیدی از کلاس DbResourceManager استفاده می‌شود. این پراپرتی از نوع <List<IDbCachedResourceManager تعریف شده است و برای جلوگیری از دسترسی بیرونی، سطح دسترسی آن internal درنظر گرفته شده است.

در کانستراکتور استاتیک این کلاس (اطلاعات بیشتر درباره static constructor در اینجا) این پراپرتی با مقداردهی به یک نمونه تازه از لیست، اصطلاحا initialize می‌شود.

سایر متدها نیز برای فراخوانی متدهای موجود در اینترفیس IDbCachedResourceManager پیاده‌سازی شده‌اند. تمامی این متدها دارای سطح دسترسی public هستند. همان‌طور که می‌بینید از خاصیت ResourceName برای مشخص‌کردن نمونه موردنظر استفاده شده است که دلیل آن در قسمت قبل شرح داده شده است.

دقت کنید که برای اطمینان از انتخاب درست همه موارد موجود در شرط انتخاب نمونه موردنظر در متد GetResouceManagers از متد ToLower برای هر دو سمت شرط استفاده شده است.


نکته مهم: درباره علت برگشت یک لیست از متد انتخاب نمونه موردنظر از کلاس DbResourceManager در کد بالا (یعنی متد GetResouceManagers) باید نکته‌ای اشاره شود. در قسمت قبل عنوان شد که سیستم مدیریت منابع ASP.NET نمونه‌های تولیدی از پرووایدرهای منابع را به ازای هر منبع کش می‌کند. اما یک نکته بسیار مهم که باید به آن توجه کرد این است که این کش برای «عبارات بومی‌سازی ضمنی» و نیز «متد مربوط به منابع محلی» موجود در کلاس HttpContext و یا نمونه مشابه آن در کلاس TemplateControl (همان متد GetLocalResourceObject که درباره این متدها در قسمت سوم این سری شرح داده شده است) از یکدیگر جدا هستند و استفاده از هریک از این دو روش موجب تولید یک نمونه مجزا از پرووایدر مربوطه می‌شود که متاسفانه کنترل آن از دست برنامه نویس خارج است. دقت کنید که این اتفاق برای منابع کلی رخ نمی‌دهد.

بنابراین برای پاک کردن مناسب ورودی‌های کش‌شده در کلاس فوق به جای استفاده از متد Single در انتخاب نمونه موردنظر از کلاس DbResourceManager (در متد GetResouceManagers) از متد Where استفاده شده و یک لیست برگشت داده می‌شود. چون با توجه به توضیح بالا امکان وجود دو نمونه DbResourceManager از یک منبع درخواستی محلی در لیست نمونه‌های نگهداری شده در این کلاس وجود دارد.

.

افزودن نمونه‌ها به کلاس DbResourceCacheManager

برای نگهداری نمونه‌های تولید شده از DbResourceManager، باید در یک قسمت مناسب این نمونه‌ها را به لیست مربوطه در کلاس DbResourceCacheManager اضافه کرد. بهترین مکان برای انجام این عمل در کلاس پایه BaseDbResourceProvider است که درخواست تولید نمونه را در متد EnsureResourceManager درصورت نال بودن آن می‌دهد. بنابراین این متد را به صورت زیر تغییر می‌دهیم:

private void EnsureResourceManager()
{
  if (_resourceManager != null) return;
  {
    _resourceManager = CreateResourceManager();
    DbResourceCacheManager.ResourceManagers.Add(_resourceManager);
  }
}

تا اینجا کار پیاده‌سازی امکان مدیریت مقادیر کش‌شده در کتابخانه تولیدی به پایان رسیده است.

استفاده از کلاس DbResourceCacheManager

پس از پیاده‌سازی تمامی موارد لازم، حالتی را درنظر بگیرید که مقادیر ورودی‌های تعریف شده در منبع "dir1/page1.aspx" تغییر کرده است. بنابراین برای بروزرسانی مقادیر کش‌شده کافی است تا از کدی مثل کد زیر استفاده شود:

DbResourceCacheManager.Clear("dir1/page1.aspx");

کد بالا کل ورودی‌های کش‌شده برای منبع "dir1/page1.aspx" را پاک می‌کند. برای پاک کردن کالچر یا یک ورودی خاص نیز می‌توان از کدهایی مشابه زیر استفاده کرد:

DbResourceCacheManager.Clear("Default.aspx", "en-US");
DbResourceCacheManager.Clear("GlobalTexts", "en-US", "Yes");

.

دریافت کد پروژه

کد کامل پروژه DbResourceProvider به همراه مثال و اسکریپت‌های دیتابیسی مربوطه از لینک زیر قابل دریافت است:

DbResourceProvider.rar

برای استفاده از این مثال ابتدا باید کتابخانه Entity Framework (با نام EntityFramework.dll) را مثلا از طریق نوگت دریافت کنید. نسخه‌ای که من در این مثال استفاده کردم نسخه 4.4 با حجم حدود 1 مگابایت است.

نکته: در این کد یک بهبود جزئی اما مهم در کلاس ResourceData اعمال شده است. در قسمت سوم این سری، اشاره شد که نام ورودی‌های منابع Case Sensitive نیست. بنابراین برای پیاده‌سازی این ویژگی، متدهای این کلاس باید به صورت زیر تغییر کنند:

public Resource GetResource(string resourceKey, string culture)
{
  using (var data = new TestContext())
  {
    return data.Resources.SingleOrDefault(r => r.Name.ToLower() == _resourceName.ToLower() && r.Key.ToLower() == resourceKey.ToLower() && r.Culture == culture);
  }
}

public List<Resource> GetResources(string culture)
{
  using (var data = new TestContext())
  {
    return data.Resources.Where(r => r.Name.ToLower() == _resourceName.ToLower() && r.Culture == culture).ToList();
  }
}
تغییرات اعمال شده همان استفاده از متد ToLower در دو طرف شرط مربوط به نام منابع و کلید ورودی‌هاست.


در آینده...

در ادامه مطالب، بحث تهیه پرووایدر سفارشی فایلهای resx. برای پیاده‌سازی امکان به‌روزرسانی در زمان اجرا ارائه خواهد شد. بعد از پایان تهیه این پرووایدر سفارشی، این سری مطالب با ارائه نکات استفاده از این پرووایدرها در ASP.NET MVC پایان خواهد یافت.


منابع

http://msdn.microsoft.com/en-us/library/aa905797.aspx

http://www.west-wind.com/presentations/wwdbresourceprovider

نظرات اشتراک‌ها
HTML Helper برای jqGrid
سوالی که برای من مطرحه اینه که آیا استفاده از JqGrid و کلا کامپوننت هایی که بر اساس Jquery عمل می‌کنند در طراحی سایت به صلاح هست یا خیر و تا چه حد امروزه وب سایت ما باید به javascript وابسته باشه ، که در صورت غیر فعال شدن آن توسط کاربر دچار مشکل نشیم
ممنون میشم از دوستان کسی پاسخ بدهد
اشتراک‌ها
آیا یک توسعه دهنده‌ی وب می‌تواند یک طراح وب هم باشد؟
وقتی شما یک توسعه دهنده‌ی وب هستین معمولاً بصورت انفرادی شروع به ایجاد پروژه جدید خودتون می‌کنید و اینطور به نظر می‌رسه که توانایی این کار رو دارید چون یک فرد باهوش و تکنیکی هستید. درسته؟ اما وقتی صحبت از طراحی می‌شه بهترین و زیرک‌ترین توسعه دهندگان هم دچار مشکل می‌شن. چرا اینطوریه و آیا راهی برای تغییر اون وجود داره؟ 
آیا یک توسعه دهنده‌ی وب می‌تواند یک طراح وب هم باشد؟
نظرات مطالب
تزریق وابستگی‌ها در ASP.NET Core - بخش 4 - طول حیات سرویس ها یا Service Lifetime
در داکیومنت‌ها به این نکته اشاره شده که سرویس‌های scoped، نیازی به طراحی به صورت thread-safe نیستند و امن هستند. 
آیا این به معنی این هست که ذاتا در درون هر وب ریکوئست و در یک ترد، امن هستند ولی به منزله thread-safe بودنشون در برنامه نویس multithread نیست؟