مطالب
تغییرات بوجود آمده در Bundling and Minification -MVC4
همانطور که در پست قبلی اشاره کردم نسخه جدید MVC با تغییرات جهت بهبود کاربری همراه بوده است.
در این پست قصد دارم شما رو با ویژگی جدید  Bundling and Minification بر روی فایلهای  Css و JavaScript آشنا کنم.اگر یک پروژه جدید به وسیله MVC 4 ابجاد کنید شاهد تغییراتی بر روی فایلهای _Layout.cshtml and Global.asax.cs خواهید بود.این تغییر شامل اضافه شدن System.Web.Optimization and BundleTable.Bundles به  Layout.cshtml است.
<link href="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Content/css")"rel="stylesheet" type="text/css" />

<link href="@System.Web.Optimization.BundleTable. Bundles.ResolveBundleUrl("~/Content/themes/base/css")" rel="stylesheet" type="text/css" />

<script src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/Scripts/js")"></script> 
که این Reference ها به عنوان Html Helper ساده هستند.
اگر به کدهای HTML  مرورگر خود نگاه کنید شاهد کدهای زیر خواهید بود.
 <link href="/Content/css?v=ji3nO1pdg6VLv3CVUWntxgZNf1z" rel="stylesheet" type="text/css" />
<link href="/Content/themes/base/css?v=UM624qf1uFt8dYti" rel="stylesheet" type="text/css" />
<script src="/Scripts/js?v=4h5lPNUsLiFoa0vqrItjS2Jp"></script>
که این به معنی فشرده شدن و همچنین باعث میشود اطلاعات ارسالی از IIS Web Server کمتر باشد.
برای استفاده از این ویژگی می‌بایست در Global.asax.cs  در قسمت  Application Startup متد زیر را اضافه کنیم.
BundleTable.Bundles.RegisterTemplateBundles();
برای ساختن Bundle سفارشی به صورت extension method  به صورت زیر عمل میکنیم.
public static void EnableBootstrapBundle(this BundleCollection bundles) {
      var bootstrapCss = new Bundle("~/bootstrap/css", new CssMinify());
      bootstrapCss.AddFile("~/css/bootstrap.css");
      bootstrapCss.AddFile("~/css/bootstrap-responsive.css");
      bootstrapCss.AddFile("~/css/application.css");
      bootstrapCss.AddFile("~/css/prettify.css");

      bundles.Add(bootstrapCss);

      var bootstrapJs = new Bundle("~/bootstrap/js", new JsMinify());
      bootstrapJs.AddFile("~/js/jquery-1.7.1.js");
      bootstrapJs.AddFile("~/js/bootstrap.js");
      bootstrapJs.AddFile("~/js/prettify.js");

      bundles.Add(bootstrapJs);
}
 در مثال بالا CssMinify وJsMinify برای مشخص شدن نوع فشرده سازی ما استفاده شده است.
برای استفاده از این ویژگی می‌بایست در Global.asax.cs  در قسمت  Application Startup متد زیر را اضافه کنیم.
BundleTable.Bundles.EnableBootstrapBundle();
و در فایل Layout.cshtm همانند مثال قبلی کد زیر را اضافه میکنیم.
<link href="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/bootstrap/css")" rel="stylesheet">
<script src="@System.Web.Optimization.BundleTable.Bundles.ResolveBundleUrl("~/bootstrap/js")"></script>
و اگر دوباره به کد تولیدی در مرورگر نگاهی بیاندازیم:
<link href="/bootstrap/css?v=uBlJsuIAGAF93nUTTez8" rel="stylesheet">
<script src="/bootstrap/js?v=O6HaHC8QqtB"></script>
.میتونه تنها ایرادش باشه   CDN عدم پشتیبانی از 
.البت شاید من نتونستم راهی براش پیدا کنم
کار اضافه ای قرار نیست انجام دهیم همه اینها به صورت توکار اعمال میشوند. 
 به طور خلاصه میشه گفت ویژگی بالا به بهبود عملکرد برنامه شما کمک زیادی میکنه.
مطالب
لینک‌های هفته‌ی اول بهمن

وبلاگ‌ها ، سایت‌ها و مقالات ایرانی (داخل و خارج از ایران)

Visual Studio


ASP. Net


طراحی و توسعه وب


اس‌کیوال سرور


سی شارپ


عمومی دات نت


ویندوز


مسایل اجتماعی و انسانی برنامه نویسی


متفرقه

مطالب
بارگذاری بدون وقفه فایل‌های جاوااسکریپت به کمک 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، خیالتان راحت باشد؛ با توجه به این منبع، تقریبا تمام مرورگرها از این ویژگی پشتیبانی می‌کنند.

مطالب
ساخت یک Web API که از عملیات CRUD پشتیبانی می کند
در این مقاله با استفاده از ASP.NET Web API یک سرویس HTTP خواهیم ساخت که از عملیات CRUD پشتیبانی می‌کند. CRUD مخفف Create, Read, Update, Delete است که عملیات پایه دیتابیسی هستند. بسیاری از سرویس‌های HTTP این عملیات را بصورت REST API هم مدل سازی می‌کنند. در مثال جاری سرویس ساده ای خواهیم ساخت که مدیریت لیستی از محصولات (Products) را ممکن می‌سازد. هر محصول شامل فیلدهای شناسه (ID)، نام، قیمت و طبقه بندی خواهد بود.

سرویس ما متدهای زیر را در دسترس قرار می‌دهد.

 Relative URl
 HTTP method
 Action
 api/products/  GET  گرفتن لیست تمام محصولات
 api/products/id/  GET  گرفتن یک محصول بر اساس شناسه
 api/products?category=category/  GET  گرفتن یک محصول بر اساس طبقه بندی
 api/products/  POST  ایجاد یک محصول جدید
 api/products/id/  PUT  بروز رسانی یک محصول
 api/products/id/  DELETE  حذف یک محصول

همانطور که مشاهده می‌کنید برخی از آدرس ها، شامل شناسه محصول هم می‌شوند. بعنوان مثال برای گرفتن محصولی با شناسه 28، کلاینت یک درخواست GET را به آدرس زیر ارسال می‌کند:

http://hostname/api/products/28

منابع

سرویس ما آدرس هایی برای دستیابی به دو نوع منبع (resource) را تعریف می‌کند:

URI
 Resource
 api/products/  لیست تمام محصولات
 api/products/id/  یک محصول مشخص

متد ها

چهار متد اصلی HTTP یعنی همان GET, PUT, POST, DELETE می‌توانند بصورت زیر به عملیات CRUD نگاشت شوند:

  • متد GET یک منبع (resource) را از آدرس تعریف شده دریافت می‌کند. متدهای GET هیچگونه تاثیری روی سرور نباید داشته باشند. مثلا حذف رکوردها با متد اکیدا اشتباه است.
  • متد PUT یک منبع را در آدرس تعریف شده بروز رسانی می‌کند. این متد برای ساختن منابع جدید هم می‌تواند استفاده شود، البته در صورتی که سرور به کلاینت‌ها اجازه مشخص کردن آدرس‌های جدید را بدهد. در مثال جاری پشتیبانی از ایجاد منابع توسط متد PUT را بررسی نخواهیم کرد.
  • متد POST منبع جدیدی می‌سازد. سرور آدرس آبجکت جدید را تعیین می‌کند و آن را بعنوان بخشی از پیام Response بر می‌گرداند.
  • متد DELETE منبعی را در آدرس تعریف شده حذف می‌کند.

نکته: متد PUT موجودیت محصول (product entity) را کاملا جایگزین میکند. به بیان دیگر، از کلاینت انتظار می‌رود که آبجکت کامل محصول را برای بروز رسانی ارسال کند. اگر می‌خواهید از بروز رسانی‌های جزئی/پاره ای (partial) پشتیبانی کنید متد PATCH توصیه می‌شود. مثال جاری متد PATCH را پیاده سازی نمی‌کند.

یک پروژه Web API جدید بسازید

ویژوال استودیو را باز کنید و پروژه جدیدی از نوع ASP.NET MVC Web Application بسازید. نام پروژه را به "ProductStore" تغییر دهید و OK کنید.

در دیالوگ New ASP.NET Project قالب Web API را انتخاب کرده و تایید کنید.

افزودن یک مدل

یک مدل، آبجکتی است که داده اپلیکیشن شما را نمایندگی می‌کند. در ASP.NET Web API می‌توانید از آبجکت‌های Strongly-typed بعنوان مدل هایتان استفاده کنید که بصورت خودکار برای کلاینت به فرمت‌های JSON, XML مرتب (Serialize) می‌شوند. در مثال جاری، داده‌های ما محصولات هستند. پس کلاس جدیدی بنام Product می‌سازیم.

در پوشه Models کلاس جدیدی با نام Product بسازید.

حال خواص زیر را به این کلاس اضافه کنید.

namespace ProductStore.Models
{
    public class Product
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public string Category { get; set; }
        public decimal Price { get; set; }
    }
}

افزودن یک مخزن

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

روی پوشه Models کلیک راست کنید و گزینه Add, New Item را انتخاب نمایید.

نوع آیتم جدید را Interface انتخاب کنید و نام آن را به IProductRepository تغییر دهید.

حال کد زیر را به این اینترفیس اضافه کنید.

namespace ProductStore.Models
{
    public interface IProductRepository
    {
        IEnumerable<Product> GetAll();
        Product Get(int id);
        Product Add(Product item);
        void Remove(int id);
        bool Update(Product item);
    }
}
حال کلاس دیگری با نام ProductRepository در پوشه Models ایجاد کنید. این کلاس قرارداد IProductRepository را پیاده سازی خواهد کرد. کد زیر را به این کلاس اضافه کنید.

namespace ProductStore.Models
{
    public class ProductRepository : IProductRepository
    {
        private List<Product> products = new List<Product>();
        private int _nextId = 1;

        public ProductRepository()
        {
            Add(new Product { Name = "Tomato soup", Category = "Groceries", Price = 1.39M });
            Add(new Product { Name = "Yo-yo", Category = "Toys", Price = 3.75M });
            Add(new Product { Name = "Hammer", Category = "Hardware", Price = 16.99M });
        }

        public IEnumerable<Product> GetAll()
        {
            return products;
        }

        public Product Get(int id)
        {
            return products.Find(p => p.Id == id);
        }

        public Product Add(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            item.Id = _nextId++;
            products.Add(item);
            return item;
        }

        public void Remove(int id)
        {
            products.RemoveAll(p => p.Id == id);
        }

        public bool Update(Product item)
        {
            if (item == null)
            {
                throw new ArgumentNullException("item");
            }
            int index = products.FindIndex(p => p.Id == item.Id);
            if (index == -1)
            {
                return false;
            }
            products.RemoveAt(index);
            products.Add(item);
            return true;
        }
    }
}

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


افزودن یک کنترلر Web API

اگر قبلا با ASP.NET MVC کار کرده باشید، با مفهوم کنترلر‌ها آشنایی دارید. در ASP.NET Web API کنترلر‌ها کلاس هایی هستند که درخواست‌های HTTP دریافتی از کلاینت را به اکشن متدها نگاشت می‌کنند. ویژوال استودیو هنگام ساختن پروژه شما دو کنترلر به آن اضافه کرده است. برای مشاهد آنها پوشه Controllers را باز کنید.

  • HomeController یک کنترلر مرسوم در ASP.NET MVC است. این کنترلر مسئول بکار گرفتن صفحات وب است و مستقیما ربطی به Web API ما ندارد.
  • ValuesController یک کنترلر نمونه WebAPI است.

کنترلر ValuesController را حذف کنید، نیازی به این آیتم نخواهیم داشت. حال برای اضافه کردن کنترلری جدید مراحل زیر را دنبال کنید.

در پنجره Solution Explorer روی پوشه Controllers کلیک راست کرده و گزینه Add, Controller را انتخاب کنید.

در دیالوگ Add Controller نام کنترلر را به ProductsController تغییر داده و در قسمت Scaffolding Options گزینه Empty API Controller را انتخاب کنید.

حال فایل کنترلر جدید را باز کنید و عبارت زیر را به بالای آن اضافه نمایید.

using ProductStore.Models;
یک فیلد هم برای نگهداری وهله ای از IProductRepository اضافه کنید.
public class ProductsController : ApiController
{
    static readonly IProductRepository repository = new ProductRepository();
}

فراخوانی ()new ProductRepository طراحی جالبی نیست، چرا که کنترلر را به پیاده سازی بخصوصی از این اینترفیس گره می‌زند. بهتر است از تزریق وابستگی (Dependency Injection) استفاده کنید. برای اطلاعات بیشتر درباره تکنیک DI در Web API به این لینک مراجعه کنید.


گرفتن منابع

ProductStore API اکشن‌های متعددی در قالب متدهای HTTP GET در دسترس قرار می‌دهد. هر اکشن به متدی در کلاس ProductsController مرتبط است.

 Relative URl
 HTTP Method
 Action
 api/products/  GET  دریافت لیست تمام محصولات
 api/products/id/  GET  دریافت محصولی مشخص بر اساس شناسه
 api/products?category=category/  GET  دریافت محصولات بر اساس طبقه بندی

برای دریافت لیست تمام محصولات متد زیر را به کلاس ProductsController اضافه کنید.

public class ProductsController : ApiController
{
    public IEnumerable<Product> GetAllProducts()
    {
        return repository.GetAll();
    }
    // ....
}
نام این متد با "Get" شروع می‌شود، پس بر اساس قراردادهای توکار پیش فرض به درخواست‌های HTTP GET نگاشت خواهد شد. همچنین از آنجا که این متد پارامتری ندارد، به URl ای نگاشت می‌شود که هیچ قسمتی با نام مثلا id نداشته باشد.

برای دریافت محصولی مشخص بر اساس شناسه آن متد زیر را اضافه کنید.
public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound); 
    }
    return item;
}

نام این متد هم با "Get" شروع می‌شود اما پارامتری با نام id دارد. این پارامتر به قسمت id مسیر درخواست شده (request URl) نگاشت می‌شود. تبدیل پارامتر به نوع داده مناسب (در اینجا int) هم بصورت خودکار توسط فریم ورک ASP.NET Web API انجام می‌شود.

متد GetProduct در صورت نامعتبر بودن پارامتر id استثنایی از نوع HttpResponseException تولید می‌کند. این استثنا بصورت خودکار توسط فریم ورک Web API به خطای 404 (Not Found) ترجمه می‌شود.

در آخر متدی برای دریافت محصولات بر اساس طبقه بندی اضافه کنید.
public IEnumerable<Product> GetProductsByCategory(string category)
{
    return repository.GetAll().Where(
        p => string.Equals(p.Category, category, StringComparison.OrdinalIgnoreCase));
}

اگر آدرس درخواستی پارامتر‌های query string داشته باشد، Web API سعی می‌کند پارامتر‌ها را با پارامتر‌های متد کنترلر تطبیق دهد. بنابراین درخواستی به آدرس "api/products?category=category" به این متد نگاشت می‌شود.

ایجاد منبع جدید

قدم بعدی افزودن متدی به ProductsController برای ایجاد یک محصول جدید است. لیست زیر پیاده سازی ساده ای از این متد را نشان می‌دهد.

// Not the final implementation!
public Product PostProduct(Product item)
{
    item = repository.Add(item);
    return item;
}
به دو چیز درباره این متد توجه کنید:

  • نام این متد با "Post" شروع می‌شود. برای ساختن محصولی جدید کلاینت یک درخواست HTTP POST ارسال می‌کند.
  • این متد پارامتری از نوع Product می‌پذیرد. در Web API پارامترهای پیچیده (complex types) بصورت خودکار با deserialize کردن بدنه درخواست بدست می‌آیند. بنابراین در اینجا از کلاینت انتظار داریم که آبجکتی از نوع Product را با فرمت XML یا JSON ارسال کند.

پیاده سازی فعلی این متد کار می‌کند، اما هنوز کامل نیست. در حالت ایده آل ما می‌خواهیم پیام HTTP Response موارد زیر را هم در بر گیرد:

  • Response code: بصورت پیش فرض فریم ورک Web API کد وضعیت را به 200 (OK) تنظیم می‌کند. اما طبق پروتکل HTTP/1.1 هنگامی که یک درخواست POST منجر به ساخته شدن منبعی جدید می‌شود، سرور باید با کد وضعیت 201 (Created) پاسخ دهد.
  • Location: هنگامی که سرور منبع جدیدی می‌سازد، باید آدرس منبع جدید را در قسمت Location header پاسخ درج کند.

ASP.NET Web API دستکاری پیام HTTP response را آسان می‌کند. لیست زیر پیاده سازی بهتری از این متد را نشان می‌دهد.

public HttpResponseMessage PostProduct(Product item)
{
    item = repository.Add(item);
    var response = Request.CreateResponse<Product>(HttpStatusCode.Created, item);

    string uri = Url.Link("DefaultApi", new { id = item.Id });
    response.Headers.Location = new Uri(uri);
    return response;
}
توجه کنید که حالا نوع بازگشتی این متد HttpResponseMessage است. با بازگشت دادن این نوع داده بجای Product، می‌توانیم جزئیات پیام HTTP response را کنترل کنیم. مانند تغییر کد وضعیت و مقدار دهی Location header.

متد CreateResponse آبجکتی از نوع HttpResponseMessage می‌سازد و بصورت خودکار آبجکت Product را مرتب (serialize) کرده و در بدنه پاسخ می‌نویسد. نکته دیگر آنکه مثال جاری، مدل را اعتبارسنجی نمی‌کند. برای اطلاعات بیشتر درباره اعتبارسنجی مدل‌ها در Web API به این لینک مراجعه کنید.


بروز رسانی یک منبع

بروز رسانی یک محصول با PUT ساده است.

public void PutProduct(int id, Product product)
{
    product.Id = id;
    if (!repository.Update(product))
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
}
نام این متد با "Put" شروع می‌شود، پس Web API آن را به درخواست‌های HTTP PUT نگاشت خواهد کرد. این متد دو پارامتر می‌پذیرد، یکی شناسه محصول مورد نظر و دیگری آبجکت محصول آپدیت شده. مقدار پارامتر id از مسیر (route) دریافت می‌شود و پارامتر محصول با deserialize کردن بدنه درخواست.


حذف یک منبع

برای حذف یک محصول متد زیر را به کلاس ProductsController اضافه کنید.

public void DeleteProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }

    repository.Remove(id);
}
اگر یک درخواست DELETE با موفقیت انجام شود، می‌تواند کد وضعیت 200 (OK) را بهمراه بدنه موجودیتی که وضعیت فعلی را نمایش می‌دهد برگرداند. اگر عملیات حذف هنوز در حال اجرا است (Pending) می‌توانید کد 202 (Accepted) یا 204 (No Content) را برگردانید.

در مثال جاری متد DeleteProduct نوع void را بر می‌گرداند، که فریم ورک Web API آن را بصورت خودکار به کد وضعیت 204 (No Content) ترجمه می‌کند.
اشتراک‌ها
Package Managerی برای ویرایشگر متن Sublime Text
Sublime Text یک ویرایشگر متنی قدرتمند و در عین حال با حجم سبک برای کدنویسی و حتی نوشتن متن‌های معمولی است. جاهایی که نیاز به ویژوال استودیو نیست میشه اونو نصب و استفاده کرد. همچنین با استفاده از Package Manager اون میشه امکانات بیشتری رو بهش اضافه کرد.
اما در حال حاضر آخرین نسخه از این ویرایشگر حروف زبان‌های راست به چپ مثل پارسی رو درست نمایش نمیده. من از نسخه 3 و Build 3092 استفاده می‌کنم.
Package Managerی برای ویرایشگر متن Sublime Text
مطالب
پیاده سازی Unobtrusive Ajax در ASP.NET Core 1.0
پیاده سازی Unobtrusive Ajax را در ASP.NET MVC 5.x، می‌توانید در مطلب «ASP.NET MVC #21» مطالعه کنید. HTML Helpers مرتبط با Ajax، به طور کامل از ASP.NET Core 1.0 حذف شده‌اند. اما این مورد به این معنا نیست که نمی‌توان Unobtrusive Ajax را در ASP.NET Core که تمرکزش بیشتر بر روی Tag Helpers جدید هست تا HTML Helpers قدیمی، پیاده سازی کرد.


Unobtrusive Ajax چیست؟

در حالت معمولی، با استفاده از متد ajax جی‌کوئری، کار ارسال غیرهمزمان اطلاعات، به سمت سرور صورت می‌گیرد. چون در این روش کدهای جی‌کوئری داخل صفحات برنامه‌های ما قرار می‌گیرند، به این روش، «روش چسبنده» می‌گویند. اما با استفاده از افزونه‌ی «jquery.unobtrusive-ajax.min.js» مایکروسافت، می‌توان این کدهای چسبنده را تبدیل به کدهای غیرچسنبده یا Unobtrusive کرد. در این حالت، پارامترهای متد ajax، به صورت ویژگی‌ها (attributes) به شکل data-ajax به المان‌های مختلف صفحه اضافه می‌شوند و به این ترتیب، افزونه‌ی یاد شده به صورت خودکار با یافتن مقادیر ویژگی‌های data-ajax، این المان‌ها را تبدیل به المان‌های ای‌جکسی می‌کند. در این حالت به کدهایی تمیزتر و عاری از متدهای چسبنده‌ی ajax قرار گرفته‌ی در داخل صفحات وب خواهیم رسید.
روش طراحی Unobtrusive را در کتابخانه‌های معروفی مانند بوت استرپ هم می‌توان مشاهده کرد.


پیشنیازهای فعال سازی Unobtrusive Ajax در ASP.NET Core 1.0

توزیع افزونه‌ی «jquery.unobtrusive-ajax.min.js» مایکروسافت، از طریق bower صورت می‌گیرد که پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 14 - فعال سازی اعتبارسنجی ورودی‌های کاربران» با آن آشنا شدیم. در اینجا نیز برای دریافت آن، تنها کافی است فایل bower.json را به نحو ذیل تکمیل کرد:
{
  "name": "asp.net",
  "private": true,
  "dependencies": {
   "bootstrap": "3.3.6",
   "jquery": "2.2.0",
   "jquery-validation": "1.14.0",
   "jquery-validation-unobtrusive": "3.2.6",
   "jquery-ajax-unobtrusive": "3.2.4"
  }
}
و پس از آن فایل bundleconfig.json مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 21 - بررسی تغییرات Bundling و Minification» یک چنین شکلی را پیدا می‌کند:
[
  {
   "outputFileName": "wwwroot/css/site.min.css",
   "inputFiles": [
    "bower_components/bootstrap/dist/css/bootstrap.min.css",
    "content/site.css"
   ]
  },
  {
   "outputFileName": "wwwroot/js/site.min.js",
   "inputFiles": [
    "bower_components/jquery/dist/jquery.min.js",
    "bower_components/jquery-validation/dist/jquery.validate.min.js",
    "bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js",
    "bower_components/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.min.js",
    "bower_components/bootstrap/dist/js/bootstrap.min.js"
   ],
   "minify": {
    "enabled": true,
    "renameLocals": true
   },
   "sourceMap": false
  }
]
در اینجا فایل‌های css و اسکریپت مورد نیاز برنامه، به ترتیب اضافه شده و یکی خواهند شد. خروجی نهایی آن‌ها به شکل زیر در صفحات وب مورد استفاده قرار می‌گیرند:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
    <link href="~/css/site.min.css" rel="stylesheet" />
</head>
<body>
    <div>
        <div>
                @RenderBody()
        </div>
    </div>

    <script src="~/js/site.min.js" type="text/javascript" asp-append-version="true"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>
در اینجا تنها دو فایل نهایی این عملیات، یعنی css/site.min.css و js/site.min.js به صفحه الحاق شده‌اند که حاوی تمام پیشنیازهای اسکریپتی و شیوه‌نامه‌های برنامه هستند و در این حالت دیگر نیاز به افزودن آن‌ها به دیگر صفحات سایت نیست.


استفاده از معادل‌های واقعی Unobtrusive Ajax در ASP.NET Core 1.0

واقعیت این است که HTML Helper ای‌جکسی حذف شده‌ی از ASP.NET Core 1.0، کاری بجز افزودن ویژگی‌های data-ajax را که توسط افزونه‌ی jquery.unobtrusive-ajax.min.js پردازش می‌شوند، انجام نمی‌دهد و این افزونه مستقل است از مباحث سمت سرور و به نگارش خاصی از ASP.NET گره نخورده است. بنابراین در اینجا تنها کاری را که باید انجام داد، استفاده از همان ویژگی‌های اصلی است که این افزونه قادر به شناسایی آن‌ها است.
خلاصه‌ی آن‌ها را جهت انتقال کدهای قدیمی و یا تهیه‌ی کدهای جدید، در جدول ذیل می‌توانید مشاهده کنید:

 HTML attribute   AjaxOptions 
 data-ajax-confirm   Confirm 
 data-ajax-method   HttpMethod 
 data-ajax-mode   InsertionMode 
 data-ajax-loading-duration   LoadingElementDuration 
 data-ajax-loading   LoadingElementId 
 data-ajax-begin   OnBegin 
 data-ajax-complete   OnComplete 
 data-ajax-failure   OnFailure 
 data-ajax-success   OnSuccess 
 data-ajax-update   UpdateTargetId 
 data-ajax-url   Url 
   
در ASP.NET Core 1.0، به علت حذف متدهای کمکی Ajax دیگر خبری از AjaxOptions نیست. اما اگر علاقمند به انتقال کدهای قدیمی به ASP.NET Core 1.0 هستید، معادل‌های اصلی این پارامترها را می‌توانید در ستون HTML attribute مشاهده کنید.

چند نکته:
- اگر قصد استفاده‌ی از این ویژگی‌ها را دارید، باید ویژگی "data-ajax="true را نیز حتما قید کنید تا سیستم Unobtrusive Ajax فعال شود.
- ویژگی data-ajax-mode تنها با ذکر data-ajax-update (و یا همان UpdateTargetId پیشین) معنا پیدا می‌کند.
- ویژگی data-ajax-loading-duration نیاز به ذکر data-ajax-loading (و یا همان LoadingElementId پیشین) را دارد.
- ویژگی data-ajax-mode مقادیر before، after و replace-with را می‌پذیرد. اگر قید نشود، کل المان با data دریافتی جایگزین می‌شود.
- سه callback قابل تعریف data-ajax-complete، data-ajax-failure و data-ajax-success، یک چنین پارامترهایی را از سمت سرور در اختیار کلاینت قرار می‌دهند:

parameters  
 Callback  
 xhr, status   data-ajax-complete 
 data, status, xhr   data-ajax-success 
 xhr, status, error   data-ajax-failure 

برای مثال می‌توان ویژگی data-ajax-success را به نحو ذیل در سمت کلاینت مقدار دهی کرد:
 data-ajax-success = "myJsMethod"
این متد جاوا اسکریپتی یک چنین امضایی را دارد:
  function myJsMethod(data, status, xhr) {
}
در این حالت در سمت سرور، پارامتر data در یک اکشن متد، به صورت ذیل مقدار دهی می‌شود:
 return Json(new { param1 = 1, param2 = 2, ... });
و در سمت کلاینت در متد myJsMethod این پارامترها را به صورت data.param1 می‌توان دریافت کرد.


مثال‌هایی از افزودن ویژگی‌های data-ajax به المان‌های مختلف

 در حالت استفاده از Form Tag Helpers که در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 12 - معرفی Tag Helpers» بررسی شدند، یک فرم ای‌جکسی، چنین تعاریفی را پیدا خواهد کرد:
با این ViewModel فرضی
using System.ComponentModel.DataAnnotations;
 
namespace Core1RtmEmptyTest.ViewModels.Account
{
    public class RegisterViewModel
    {
        [Required]
        [EmailAddress]
        [Display(Name = "Email")]
        public string Email { get; set; }
    }
}
که در View متناظر Ajax ایی ذیل استفاده شده‌است:
@using Core1RtmEmptyTest.ViewModels.Account
@model RegisterViewModel
@{
}
 
<form method="post"
      asp-controller="TestAjax"
      asp-action="Index"
      asp-route-returnurl="@ViewBag.ReturnUrl"
      class="form-horizontal"
      role="form"
      data-ajax="true"
      data-ajax-loading="#Progress"
      data-ajax-success="myJsMethod">
 
    <input asp-for="Email" class="form-control" />
    <span asp-validation-for="Email" class="text-danger"></span>
 
    <button type="submit">ارسال</button>
 
    <div id="Progress" style="display: none">
        <img src="images/loading.gif" alt="loading..." />
    </div>
</form>
 
@section scripts{
    <script type="text/javascript">
        function myJsMethod(data, status, xhr) {
            alert(data.param1);
        }
    </script>
}
در اینجا تمام تعاریف مانند قبل است؛ تنها سه ویژگی data-ajax جهت فعال سازی jquery-ajax-unobtrusive به فرم اضافه شده‌اند. همچنین یک callback دریافت پیام موفقیت آمیز بودن عملیات Ajax ایی نیز تعریف شده‌است.

این View از کنترلر ذیل استفاده می‌کند:
using Core1RtmEmptyTest.ViewModels.Account;
using Microsoft.AspNetCore.Mvc;
 
namespace Core1RtmEmptyTest.Controllers
{
    public class TestAjaxController : Controller
    {
 
        public IActionResult Index()
        {
            return View();
        }
 
        [HttpPost]
        public IActionResult Index([FromForm]RegisterViewModel vm)
        {
            var ajax = isAjax();
            if (ajax)
            {
                // it's an ajax post
            }
 
 
            if (ModelState.IsValid)
            {
                //todo: save data
 
                return Json(new { param1 = 1, param2 = 2 });
            }
            return View();
        }
 
        private bool isAjax()
        {
            return Request?.Headers != null && Request.Headers["X-Requested-With"] == "XMLHttpRequest";
        }
    }
}
به ASP.NET Core 1.0، متد کمکی IsAjax اضافه نشده‌است؛ اما تعریف آن‌را در این کنترلر مشاهده می‌کنید. در مورد قید FromForm در ادامه توضیح داده خواهد شد (هرچند در این مورد خاص، حالت پیش فرض است و الزامی به قید آن نیست).

و یا Action Link ای‌جکسی نیز به صورت خلاصه به این نحو قابل تعریف است:
<div id="EmployeeInfo">
<a 
 asp-controller="MyController" asp-action="MyAction"
 data-ajax="true" 
 data-ajax-loading="#Progress" 
 data-ajax-method="POST" 
 data-ajax-mode="replace" 
 data-ajax-update="#EmployeeInfo">
 Get Employee-1 info
</a>

  <div id="Progress" style="display: none">
    <img src="images/loading.gif" alt="loading..."  />
  </div>
</div>


نکته‌ای در مورد اکشن متدهای ای‌جکسی در ASP.NET Core 1.0

همانطور که در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 18 - کار با ASP.NET Web API»، قسمت «تغییرات Model binding پیش فرض، برای پشتیبانی از ASP.NET MVC و ASP.NET Web API» نیز ذکر شد:
public IActionResult Index([FromBody] MyViewModel vm)
{
   return View();
}
ذکر ویژگی FromBody در اینجا الزامی است. از این جهت که اطلاعات با فرمت JSON، از قسمت body درخواست استخراج و به MyViewModel بایند خواهند شد (در حالت dataType: json). و اگر dataType : application/x-www-form-urlencoded; charset=utf-8 بود (مانند حالت پیش فرض Unobtrusive Ajax)، باید از ویژگی FromForm استفاده شود. در غیر اینصورت در سمت سرور نال دریافت خواهیم کرد.
مطالب
توسعه سرویس‌های مبتنی بر REST در AngularJs با استفاده از RestAngular : بخش دوم
در بخش پیشین  کلیات کتابخانه‌ی Restangular  را بررسی کردیم. در این بخش قصد داریم تا در طی یک پروژه، امکانات و قابلیت‌های بی‌نظیر این سرویس را در یک پروژه‌ی واقعی مشاهده کنیم.

کلیات پروژه

در این پروژه قصد داریم تا لیست کتاب‌های یک کتابخانه را نمایش دهیم. این کتابها قابلیت ویرایش نام دارند و همچنین شما می‌توانید کتابهای جدیدی را به لیست کتابها اضافه نمایید. تصویر زیر خروجی این پروژه است:

پایگاه داده‌ی برنامه با نام LibDb درون پوشه‌ی app_data قرار داده شده‌است. این پایگاه داده تنها دارای یک جدول است؛ با نام Books که دیاگرام آن را در شکل زیر مشاهده می‌کنید:


پیاده سازی

در ابتدا یک پروژه‌ی Empty را با رفرنس web API ایجاد می‌کنیم. حال درون فایل WebApiConfig.cs تکه کد زیر را اضافه می‌کنیم. این تکه کد فیلدها و آبجکت‌هایی را که از سمت سرور بازگشت داده می‌شوند، به صورت camelCase تبدیل می‌کند.
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();

jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
در ادامه مدل edmx را ساخته و پس از آن Books Web Api را درون پوشه‌ی کنترلر ایجاد می‌کنیم. این کنترلر به صورت Default web Api Scaffold ساخته شده است و به دلیل اینکه به بحث ما مرتبط نیست؛ از توضیح بیشتر آن می‌گذریم.
حال پوشه‌ای را با نام app برای نگه داری فایل‌های AngularJs اضافه می‌کنیم و درون آن فایل app.js را ایجاد می‌کنیم:
var angularExample = angular.module('angularexample', ["restangular"])
angularExample.config(["RestangularProvider", function (RestangularProvider) {
    //RestangularProvider.setRestangularFields({
    //    id: "id"
    //});
    RestangularProvider.setBaseUrl('/api');
}]);

angularExample.controller("MainCtrl", ["Restangular", "$scope", function (Restangular, $scope) {
    var resource = Restangular.all('books');
    resource.getList().then(function (response) {
        $scope.books = response;
    });
    $scope.add = function () {
        resource.post($scope.newBook).then(function (newResource) {
            $scope.books.push(newResource);
        })
    }
}]);
محتویات فایل app.js را مشاهده می‌کنید. در ادامه درباره‌ی این قسمت بیشتر صحبت می‌کنیم.
حال در روت پروژه فایل index.html را ایجاد می‌کنیم:
<!DOCTYPE html>
<html ng-app="angularexample" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Rest Angular Sample</title>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js"></script>
    <script src="http://cdn.jsdelivr.net/underscorejs/1.5.1/underscore-min.js"></script>
    <script src="/Scripts/restangular.min.js"></script>
    <script src="/app/app.js"></script>
</head>
<body>
    <div ng-controller="MainCtrl">
        <div ng-repeat="item in books">
            name is: {{item.name}}<br />
            change name: <input type="text" ng-model="item.name" /><button type="submit" ng-click="item.put();">update</button>
        </div>
        <div>
            add new: <br />
            <input type="text" ng-model="newBook.name" /><button type="submit" ng-click="add()">add</button>
        </div>
    </div>
</body>
و در نهایت در پوشه‌ی Scripts فایل‌های سرویس Restangular را قرار می‌دهیم.
تا به اینجای کار تمامی کارهای مورد نیاز تشکیل پروژه را انجام داده‌ایم. حال به بررسی بیشتر سرویس‌های این پروژه می‌پردازیم؛ یعنی کدهای درون فایل app.js.
ببینیم که برای دریافت لیست تمامی کتابها، ما چه کارهایی را انجام داده‌ایم! به تکه کد زیر دقت کنید:
    var resource = Restangular.all('books');
    resource.getList().then(function (response) {
        $scope.books = response;
    });
این تمامی آن چیزی است که ما برای دریافت لیست تمامی کتابها انجام داده‌ایم. به همین سادگی! در خط اول از متد all که در بخش قبل توضیح مختصری راجع به آن داده بودم برای دریافت لیست تمامی کتابها استفاده کردم که پارامتر درون آن آدرس Web Api است. البته همانطور که می‌دانید در ASP.NET Web Api ما همیشه به base address یک api نیز اضافه می‌کنیم. حال اینکه چرا api در اینجا نیامده در ادامه و در بخش تنظیمات کلی Restangular توضیح می‌دهم. در خط دوم نیز از اشاره‌گر resources متد getList را فراخوانی کرده و لیست کتابها را در response دریافت می‌کنیم.
پس از آن می‌خواهیم ببینیم که عملیات ایجاد یک کتاب جدید چگونه انجام می‌گردد. تکه کد زیر این عملیات را انجام می‌دهد:
    $scope.add = function () {
        resource.post($scope.newBook).then(function (newResource) {
            $scope.books.push(newResource);
        })
    }
ما یک متد با نام add ایجاد کرده‌ایم که در سمت View توسط دایرکتیو ng-click آن را فراخوانی می‌کنیم.
همانطور که مشاهده میکنید درون app.js متدی برای update موارد قبلی نیست. بیایید سری به View بزنیم. به المنت div زیر توجه کنید. همانطور که می‌بینید تمامی عملیات update با یک دستور ساده item.put حل و فصل شده.
        <div ng-repeat="item in books">
            name is: {{item.name}}<br />
            change name: <input type="text" ng-model="item.name" /><button type="submit" ng-click="item.put();">update</button>
        </div>
تمامی آنچه که گفته شد تنها بخشی از قابلیت‌های شگفت انگیز این افزونه بود. امیدوارم که مطلب مفید واقع شده باشد. سورس این پروژه را می‌توانید از لینک زیر دریافت کنید.

سورس پروژه: RestangularSample.rar  

مطالب
آشنایی با ساختار IIS قسمت پنجم
در مطالب قبلی در مورد ماژولار بودن IIS زیاد صحبت کردیم، ولی اجازه بدهید این مورد را به صورت کاربردی‌تر و موشکافانه‌تر بررسی کنیم. برای اینکه به مشکلی در طول این سری از مطالب برنخورید، IIS را به صورت کامل یعنی full feature نصب نمایید. از بخش control panel>programs & features>Turn Windows features on or off اقدام نمایید و هرچه زیر مجموعه Internet information service هست را برگزینید. در صورتی که از نسخه‌های ویندوز سرور 2008 استفاده می‌کنید از طریق server manager>roles>web server اقدام نمایید.
برای نصب یک ماژول باید دو مرحله را انجام داد:
  1. نصب ماژول
  2. فعال سازی ماژول
نکته ای که در مورد ماژول‌های native وجود دارد این هست که این ماژول‌ها دسترسی بدون محدودیتی به منابع سروری دارند و از این رو حتما باید این نکته را دقت کنید که ماژول native شما از یک منبع مورد اعتماد دریافت شده باشد.

نصب یک native module
برای نصب می‌توانید یکی از سه راه زیر را استفاده کنید:
  1. ویرایش دستی فایل کانفیگ و از نسخه IIS7.5 به بعد هم می‌توانید از configuration editor هم استفاده کنید.
  2. استفاده از محیط گرافیکی IIS
  3. استفاده از خط فرمان با دستور Appcmd
مزیت روش دستی این هست که شما دقیقا می‌دانید در پشت صحنه چه اتفاقی می‌افتد و نتیجه هر کدام از این سه روش، اضافه شدن یک مدخل ورودی به تگ <globalmodules> است. برای اعمال تغییرات، مسیر زیر را بروید:
%windir%\system32\inetsrv\config\applicationhost.config
کسی که نیاز به دسترسی به این مسیر و انجام تغییرات دارد باید در بالاترین سطح مدیریتی سرور باشد.
اگر فایل را باز کنید و تگ globalmodule را پیدا کنید متوجه می‌شوید که تمامی ماژول‌ها در این قسمت معرفی شده‌اند و برای خود یک مدخل ورودی یا همان تگ add را دارند که در آن مسیر فایل dll هم ذکر شده است:
<globalModules> 
 
 <addname="DefaultDocumentModule"image="%windir%\system32\inetsrv\defdoc.dll"/> 
 
 <addname="DirectoryListingModule"image="%windir%\system32\inetsrv\dirlist.dll"/> 

<add name="StaticFileModule"image="%windir%\system32\inetsrv\static.dll"/>
...

 </globalModules>

برای حذف یا جایگزینی یک ماژول به راحتی می‌توانید مدخل ورودی یک ماژول را به صورت دستی حذف نمایید و برای جایگزینی هم بعد از حذف، ماژول خود را معرفی کنید. ولی توجه داشته باشید که این حذف به معنی حذف این ماژول از تمامی اپلیکیشن‌های موجود بر روی IIS هست و سپس اضافه کردن یک ماژول به این بخش. همچنین اگر قصد شما فقط حذف یک ماژول از روی یکی از اپلیکشن‌ها باشد باید از طریق فایل کانفیگ سایت از مسیر تگ‌های <system.webserver><modules> و با استفاده از تگ‌های add و remove به معرفی یک ماژول مختص این اپلیکیشن و یا حذف یک ماژول خاص اقدام نمایید.

PreConditions
این ویژگی می‌تواند در خط معرفی ماژول، مورد استفاده قرار بگیرد. اگر به فایل نگاه کنید می‌بینید که در بعضی خطوط این ویژگی ذکر شده است. تعریف این ویژگی به هسته IIS می‌گوید که این ماژول در چه مواردی و به چه شیوه ای باید به کار گرفته شود.
 <add name="ManagedEngine64" image="%windir%\Microsoft.NET\Framework64\v2.0.50727\webengine.dll" preCondition="integratedMode,runtimeVersionv2.0,bitness64" /> 
 
 <add name="ManagedEngine" image="%windir%\Microsoft.NET\Framework\v2.0.50727\webengine.dll" preCondition="integratedMode,runtimeVersionv2.0,bitness32" /> 
 
<add name="ManagedEngineV4.0_32bit" image="%windir%\Microsoft.NET\Framework\v4.0.30319\webengine4.dll" preCondition="integratedMode,runtimeVersionv4.0,bitness32" /> 
 
<add name="ManagedEngineV4.0_64bit" image="%windir%\Microsoft.NET\Framework64\v4.0.30319\webengine4.dll" preCondition="integratedMode,runtimeVersionv4.0,bitness64" />

مقادیری که precondition میتواند بگیر شامل موارد زیر هستند:
bitness
این گزینه به دو صورت bitness32 و bitness64 یافت می‌شود. امروزه پردازنده‌های 64 بیتی بسیار متداول شده اند و بسیاری از تولید کنندگان دارند به سمت عرضه ابزارهای 64 بیتی رو می‌آورند و به زودی عرضه‌های 32 بیتی را متوقف می‌کنند و به سمت سیستم عامل‌های 64 بیت سوییچ خواند کرد ولی باز هم هنوز برنامه‌های 32 بیتی زیادی هستند که مورد استفاده قرار می‌گیرند و نمی‌توان آن‌ها را نادیده گرفت. برای همین ویندوزهای 64 بیتی مایکروسافت در کنار محیط 64 بیتی‌شان از یک محیط 32 بیت به اسم WOW64 استفاده می‌کنند. در این حالت این امتیاز به شما داده می‌شود که از پروسه‌های کارگر 32 بیتی در کنار پروسه‌های کارگر 64 بیتی استفاده کنید و PreCondition به bitness32 یا bitness64 تنظیم می‌شود تا از صحت بارگزاری dll در یک محیط درست مطمئن شود. در صورتی که این خصوصیت ذکر نشود یک هندلر 32 بیتی و 64 بیتی و یک module map اجرا می‌شود.

Runtime version
اگر ماژول خاصی برای اجرا به ورژن خاصی از net framwork. نیاز دارد، این ویژگی ذکر می‌شود. در صورتی که ماژولی قصد اجرای بر روی فریم ورک اشتباهی داشته باشد سبب خطا خواهد شد.

Mana gedHandler 
با معرفی IIS7 ما با یک مدل توسعه پذیر روبرو شدیم و می‌توانستیم ماژول‌ها و هندلرهای خود را بنویسیم و مستقیما در Pipeline قرار دهیم ولی سوییچ کردن بین دو بخش کدهای مدیریت شده و native یک عمل سنگین برای سیستم به شمار می‌آید و به منظور کاهش این بار گزینه managedhandler قرار داده شده است تا تعیین کند مواقعی که درخواست نیازی به این ماژول ندارد، این ماژول اجرا نگردد. به عنوان مثال فایل‌های ایستا چون jpg یا html و... شامل این ماژول نخواهند شد. واضح‌ترین مثال در این زمینه Forms Authentication می‌باشد و موقعی اجرا می‌شوند که درخواست فایل‌های aspx شده باشد و اگر یک فایل html را درخواست کنید این ماژول امنیتی روی آن اثری ندارد و عملیات شناسایی هویت روی آن اجرا نمی‌شود و اگر می‌خواهید روی همه فایل‌ها، این عملیات شناسایی انجام شود باید خصوصیت "precondition="managedhandler حذف شود.
در صورتی که تگ module را به صورت زیر بنویسید تمامی ماژول‌ها برای تمامی درخواست‌ها اجرا خواهد شد، صرف نظر از اینکه آیا ماژولی به صورت "precondition="managedmodule مقداردهی شده است یا خیر.
<modules runAllManagedModulesForAllRequests="true"/>
 
The Mode Precondition 
تا به الان گفتیم که چگونه میتوانیم یک ماژول و یا هندلر مدیریت شده‌ای را به Pipeline اضافه کنیم؛ ولی IIS7 به بالا نیاز دارد که تا پروسه‌های کارگر را به روشی خاص به این منظور اجرا کند و فریم ورک دات نت را برای اجرای آن‌ها بارگزاری کند. همچنین به اجرای ماژولی به اسم webengine.dll برای مدیریت  ماژولهای مدیریت شده نیازمند است و خود IIS در مورد کدهای مدیریت شده چیزی متوجه نمی‌شود. پس ما برای اینکه IIS را متوجه این موضوع نمائیم، باید Integrated mode را به آن معرفی کنیم.
در نسخه‌های قبلی IIS یک روش قدیمی برای کدهای مدیریت شده وجود داشت که از طریق اینترفیسی به نام ISAPI صورت می‌گرفت. در این حالت ASPNET_ISAPI.DLL مسئول این کار بود و اگر هنوز هم میخواهید از این dll در نسخه‌های جدیدتر IIS کمک بگیرید باید به جای معرفی classic mode ، integration mode را معرفی کنید.
با برابر کردن precondtion به مقدار "intgretedmode" هندلر یا ماژول شما در یک پول با خصوصیت integrated بارگزاری خواهد شد و اگر مقدار آن "classicmode" باشد در یک پول بدون خاصیت integrated بارگزاری می‌شود.
تفاوت بین دو روش کلاسیک و مجتمع integrated بر سر این هست که در روش جدید، ماژول شما به عنوان یک پلاگین برای IIS دیده نمی‌شود و کد شما را جزئی از کامپوننت‌های خود به شمار می‌آورد. به صورت واضح‌تر در حالت کلاسیک موقعی که درخواستی وارد pipeline میشد ابتدا از کامپوننت‌ها و ماژول‌های داخلی خود IIS عبور داده میشد و بعد فایل ASPNET_ISAPI.DLL جهت پردازش کدهای مدیریت شده صدا زده میشد و با توجه به کدهای شما، بعضی مراحل تکرار میشد؛ مثلا اگر کد شما در مورد Authentication بود و بعد از گذر از مراحل auth داخل خود IIS و بقیه موارد دوباره نوبت کد شما و گذر از مراحل authentication بود. یعنی وجود دو pipeline؛ ولی در حالت مجتمع این دوبار انجام وظیفه از بین رفته است چرا که کدهای شما به طور مستقیم در pipeline قرار دارند و آن‌ها را جزئی از خود می‌داند، نه یک پلاگین که افزون بر فعالیت خودشان، اجرای کدهای شما رو هم بر دوش بکشند.
شکل زیر نمونه ای از حالت کلاسیک را نشان می‌دهد که در آن دو بار عمل auth دارد انجام میگیرد.
 



شکل زیر هم نمونه ای حالت مجتمع هست:

 
در کل امروزه دیگر استفاده از روش کلاسیک راهکار درستی نیست و این ویژگی تنها به عنوان یک سازگاری با نمونه کارهای قدیمی است.
تگ‌هایی که از خصوصت precondition استفاده می‌کنند به شرح زیر هستند:
  • ISAPI filters 
  • globalModules 
  • handlers 
  • modules 

در مورد بقیه تگ‌ها در آینده بیشتر بحث می‌کنیم. بهتر هست مطلب را با توضیح precondition جهت ممانعت از طولانی و طومار شدن در اینجا ببندیم و در قسمت آینده دیگر روش‌های نصب ماژولمان را دنبال کنیم.
مطالب
رویدادها در jQuery
Jquery یکی از کتابخانه‌های قدرتمند JavaScript است که به طور وسیعی مورد استفاده طراحان وب قرار میگیرد. این کتابخانه از سه دیدگاه بسیار سودمند است؛ ابتدا به دلیل مدیریت قدرتمند Dom که دارد و دوم اینکه ارتباط Ajax را بسیار راحت کرده است و هم اینکه بستر پلاگین نویسی را فراهم ساخته است، به طوری که میلیون‌ها کتابخانه ثالث از روی همین کتابخانه ایجاد شده است. زمانیکه شما به مطالعه این کتابخانه رو آورید و نمونه کدهای مختلفی از آن را بر روی شبکه اینترنت ببینید، ممکن است این کتابخانه در عین سادگی که دارد، ابهاماتی را برای شما ایجاد کند که یکی از آن‌ها نحوه استفاده از رویدادهاست. لیست زیر نمونه‌ای از نحوه رویداد نویسی در جی کوئری است:
$().click(fn)
$().bind('click',fn)
$().live('click',fn)
$().delegate(selector, 'click', fn)
$().on('click',fn);
$().on('click', selector ,fn);
دو مورد اول در لیست، یکی هستند و در واقع مورد اول یک میانبر برای مورد دوم محسوب میشود و در پشت صحنه در واقع همان خط دوم مورد استفاده قرار میگیرد. از مزایایی که در مورد bind نسبت به استفاده مستقیم از اسم رویداد میتوان گفت این است که میتواند در یک زمان به چند رویداد متصل شود. به عنوان مثال کد زیر نمونه‌ای از آن است:
    $(".test").bind( "click mouseover mouseout", function() { console.log('fired!') } );
در خط بالا المان‌هایی که با کلاس test مزین گشته‌اند، به سه رویداد و یک تابع بایند یا متصل گشته‌اند. برای غیرفعال سازی این رویدادها میتوان از متد unbind استفاده کرد:
$(".test").unbind( "click");
برای استفاده از این دو حالت باید در نظر گرفت که تنها المان‌هایی به این رویداد متصل یا بایند میگردند که در زمان بررسی DOM، یعنی بارگذاری اولیه سایت وجود داشته‌اند و در صورتیکه المان جدیدی به صفحه اضافه شود یا اینکه جایگزین المان دیگری شود، بایند نخواهند شد و تنها المان‌های قدیمی کار خواهند کرد و در صورتیکه المانی از روی صفحه حذف شود، این اتصال از بین خواهد رفت.

مورد سوم، متد معروف live است که هم بر روی المان‌های قدیمی و هم بر روی المان‌های جدید جواب میدهد. ولی نکته‌ای که در مورد live وجود دارد این است که در نسخه‌های اخیر به دلیلی که در جلوتر عنوان میشود حذف شده است و دیگر وجود ندارد. دلیل این حذف هم این میباشد که در واقع متد live برای اینکه افزوده شدن المان‌های جدید را زیر نظر بگیرد، به خود المان متصل نمیشود؛ بلکه به به کل سند HTML یا بدنه وب سایت متصل میشد و کل ناحیه را مورد بررسی قرار میداد تا اگر المان جدیدی اضافه شد، بتواند رویداد مرتبطی را بر روی آن اجرا کند. همچنین در این حالت رویدادی‌هایی از این نوع، در اجداد این المان نیز اجرا میگردند. یعنی رویداد، از المان آغاز شده، سپس به سمت اجدادش انتقال خواهد یافت (به این حالت Bubbling up گفته میشود). پس برای اینکه این ناحیه کوچک‌تر و جزئی‌تر شود، متد دیگری جایگزین آن شد؛ به نام delegate:
$("#area").delegate('.test','click',function(e){...});

----------------------------------------
<div id="area">
<span>Span1</span>
<span>Span2</span>
</div>
این حالت که جایگزین حالت قبلی شده است بدین صورت است که ناحیه‌ای با آی دی area وجود دارد که المان‌هایی با کلاس test در آن قرار میگیرند. پس، از این به بعد، بجای اینکه کل سند بررسی شود، فقط همین بخش مورد بررسی قرار میگیرد و رویداد کلیک، بر روی المانی با کلاس test اجرا میشود و bubbling up کمتری خواهیم داشت و میتوان آن را کنترل کرد. برای حذف رویدادهای live از المان‌های مربوطه، میتوان از die و برای delegate از undelegate همانند unbind استفاده کرد. همچنین حالت استفاده از زنجیره‌ای از متدها در متد live در نظر نگرفته شده، ولی بعد از دلیگیت میتوان از متدهای دیگر، به صورت زنجیره‌ای استفاده کرد.

 از نسخه 1.7 به بعد  توصیه شده‌است به جای تمامی متدهای بالا،  از on استفاده شود که به مراتب کارآایی بهتری دارد و اکثر این متدهای بالا، حذف یا منسوخ شده اعلام گشته‌اند و احتمال حذف آن‌ها در نسخه آتی وجود دارد. به عنوان مثال delegate از نسخه 3 به بعد منسوخ اعلام شده‌است. نحوه صدا زدن on بجای دلیگیت به شکل زیر خواهد بود:
$("#area").on('click','.test',function(e){...});


نکته مهم : بعضی‌ها برای راحتی کار بدین شکل کار میکنند ولی این شیوه فرقی با متد live  ندارد.
$("body").on('click','.test',function(e){...});

در حالت Bubbling up این گونه است که اگر کدی به شکل زیر باشد و رویداد کلیک روی text هدف ما باشد، رویداد کلیک به سمت اجداد آن حرکت میکند. یعنی ابتدا رویداد کلیک b اجرا میشود، سپس a و سپس رویداد کلیک div اجرا میشود و الی آخر:
<div> <a> <b>text</b> </a> </div>
برای حل این مسئله و جلوگیری از این اتفاق میتوان از متد stopPropagation استفاده کرد که در حالت استفاده از متد on جواب میدهد؛ ولی در متد live، استفاده از آن بی فایده است. کد زیر را میتوانید با حالت‌های مختلف بررسی کرده و نتیجه این متد را در کنسول مرورگر خود ببینید:
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title></title>
    <script
  src="https://code.jquery.com/jquery-1.12.4.min.js"
  integrity="sha256-ZosEbRLbNQzLpnKIkEdrPv7lOy9C27hHQ+Xp8a4MxAQ="
  crossorigin="anonymous"></script>
        <script src="http://code.jquery.com/jquery-migrate-1.4.1.js"></script>
    <script>
    $(document).ready(function(){
      $(".myspan").on( "click",".test", function(e) {
        e.stopPropagation();
        console.log('test fired!') } );
      $(".myspan").on( "click", function() { console.log(' myspan fired!') } );
      $(".mydiv").on( "click", function() { console.log(' mydiv fired!') } );

    });
    </script>
  </head>
  <body>
<div class="mydiv">
  <span class="myspan">
    <button type="button" class="test">Ok</button>
  </span>

</div>
  </body>
</html>

نکته تکمیلی
اینکه در استفاده از دلیگیت و on میتوان رویدادها را به شکل زیر هم مورد استفاده قرار داد:
$().on({
    'click': function (e) {
       // function
    },
    'hover': function (e) {
       // function    
    }
});