مطالب دوره‌ها
تزریق وابستگی‌ها و سناریوهای بسیار متعدد موجود
تعدادی از پرکاربردترین حالت‌های تزریق وابستگی‌ها را در دوره جاری بررسی کردیم. برای مثال چگونه می‌توان تزریق وابستگی‌های یک کنترلر ASP.NET MVC را خودکار کرد، یا در وب فرم‌ها وضعیت چگونه است. اما در حین حل مسایلی از این دست، به سناریوهای بسیار متعددی برخواهید خورد. برای مثال تزریق وابستگی‌های خودکار در یک کنترلر را آموختیم؛ در مورد فیلترها و Action Resultهای سفارشی چطور باید رفتار کرد؟ در WCF چطور؟ در هندلرهای وب فرم‌ها چطور؟ و بسیاری از حالات دیگر. البته تمام این موارد را توسط الگوی Service locator که شامل استفاده مستقیم از امکانات وهله سازی یک IoC Container، در کلاس مدنظر است، می‌توان حل کرد؛ اما باید تا حد امکان از این روش با توجه به اینکه خود IoC Container را تبدیل به یک وابستگی مدفون شده در کلاس‌های ما می‌کند، پرهیز نمود.
اگر به دنبال کتابخانه‌ای هستید که بسیاری از این سناریوها را پیاده سازی کرده است، کتابخانه AutoFac پیشنهاد می‌شود. حتی اگر علاقمند به استفاده از آن نباشید، می‌توان از نحوه پیاده سازی‌های مختلف آن در مورد حالت‌های مختلف خودکار سازی تزریق وابستگی‌ها، ایده گرفت و سپس این کدها را با IoC Container مورد علاقه خود پیاده سازی کرد.

صفحه خانگی AutoFac
http://code.google.com/p/autofac
http://autofac.org

بسته نیوگت
http://www.nuget.org/packages/Autofac

محلی برای ایده گرفتن مثلا در مورد فیلترهای ASP.NET MVC
و در مورد نحوه استفاده از آن‌ها، نیاز است آزمون‌های واحد این پروژه را بررسی کنید و یا مستندات پروژه را مطالعه کنید.
همچنین بررسی لیست مستندات کلی آن نیز بسیار مفید است

به صورت خلاصه، هرجایی در مورد تزریق وابستگی‌های خودکار جهت پرهیز از استفاده مستقیم از الگوی Service locator ایده‌ای نداشتید، سورس پروژه AutoFac را بررسی کنید.


پ.ن.
سایت bitbucket امکان import کامل مخازن کد Google code را نیز دارد (در صورتیکه دسترسی شما به گوگل کد محدود است).
مطالب
ASP.NET MVC #11

بررسی نکات تکمیلی Model binder در ASP.NET MVC

یک برنامه خالی جدید ASP.NET MVC را شروع کنید و سپس مدل زیر را به پوشه Models آن اضافه نمائید:

using System;

namespace MvcApplication7.Models
{
public class User
{
public int Id { set; get; }
public string Name { set; get; }
public string Password { set; get; }
public DateTime AddDate { set; get; }
public bool IsAdmin { set; get; }
}
}

از این مدل چند مقصود ذیل دنبال می‌شوند:
استفاده از Id به عنوان primary key برای edit و update رکوردها. استفاده از DateTime برای اینکه اگر کاربری اطلاعات بی ربطی را وارد کرد چگونه باید این مشکل را در حالت model binding خودکار تشخیص داد و استفاده از IsAdmin برای یادآوری یک نکته امنیتی بسیار مهم که اگر حین model binding خودکار به آن توجه نشود، سایت را با مشکلات حاد امنیتی مواجه خواهد کرد. سیستم پیشرفته است. می‌تواند به صورت خودکار ورودی‌های کاربر را تبدیل به یک شیء حاضر و آماده کند ... اما باید حین استفاده از این قابلیت دلپذیر به یک سری نکات امنیتی هم دقت داشت تا سایت ما به نحو دلپذیری هک نشود!

در ادامه یک کنترلر جدید به نام UserController را به پوشه کنترلرهای پروژه اضافه نمائید. همچنین نام کنترلر پیش فرض تعریف شده در قسمت مسیریابی فایل Global.asax.cs را هم به User تغییر دهید تا در هربار اجرای برنامه در VS.NET، نیازی به تایپ آدرس‌های مرتبط با UserController نداشته باشیم.
یک منبع داده تشکیل شده در حافظه را هم برای نمایش لیستی از کاربران، به نحو زیر به پروژه اضافه خواهیم کرد:

using System;
using System.Collections.Generic;

namespace MvcApplication7.Models
{
public class Users
{
public IList<User> CreateInMemoryDataSource()
{
return new[]
{
new User { Id = 1, Name = "User1", Password = "123", IsAdmin = false, AddDate = DateTime.Now },
new User { Id = 2, Name = "User2", Password = "456", IsAdmin = false, AddDate = DateTime.Now },
new User { Id = 3, Name = "User3", Password = "789", IsAdmin = true, AddDate = DateTime.Now }
};
}
}
}

در اینجا فعلا هدف آشنایی با زیر ساخت‌های ASP.NET MVC است و درک صحیح نحوه کارکرد آن. مهم نیست از EF استفاده می‌کنید یا NH یا حتی ADO.NET کلاسیک و یا از Micro ORMهایی که پس از ارائه دات نت 4 مرسوم شده‌اند. تهیه یک ToList یا Insert و Update با این فریم ورک‌ها خارج از بحث جاری هستند.

سورس کامل کنترلر User به شرح زیر است:

using System;
using System.Linq;
using System.Web.Mvc;
using MvcApplication7.Models;

namespace MvcApplication7.Controllers
{
public class UserController : Controller
{
[HttpGet]
public ActionResult Index()
{
var usersList = new Users().CreateInMemoryDataSource();
return View(usersList); // Shows the Index view.
}

[HttpGet]
public ActionResult Details(int id)
{
var user = new Users().CreateInMemoryDataSource().FirstOrDefault(x => x.Id == id);
if (user == null)
return View("Error");
return View(user); // Shows the Details view.
}

[HttpGet]
public ActionResult Create()
{
var user = new User { AddDate = DateTime.Now };
return View(user); // Shows the Create view.
}

[HttpPost]
public ActionResult Create(User user)
{
if (this.ModelState.IsValid)
{
// todo: Add record
return RedirectToAction("Index");
}
return View(user); // Shows the Create view again.
}

[HttpGet]
public ActionResult Edit(int id)
{
var user = new Users().CreateInMemoryDataSource().FirstOrDefault(x => x.Id == id);
if (user == null)
return View("Error");
return View(user); // Shows the Edit view.
}

[HttpPost]
public ActionResult Edit(User user)
{
if (this.ModelState.IsValid)
{
// todo: Edit record
return RedirectToAction("Index");
}
return View(user); // Shows the Edit view again.
}

[HttpPost]
public ActionResult Delete(int id)
{
// todo: Delete record
return RedirectToAction("Index");
}
}
}

توضیحات:

ایجاد خودکار فرم‌های ورود اطلاعات

در قسمت قبل برای توضیح دادن نحوه ایجاد فرم‌ها در ASP.NET MVC و همچنین نحوه نگاشت اطلاعات آن‌ها به اکشن متدهای کنترلرها، فرم‌های مورد نظر را دستی ایجاد کردیم.
اما باید درنظر داشت که برای ایجاد Viewها می‌توان از ابزار توکار خود VS.NET نیز استفاده کرد و سپس اطلاعات و فرم‌های تولیدی را سفارشی نمود. این سریع‌ترین راه ممکن است زمانیکه مدل مورد استفاده کاملا مشخص است و می‌خواهیم Strongly typed views را ایجاد کنیم.
برای نمونه بر روی متد Index کلیک راست کرده و گزینه Add view را انتخاب کنید. در اینجا گزینه‌ی create a strongly typed view را انتخاب کرده و سپس از لیست مدل‌ها، User را انتخاب نمائید. Scaffold template را هم بر روی حالت List قرار دهید.
برای متد Details هم به همین نحو عمل نمائید.
برای ایجاد View متناظر با متد Create در حالت HttpGet، تمام مراحل یکی است. فقط Scaffold template انتخابی را بر روی Create قرار دهید تا فرم ورود اطلاعات، به صورت خودکار تولید شود.
متد Create در حالت HttpPost نیازی به View اضافی ندارد. چون صرفا قرار است اطلاعاتی را از سرور دریافت و ثبت کند.
برای ایجاد View متناظر با متد Edit در حالت HttpGet، باز هم مراحل مانند قبل است با این تفاوت که Scaffold template انتخابی را بر روی گزینه Edit قرار دهید تا فرم ویرایش اطلاعات کاربر به صورت خودکار به پروژه اضافه شود.
متد Edit در حالت HttpPost نیازی به View اضافی ندارد و کارش تنها دریافت اطلاعات از سرور و به روز رسانی بانک اطلاعاتی است.
به همین ترتیب متد Delete نیز، نیازی به View خاصی ندارد. در اینجا بر اساس primary key دریافتی، می‌توان یک کاربر را یافته و حذف کرد.



سفارشی سازی Viewهای خودکار تولیدی

با کمک امکانات Scaffolding نامبرده شده، حجم قابل توجهی کد را در اندک زمانی می‌توان تولید کرد. بدیهی است حتما نیاز به سفارشی سازی کدهای تولیدی وجود خواهد داشت. مثلا شاید نیازی نباشد فیلد پسود کاربر، در حین نمایش لیست کاربران، نمایش داده شود. می‌شود کلا این ستون را حذف کرد و از این نوع مسایل.
یک مورد دیگر را هم در Viewهای تولیدی حتما نیاز است که ویرایش کنیم. آن هم مرتبط است به لینک حذف اطلاعات یک کاربر در صفحه Index.cshtml:

@Html.ActionLink("Delete", "Delete", new { id=item.Id }

در قسمت قبل هم عنوان شد که اعمال حذف باید بر اساس HttpPost محدود شوند تا بتوان میزان امنیت برنامه را بهبود داد. متد Delete هم در کنترلر فوق تنها به حالت HttpPost محدود شده است. بنابراین ActionLink پیش فرض را حذف کرده و بجای آن فرم و دکمه زیر را قرار می‌دهیم تا اطلاعات به سرور Post شوند:

@using (Html.BeginForm(actionName: "Delete", controllerName: "User", routeValues: new { id = item.Id }))
{
<input type="submit" value="Delete"
onclick="return confirm ('Do you want to delete this record?');" />
}

در اینجا نحوه ایجاد یک فرم، که id رکورد متناظر را به سرور ارسال می‌کند، مشاهده می‌کنید.



علت وجود دو متد، به ازای هر Edit یا Create

به ازای هر کدام از متدهای Edit و Create دو متد HttpGet و HttpPost را ایجاد کرده‌ایم. کار متدهای HttpGet نمایش View‌های متناظر به کاربر هستند. بنابراین وجود آن‌ها ضروری است. در این حالت چون از دو Verb متفاوت استفاده شده، می‌توان متدهای هم نامی را بدون مشکل استفاده کرد. به هر کدام از افعال Get و Post و امثال آن، یک Http Verb گفته می‌شود.



بررسی معتبر بودن اطلاعات دریافتی

کلاس پایه Controller که کنترلرهای برنامه از آن مشتق می‌شوند، شامل یک سری خواص و متدهای توکار نیز هست. برای مثال توسط خاصیت this.ModelState.IsValid می‌توان بررسی کرد که آیا Model دریافتی معتبر است یا خیر. برای بررسی این مورد، یک breakpoint را بر روی سطر this.ModelState.IsValid در متد Create قرار دهید. سپس به صفحه ایجاد کاربر جدید مراجعه کرده و مثلا بجای تاریخ روز، abcd را وارد کنید. سپس فرم را به سرور ارسال نمائید. در این حالت مقدار خاصیت this.ModelState.IsValid مساوی false می‌باشد که حتما باید به آن پیش از ثبت اطلاعات دقت داشت.



شبیه سازی عملکرد ViewState در ASP.NET MVC

در متدهای Create و Edit در حالت Post، اگر اطلاعات Model معتبر نباشند، مجددا شیء User دریافتی، به View بازگشت داده می‌شود. چرا؟
صفحات وب، زمانیکه به سرور ارسال می‌شوند، تمام اطلاعات کنترل‌های خود را از دست خواهد داد (صفحه پاک می‌شود، چون مجددا یک صفحه خالی از سرور دریافت خواهد شد). برای رفع این مشکل در ASP.NET Web forms، از مفهومی به نام ViewState کمک می‌گیرند. کار ViewState ذخیره موقت اطلاعات فرم جاری است برای استفاده مجدد پس از Postback. به این معنا که پس از ارسال فرم به سرور، اگر کاربری در textbox اول مقدار abc را وارد کرده بود، پس از نمایش مجدد فرم، مقدار abc را در همان textbox مشاهده خواهد کرد (شبیه سازی برنامه‌های دسکتاپ در محیط وب). بدیهی است وجود ViewState برای ذخیره سازی این نوع اطلاعات، حجم صفحه را بالا می‌برد (بسته به پیچیدگی صفحه ممکن است به چند صد کیلوبایت هم برسد).
در ASP.NET MVC بجای استفاده از ترفندی به نام ViewState، مجددا اطلاعات همان مدل متناظر با View را بازگشت می‌دهند. در این حالت پس از ارسال صفحه به سرور و نمایش مجدد صفحه ورود اطلاعات، تمام کنترل‌ها با همان مقادیر قبلی وارد شده توسط کاربر قابل مشاهده خواهند بود (مدل مشخص است، View ما هم از نوع strongly typed می‌باشد. در این حالت فریم ورک می‌داند که اطلاعات را چگونه به کنترل‌های قرار گرفته در صفحه نگاشت کند).
در مثال فوق، اگر اطلاعات وارد شده صحیح باشند، کاربر به صفحه Index هدایت خواهد شد. در غیراینصورت مجددا همان View جاری با همان اطلاعات model قبلی که کاربر تکمیل کرده است به او برای تصحیح، نمایش داده می‌شود. این مساله هم جهت بالا بردن سهولت کاربری برنامه بسیار مهم است. تصور کنید که یک فرم خالی با پیغام «تاریخ وارد شده معتبر نیست» مجدا به کاربر نمایش داده شود و از او درخواست کنیم که تمام اطلاعات دیگر را نیز از صفر وارد کند چون اطلاعات صفحه پس از ارسال به سرور پاک شده‌اند؛ که ... اصلا قابل قبول نیست و فوق‌العاده برنامه را غیرحرفه‌ای نمایش می‌دهد.



خطاهای نمایش داده شده به کاربر

به صورت پیش فرض خطایی که به کاربر نمایش داده می‌شود، استثنایی است که توسط فریم ورک صادر شده است. برای مثال نتوانسته است abcd را به یک تاریخ معتبر تبدیل کند. می‌توان توسط this.ModelState.AddModelError خطایی را نیز در اینجا اضافه کرد و پیغام بهتری را به کاربر نمایش داد. یا توسط یک سری data annotations هم کار اعتبار سنجی را سفارشی کرد که بحث آن به صورت جداگانه در یک قسمت مستقل بررسی خواهد شد.
ولی به صورت خلاصه اگر به فرم‌های تولید شده توسط VS.NET دقت کنید، در ابتدای هر فرم داریم:

@Html.ValidationSummary(true)

در اینجا خطاهای عمومی در سطح مدل نمایش داده می‌شوند. برای اضافه کردن این نوع خطاها، در متد AddModelError، مقدار key را خالی وارد کنید:

ModelState.AddModelError(string.Empty, "There is something wrong with model.");

همچنین در این فرم‌ها داریم:
@Html.EditorFor(model => model.AddDate)
@Html.ValidationMessageFor(model => model.AddDate)

EditorFor سعی می‌کند اندکی هوش به خرج دهد. یعنی اگر خاصیت دریافتی مثلا از نوع bool بود، خودش یک checkbox را در صفحه نمایش می‌دهد. همچنین بر اساس متادیتا یک خاصیت نیز می‌تواند تصمیم گیری را انجام دهد. این متادیتا منظور attributes و data annotations ایی است که به خواص یک مدل اعمال می‌شود. مثلا اگر ویژگی HiddenInput را به یک خاصیت اعمال کنیم، به شکل یک فیلد مخفی در صفحه ظاهر خواهد شد.
یا متد Html.DisplayFor، اطلاعات را به صورت فقط خواندنی نمایش می‌دهد. اصطلاحا به این نوع متدها، Templated Helpers هم گفته می‌شود. بحث بیشتر درباره‌ای این موارد به قسمتی مجزا و مستقل موکول می‌گردد. برای نمونه کل فرم ادیت برنامه را حذف کنید و بجای آن بنویسید Html.EditorForModel و سپس برنامه را اجرا کنید. یک فرم کامل خودکار ویرایش اطلاعات را مشاهده خواهید کرد (و البته نکات سفارشی سازی آن به یک قسمت کامل نیاز دارند).
در اینجا متد ValidationMessageFor کار نمایش خطاهای اعتبارسنجی مرتبط با یک خاصیت مشخص را انجام می‌دهد. بنابراین اگر قصد ارائه خطایی سفارشی و مخصوص یک فیلد مشخص را داشتید، در متد AddModelError، مقدار پارامتر اول یا همان key را مساوی نام خاصیت مورد نظر قرار دهید.


مقابله با مشکل امنیتی Mass Assignment در حین کار با Model binders

استفاده از Model binders بسیار لذت بخش است. یک شیء را به عنوان پارامتر اکشن متد خود معرفی می‌کنیم. فریم ورک هم در ادامه سعی می‌کند تا اطلاعات فرم را به خواص این شیء نگاشت کند. بدیهی است این روش نسبت به روش ASP.NET Web forms که باید به ازای تک تک کنترل‌های موجود در صفحه یکبار کار دریافت اطلاعات و مقدار دهی خواص یک شیء را انجام داد، بسیار ساده‌تر و سریعتر است.
اما اگر همین سیستم پیشرفته جدید ناآگاهانه مورد استفاده قرار گیرد می‌تواند منشاء حملات ناگواری شود که به نام «Mass Assignment» شهرت یافته‌اند.
همان صفحه ویرایش اطلاعات را درنظر بگیرید. چک باکس IsAdmin قرار است در قسمت مدیریتی برنامه تنظیم شود. اگر کاربری نیاز داشته باشد اطلاعات خودش را ویرایش کند، مثلا پسوردش را تغییر دهد، با یک صفحه ساده کلمه عبور قبلی را وارد کنید و دوبار کلمه عبور جدید را نیز وارد نمائید، مواجه خواهد شد. خوب ... اگر همین کاربر صفحه را جعل کند و فیلد چک باکس IsAdmin را به صفحه اضافه کند چه اتفاقی خواهد افتاد؟ بله ... مشکل هم همینجا است. در اینصورت کاربر عادی می‌تواند دسترسی خودش را تا سطح ادمین بالا ببرد، چون model binder اطلاعات IsAdmin را از کاربر دریافت کرده و به صورت خودکار به model ارائه شده، نگاشت کرده است.
برای مقابله با این نوع حملات چندین روش وجود دارند:
الف) ایجاد لیست سفید
به کمک ویژگی Bind می‌توان لیستی از خواص را جهت به روز رسانی به model binder معرفی کرد. مابقی ندید گرفته خواهند شد:

public ActionResult Edit([Bind(Include = "Name, Password")] User user)

در اینجا تنها خواص Name و Password توسط model binder به خواص شیء User نگاشت می‌شوند.
به علاوه همانطور که در قسمت قبل نیز ذکر شد، متد edit را به شکل زیر نیز می‌توان بازنویسی کرد. در اینجا متدهای توکار UpdateModel و TryUpdateModel نیز لیست سفید خواص مورد نظر را می‌پذیرند (اعمال دستی model binding):

[HttpPost]
public ActionResult Edit()
{
var user = new User();
if(TryUpdateModel(user, includeProperties: new[] { "Name", "Password" }))
{
// todo: Edit record
return RedirectToAction("Index");
}
return View(user); // Shows the Edit view again.
}


ب) ایجاد لیست سیاه
به همین ترتیب می‌توان تنها خواصی را معرفی کرد که باید صرفنظر شوند:
public ActionResult Edit([Bind(Exclude = "IsAdmin")] User user)

در اینجا از خاصیت IsAdmin صرف نظر گردیده و از مقدار ارسالی آن توسط کاربر استفاده نخواهد شد.
و یا می‌توان پارامتر excludeProperties متد TryUpdateModel را نیز مقدار دهی کرد.

لازم به ذکر است که ویژگی Bind را به کل یک کلاس هم می‌توان اعمال کرد. برای مثال:

using System;
using System.Web.Mvc;

namespace MvcApplication7.Models
{
[Bind(Exclude = "IsAdmin")]
public class User
{
public int Id { set; get; }
public string Name { set; get; }
public string Password { set; get; }
public DateTime AddDate { set; get; }
public bool IsAdmin { set; get; }
}
}

این مورد اثر سراسری داشته و قابل بازنویسی نیست. به عبارتی حتی اگر در متدی خاصیت IsAdmin را مجددا الحاق کنیم، تاثیری نخواهد داشت.
یا می‌توان از ویژگی ReadOnly هم استفاده کرد:
using System;
using System.ComponentModel;

namespace MvcApplication7.Models
{
public class User
{
public int Id { set; get; }
public string Name { set; get; }
public string Password { set; get; }
public DateTime AddDate { set; get; }

[ReadOnly(true)]
public bool IsAdmin { set; get; }
}
}

در این حالت هم خاصیت IsAdmin هیچگاه توسط model binder به روز و مقدار دهی نخواهد شد.

ج) استفاده از ViewModels
این راه حلی است که بیشتر مورد توجه معماران نرم افزار است و البته کسانی که پیشتر با الگوی MVVM کار کرده باشند این نام برایشان آشنا است؛ اما در اینجا مفهوم متفاوتی دارد. در الگوی MVVM، کلاس‌های ViewModel شبیه به کنترلرها در MVC هستند یا به عبارتی همانند رهبر یک اکستر عمل می‌کنند. اما در الگوی MVC خیر. در اینجا فقط مدل یک View هستند و نه بیشتر. هدف هم این است که بین Domain Model و View Model تفاوت قائل شد.
کار View model در الگوی MVC، شکل دادن به چندین domain model و همچنین اطلاعات اضافی دیگری که نیاز هستند، جهت استفاده نهایی توسط یک View می‌باشد. به این ترتیب View با یک شیء سر و کار خواهد داشت و همچنین منطق شکل دهی به اطلاعات مورد نیازش هم از داخل View حذف شده و به خواص View model در زمان تشکیل آن منتقل می‌شود.
مشخصات یک View model خوب به شرح زیر است:
الف) رابطه بین یک View و View model آن، رابطه‌ای یک به یک است. به ازای هر View، بهتر است یک کلاس View model وجود داشته باشد.
ب) View ساختار View model را دیکته می‌کند و نه کنترلر.
ج) View modelها صرفا یک سری کلاس POCO (کلاس‌هایی تشکیل شده از خاصیت، خاصیت، خاصیت ....) هستند که هیچ منطقی در آن‌ها قرار نمی‌گیرد.
د) View model باید حاوی تمام اطلاعاتی باشد که View جهت رندر نیاز دارد و نه بیشتر و الزامی هم ندارد که این اطلاعات مستقیما به domain models مرتبط شوند. برای مثال اگر قرار است firstName+LastName در View نمایش داده شود، کار این جمع زدن باید حین تهیه View Model انجام شود و نه داخل View. یا اگر قرار است اطلاعات عددی با سه رقم جدا کننده به کاربر نمایش داده شوند، وظیفه View Model است که یک خاصیت اضافی را برای تهیه این مورد تدارک ببیند. یا مثلا اگر یک فرم ثبت نام داریم و در این فرم لیستی وجود دارد که تنها Id عنصر انتخابی آن در Model اصلی مورد استفاده قرار می‌گیرد، تهیه اطلاعات این لیست هم کار ViewModel است و نه اینکه مدام به Model اصلی بخواهیم خاصیت اضافه کنیم.

ViewModel چگونه پیاده سازی می‌شود؟
اکثر مقالات را که مطالعه کنید، این روش را توصیه می‌کنند:

public class MyViewModel
{
    public SomeDomainModel1 Model1 { get; set; }
    public SomeDomainModel2 Model2 { get; set; }
    ...
}

یعنی اینکه View ما به اطلاعات مثلا دو Model نیاز دارد. این‌ها را به این شکل محصور و کپسوله می‌کنیم. اگر View، واقعا به تمام فیلدهای این کلاس‌ها نیاز داشته باشد، این روش صحیح است. در غیر اینصورت، این روش نادرست است (و متاسفانه همه جا هم دقیقا به این شکل تبلیغ می‌شود).
ViewModel محصور کننده یک یا چند مدل نیست. در اینجا حس غلط کار کردن با یک ViewModel را داریم. ViewModel فقط باید ارائه کننده اطلاعاتی باشد که یک View نیاز دارد و نه بیشتر و نه تمام خواص تمام کلاس‌های تعریف شده. به عبارتی این نوع تعریف صحیح است:

public class MyViewModel
{
    public string SomeExtraField1 { get; set; }
    public string SomeExtraField2 { get; set; }
public IEnumerable<SelectListItem> StateSelectList { get; set; }
// ...
    public string PersonFullName { set; set; }
}

در اینجا، View متناظری، قرار است نام کامل یک شخص را به علاوه یک سری اطلاعات اضافی که در domain model نیست، نمایش دهد. مثلا نمایش نام استان‌ها که نهایتا Id انتخابی آن قرار است در برنامه استفاده شود.
خلاصه علت وجودی ViewModel این موارد است:
الف) Model برنامه را مستقیما در معرض استفاده قرار ندهیم (عدم رعایت این نکته به مشکلات امنیتی حادی هم حین به روز رسانی اطلاعات ممکن است ختم ‌شود که پیشتر توضیح داده شد).
ب) فیلدهای نمایشی اضافی مورد نیاز یک View را داخل Model برنامه تعریف نکنیم (مثلا تعاریف عناصر یک دراپ داون لیست، جایش اینجا نیست. مدل فقط نیاز به Id عنصر انتخابی آن دارد).

با این توضیحات، اگر View به روز رسانی اطلاعات کلمه عبور کاربر، تنها به اطلاعات id آن کاربر و کلمه عبور او نیاز دارد، فقط باید همین اطلاعات را در اختیار View قرار داد و نه بیشتر:

namespace MvcApplication7.Models
{
public class UserViewModel
{
public int Id { set; get; }
public string Password { set; get; }
}
}

به این ترتیب دیگر خاصیت IsAdming اضافه‌ای وجود ندارد که بخواهد مورد حمله واقع شود.



استفاده از model binding برای آپلود فایل به سرور

برای آپلود فایل به سرور تنها کافی است یک اکشن متد به شکل زیر را تعریف کنیم. HttpPostedFileBase نیز یکی دیگر از model binderهای توکار ASP.NET MVC است:

[HttpGet]
public ActionResult Upload()
{
return View(); // Shows the upload page
}

[HttpPost]
public ActionResult Upload(System.Web.HttpPostedFileBase file)
{
string filename = Server.MapPath("~/files/somename.ext");
file.SaveAs(filename);
return RedirectToAction("Index");
}

View متناظر هم می‌تواند به شکل زیر باشد:

@{
ViewBag.Title = "Upload";
}
<h2>
Upload</h2>
@using (Html.BeginForm(actionName: "Upload", controllerName: "User",
method: FormMethod.Post,
htmlAttributes: new { enctype = "multipart/form-data" }))
{
<text>Upload a photo:</text> <input type="file" name="photo" />
<input type="submit" value="Upload" />
}

اگر دقت کرده باشید در طراحی ASP.NET MVC از anonymously typed objects زیاد استفاده می‌شود. در اینجا هم برای معرفی enctype فرم آپلود، مورد استفاده قرار گرفته است. به عبارتی هر جایی که مشخص نبوده چه تعداد ویژگی یا کلا چه ویژگی‌ها و خاصیت‌هایی را می‌توان تنظیم کرد، اجازه تعریف آن‌ها را به صورت anonymously typed objects میسر کرده‌اند. یک نمونه دیگر آن در متد routes.MapRoute فایل Global.asax.cs است که پارامتر سوم دریافت مقدار پیش فرض‌ها نیز anonymously typed object است. یا نمونه دیگر آن‌را در همین قسمت در جایی که لینک delete را به فرم تبدیل کردیم مشاهده نمودید. مقدار routeValues هم یک anonymously typed object معرفی شد.



سفارشی سازی model binder پیش فرض ASP.NET MVC

در همین مثال فرض کنید تاریخ را به صورت شمسی از کاربر دریافت می‌کنیم. خاصیت تعریف شده هم DateTime میلادی است. به عبارتی model binder حین تبدیل رشته تاریخ شمسی دریافتی به تاریخ میلادی با شکست مواجه شده و نهایتا خاصیت this.ModelState.IsValid مقدارش false خواهد بود. برای حل این مشکل چکار باید کرد؟
برای این منظور باید نحوه پردازش یک نوع خاص را سفارشی کرد. ابتدا با پیاده سازی اینترفیس IModelBinder شروع می‌کنیم. توسط bindingContext.ValueProvider می‌توان به مقداری که کاربر وارد کرده در میانه راه دسترسی یافت. آن‌را تبدیل کرده و نمونه صحیح را بازگشت داد.
نمونه‌ای از این پیاده سازی را در ادامه ملاحظه می‌کنید:

using System;
using System.Globalization;
using System.Web.Mvc;

namespace MvcApplication7.Binders
{
public class PersianDateModelBinder : IModelBinder
{

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
var modelState = new ModelState { Value = valueResult };
object actualValue = null;
try
{
var parts = valueResult.AttemptedValue.Split('/'); //ex. 1391/1/19
if (parts.Length != 3) return null;
int year = int.Parse(parts[0]);
int month = int.Parse(parts[1]);
int day = int.Parse(parts[2]);
actualValue = new DateTime(year, month, day, new PersianCalendar());
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}

bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
}

سپس برای معرفی PersianDateModelBinder جدید تنها کافی است سطر زیر را

ModelBinders.Binders.Add(typeof(DateTime), new PersianDateModelBinder());

به متد Application_Start قرار گرفته در فایل Global.asax.cs برنامه اضافه کرد. از این پس کاربران می‌توانند تاریخ‌ها را در برنامه شمسی وارد کنند و model binder بدون مشکل خواهد توانست اطلاعات ورودی را به معادل DateTime میلادی آن تبدیل کند و استفاده نماید.
تعریف مدل بایندر سفارشی در فایل Global.asax.cs آن‌را به صورت سراسری در تمام مدل‌ها و اکشن‌متدها فعال خواهد کرد. اگر نیاز بود تنها یک اکشن متد خاص از این مدل بایندر سفارشی استفاده کند می‌توان به روش زیر عمل کرد:

public ActionResult Create([ModelBinder(typeof(PersianDateModelBinder))] User user)

همچنین ویژگی ModelBinder را به یک کلاس هم می‌توان اعمال کرد:

[ModelBinder(typeof(PersianDateModelBinder))]
public class User
{


نظرات اشتراک‌ها
6 اصول کمتر شناخته شده CSS
«6 اصول» در زبان فارسی اشتباه است. «6 اصل» درست است.

برگرفته شده از درس سوم دوره‌ی آموزشی «ویرایش استاندارد متون فارسی»:
«... نکته‌ی مهم دیگر در نوشتن عدد و معدود، استفاده‌ی درست از اسم جمع و مفرد است. وقتی که پیش از  اسم، عدد یا کلمه‌‌ای «چند» یا «چندین» می‌آید، آن اسم در زبان فارسی (برخلاف انگلیسی) به صورت مفرد می‌آید. چندین مقالات، چند کتاب‌ها، صدهزار افراد این موارد همگی اشتباه است ...»
نظرات مطالب
کوئری هایی با قابلیت استفاده ی مجدد
من یک دور بازخورد‌های شما را خواندم اما متوجه موردی که برای شما ابهام ایجاد کرده نشدم.
آیا شما از Entity Framework استفاده می‌کنید؟ اگر پاسخ مثبت است، خود EF لایه‌ی Repository را پیاده سازی کرده است، و این پیاده سازی یک IQueryable جهت انجام Query‌های متفاوت در اختیار شما قرار می‌دهد. شما می‌توانید مستقیما از DbContext سمت لایه‌ی سرویس استفاده کنید و داده‌ها را جهت استفاده برای استفاده کننده‌ی لایه‌ی سرویس فراهم کنید. 
لایه‌ی سرویس باید داده‌ها را درون حافظه برگرداند، نه اینکه یک IQueryable برگرداند که استفاده کننده آن را اجرا کند.
از Repository در لایه‌ی سرویس استفاده کنید.
مطالب
مروری بر تاریخچه محدودیت حافظه مصرفی برنامه‌های ASP.NET در IIS

زمانیکه اولین نگارش ASP.NET‌ حدود 10 سال قبل منتشر شد،‌ تنها سیستم عاملی که از آن پشتیبانی می‌کرد، ویندوز سرور 2000 بود، تنها پروسه‌ی اجرایی آن aspnet_wp نام داشت و تنها معماری پشتیبانی شده هم X86 بود. به پروسه‌ی aspnet_wp محدودیت مصرف حافظه‌ای اعمال شده بود که در حین آغاز آن بر اساس مقدار قابل تغییر processModel memoryLimit محاسبه و اعمال می‌شد (تعریف شده در فایل ماشین کانفیگ). این عدد به صورت درصدی از ظرفیت RAM فیزیکی سیستم، قابل تعریف و به صورت پیش فرض به 60 درصد تنظیم شده بود. به این ترتیب این پروسه مجاز نبود تا تمام حافظه‌ی فیزیکی مهیا را مصرف کند و در صورت وجود نشتی حافظه‌ای در برنامه‌ای خاص، این پروسه امکان بازیابی مجدد حافظه را پیدا می‌کرد (recycling). همچنین یک مورد دیگر را هم باید در نظر داشت و آن هم وجود قابلیتی است به نام ASP.NET Cache است که امکان ذخیره سازی مقادیر اشیاء را در حافظه‌ی مصرفی این پروسه مهیا می‌سازد. هر زمان که میزان این حافظه‌ی مصرفی به حد نزدیکی از محدودیت تعریف شده برسد، این پروسه به صورت خودکار شروع به حذف آن‌ها خواهد کرد.
محدودیت 60 درصدی تعریف شده، برای سیستم‌هایی با میزان RAM کم بسیار مفید بود اما در سیستم‌هایی با میزان RAM بیشتر، مثلا 4 گیگ به 2.4GB حافظه مهیا (60 درصد حافظه فیزیکی سیستم) محدود می‌شد و همچنین باید در نظر داشت که میزان user mode virtual address space مهیا نیز تنها 2 گیگابایت بود. بنابراین هیچگاه استفاده مؤثری از تمام ظرفیت RAM مهیا صورت نمی‌گرفت و گاها مشاهده می‌شد که یک برنامه تنها با مصرف 1.5GB RAM می‌توانست پیغام OutOfMemoryException را صادر کند. در این حالت مطابق بررسی‌های صورت گرفته مشخص شد که اگر مقدار processModel memoryLimit به حدود 800 مگابایت تنظیم شود، بهترین عملکرد را برای سیستم‌های مختلف می‌توان مشاهده کرد.

با ارائه‌ی ویندوز سرور 2003 و همچنین ارائه‌ی نسخه‌ی 1.1 دات نت فریم ورک و ASP.NET ، این وضعیت تغییر کرد. پروسه‌ی جدید در اینجا w3wp نام دارد و این پروسه تعاریف مرتبط با محدودیت حافظه‌ی خود را از تنظیمات IIS دریافت می‌کند (قسمت Maximum Used Memory در برگه‌ی Recycling مربوط به خواص Application Pool مرتبط). متاسفانه این عدد به صورت پیش فرض محدودیتی ندارد و به ظاهر برنامه مجاز است تا حد امکان از حافظه‌ی مهیا استفاده کند. به همین جهت یکی از مواردی را که باید در نظر داشت، مقدار دهی Maximum Used Memory ذکر شده است. خصوصا اینکه در نگارش 1.1 ، تنظیمات میزان مصرف RAM مرتبط با ASP.NET Cache نیز با برنامه یکی است.

در نگارش 2.0 دات نت فریم ورک، تنظیمات مرتبط با ASP.NET cache از تنظیمات میزان RAM مصرفی یک برنامه‌ی ASP.NET جدا شد و این مورد توسط قسمت cache privateBytesLimit قابل تنظیم و مدیریت است (در فایل IIS Metabase و همچنین فایل web.config برنامه).

نکته!
اگر process memory limit و همچنین cache memory limit را تنظیم نکنید، باز به همان عدد 60 درصد سابق بازخواهیم گشت و این مورد به صورت خودکار توسط IIS محاسبه و اعمال می‌شود. البته محدودیت ذکر شده برای پروسه‌های 64 بیتی در این حالت بسیار بهتر خواهد بود. اگر هر دوی این‌ها را تنظیم کنید، عدد حداقل بکارگرفته شده، مبنای کار خواهد بود و اگر تنها یکی را تنظیم کنید ، این عدد به هر دو حالت اعمال می‌گردد. برای بررسی بهتر می‌توان به مقدار Cache.EffectivePrivateBytesLimit و Cache.EffectivePercentagePhysicalMemoryLimit مراجعه کرد.

و ... اکنون بهتر می‌توانید به این سؤال پاسخ دهید که «سرور ما بیشتر از 4 گیگ رم دارد و برنامه‌ی ASP.NET من الان فقط 850 مگ رم مصرف کرده (که البته این هم نشانی از عدم dispose صحیح منابع است یا عدم تعیین تقدم و تاخر و زمان منقضی شدن، حین تعریف اشیاء کش)، اما پیغام out of memory exception را دریافت می‌کنم. چرا؟!»


بنابراین ایجاد یک Application pool جدید به ازای هر برنامه‌ی ASP.NET امری است بسیار مهم زیرا:
- به این ترتیب هر برنامه‌ی ASP.NET در پروسه‌ای ایزوله از پروسه‌ی دیگر اجرا خواهد شد (این مساله از لحاظ امنیتی هم بسیار مهم است). در اینجا هر برنامه، از پروسه‌ی w3wp.exe مجزای خاص خود استفاده خواهد کرد (شبیه به مرورگرهایی که هر tab را در یک پروسه جدید اجرا می‌کنند).
- اگر پروسه‌ای به حد بالای مصرف حافظه‌ی خود رسید با تنظیمات انجام شده در قسمت recycling مرتبط با Application pool اختصاصی آن، به صورت خودکار کار بازیابی حافظه صورت می‌گیرد و این امر بر روی سایر برنامه‌ها تاثیر نخواهد داشت (کاربران سایر برنامه‌ها مدام شکایت نمی‌کنند که سشن‌ها پرید. کش خالی شد. زیرا در حالت وجود application pool اختصاصی به ازای هر برنامه، مدیریت حافظه برنامه‌ها از هم ایزوله خواهند بود)
- کرش صورت گرفته در یک برنامه به دلیل عدم مدیریت خطاها، بر روی سایر برنامه‌ها تاثیر منفی نخواهد گذاشت. (زمانیکه ASP.NET worker process به دلیل استثنایی مدیریت نشده خاتمه یابد بلافاصله و به صورت خودکار مجددا «وهله‌ی دیگری» از آن شروع به کار خواهد کرد؛ یعنی تمام سشن‌های قبلی از بین خواهند رفت؛ که در صورت ایزوله سازی ذکر شده، سایر برنامه‌ها در امان خواهند ماند؛ چون در پروسه ایزوله‌ی خود مشغول به کار هستند)
- با وجود application pool اختصاصی به ازای هر برنامه، می‌توان برای سایت‌های کم ترافیک و پرترافیک، زمان‌های recycling متفاوتی را اعمال کرد. به این ترتیب مدیریت حافظه‌ی بهتری قابل پیاده سازی می‌باشد. همچنین در این حالت می‌توان مشخص کرد کدام سایت از تعداد worker process بیشتر یا کمتری استفاده کند.
- کاربری که پروسه‌ی ASP.NET تحت آن اجرا می‌شود نیز همینجا تعریف می‌گردد. بنابراین به این ترتیب می‌توان به برنامه‌ای دسترسی بیشتر و یا کمتر داد، بدون تاثیر گذاری بر روی سایر برنامه‌های موجود.

نتیجه گیری:
- از IIS استفاده می‌کنید؟ آیا می‌دانید Application pool چیست؟
- آیا می‌دانید در صورت عدم مقدار دهی پارامترهای حافظه‌ی یک Application pool ، به صورت پیش فرض چند درصد از حافظه‌ی فیزیکی مهیا در اختیار شما است؟


برای مطالعه بیشتر:

نظرسنجی‌ها
آیا در تیم توسعه دات نت خود از تکنولوژی‌های اورکستراسیون مانند Kubernetes برای مدیریت و مقیاس‌پذیری Docker استفاده می‌کنید؟
تازه‌کار هستیم و هنوز از این تکنولوژی‌ها استفاده نمی‌کنیم.
به‌صورت محدود در برخی پروژه‌ها از Kubernetes یا تکنولوژی‌های مشابه استفاده می‌کنیم.
در برخی پروژه‌ها از این تکنولوژی‌ها برای مدیریت Docker استفاده می‌شود.
Kubernetes یا مشابه آن در پروژه‌های متعدد دات نت مورد استفاده قرار می‌گیرد.
در تمامی پروژه‌های دات نت ما از تکنولوژی‌های اورکستراسیون برای مقیاس‌پذیری و مدیریت Docker استفاده می‌شود.
نظرات مطالب
Protocol Buffers فرمتی برای تبادل دیتا
چند تا مساله را میگم و دیگه این بحث رو ادامه نمیدم به هیچ وجه (چون مساله‌ی مطرح شده هیچ ربطی به این مقاله‌ی خاص نداره و فقط چون این وبسایت بازدید کننده‌ی زیادی داره مجبورم کامل توضیح بدم که ما اشتباها خط غلط به کسی ندیم و گمراه نکنیم کسی رو ناخواسته)
1) موضوع این مقاله فقط و فقط protocol buffers بود و هیچ ربطی به هیچ زبان برنامه نویسی نداشت و وارد شدن به این مساله در اینجا اشتباه است، من حتی در این مقاله توضیح ندادم که با استفاده از grpc و protobuf مستقیما نمیتونیم از طریق browser درخواست بدیم، و وارد کردن این مساله و همینطور لایبری‌های مختلف جاوا اسکریپتی و سی شارپی جایگاهش در این نقاله نبود چون نگاه خیلی بالاتر از این بود تا اینکه به سطح بیاییم و بخواهیم راجع به یک کتابخونه خاص و یک زبان خاص و مقایسه بین آنها رو انجام بدیم!
2) از اینجا میتونید مشاهد کنید که حداقل به صورت رسمی (اینکه توی گوگل سرچ زده بشه و اولین نتیجه به عنوان سند در پاسخ داده بشه مورد قبول نیست، چون قطعا ممکنه به صورت غیر رسمی ابزار هایی باشه که مورد قبول در تولید یک سیستم روی پروداکشن نیست!) زبان‌های swift, rust , ... هنوز به صورت رسمی ساپورت نمیشند! پس اگر نگاهمون به عنوان توسعه دهنده فراتر از یک مثال hello-world باشه باید اینو مد نظر بگیریم.
3) تکنولوژی باید به ساختار و سیستم ما کمک کنه نه اینکه خودش bottleneck سیستم بشه، تکنولوژی جذابه ولی نکته‌ی مهم اینه که ما اول از همه به این مساله دقت کنیم که باید به عنوان برنامه نویس روی تکنولوژی سوار باشیم نه برعکس (بزرگترین مشکل وقتی پیش میاد که هیجان زده ابزارو تکنولوژی جدید آشنا بشیم و بخواهیم در سیستم استفاده کنیم! هیجان بیش از حد قاتل سسیستمه) با انتخاب اشتباهمون کل سازمان رو دچار مشکل نکنیم، مخصوصل وقتی با یک تکنولوژی آشنا میشیم وفور لایبری‌های مختلف نباید ما رو گمراه کنه!
4) وقتی دیدتون به عنوان توسعه دهنده از مونولوتیک خارج میشه و به مایکروسرویس نگاه میکنید حتما باید از زاویه‌ی خیلی بالاتری نگاه کنید، جایی که توقع میره همه‌ی سیستم بتونه به همدیگه به بهترین نحو بدون مشکل کار کنه پس انتخاب ابزار خیلی اهمیتش بالاتره از تکنولوژی جدید که شاید تازه هم باهاش آشنا شده باشید، api-gateway  به عنوان یک اصل برای معماری ماکروسرویس پذیرفته شده و حتی خیلی توصیه میشه
5) نگاه به سیستم‌های طراحی شده‌ی دنیا هم خیلی بهمون کمک میکنه حتی پروژه‌های بزرگی مثله etcd از gateway استفاده کرده اند چرا که سنگ بزرگ نشانه نزدنه و ریسک تغییرات خیلی بزرگ و بنیادین کل نرم افزار رو به چالش میشکه و علاوه بر اینکه هزینه‌ی زیاد learning curve برای کله تیم داره، ریسک شکست رو هم خیلی بالا میبره، شما فرض کنید که برنامه نویس فرانت اند که تا حالا تو عمرش به غیر از restful api چیز دیگه ای ندیده ناگهان با کلی ابزار جدید آشنا باید بشه که حتی ممکنه رسمی و صد در صد قابل اتکا هم نباشه، این خودش شروع fail شدن یک سیستم رو نتیجه میده!
در مقاله‌ی بعدی سعی میکنم مثال عملی با استفاده از زبان‌های برنامه نویسی مختلف بزنم و توضیحات جزئی‌تری بدم و موارد بیشتر رو فقط و فقط به خواننده که خودش قطعا توسعه دهنده هست و میتونه بالا و پایین رو ببینه میسپارم!
ممنون
مطالب
تفاوت AngularJS با KnockoutJS
با پیشرفت HTML 5 و پدید آمدن چارچوب‌های مختلف JavaScript توسعه‌ی نرم افزار‌های تک صفحه ای تحت وب (Single Page Applications) محبوب شده است. 
اخیرا مطالب خوبی در رابطه با AngularJS در وبسایت جاری منتشر شده است. KnockoutJS توسط Microsoft معرفی شد و در قالب پیشفرض پروژه‌های SPA قرار گرفت ، بنابراین احتمالا این سوال برای افرادی مطرح شده است که تفاوت بین KnockoutJS و AngularJS چیست ؟ 
می توان پاسخ داد این مقایسه ممکن نیست. 
KnockoutJS : یک پیاده سازی مستقل JavaScript از الگوی MVVM با امکانات Databinding می‌باشد. Knokcout یک کتابخانه‌ی Databinding است نه یک کتاب خانه‌ی SPA
AngularJS : طبق معرفی در این مطلب AngularJS فریم ورکی متن باز و نوشته شده به زبان جاوا اسکریپت است. هدف از به وجود آمدن این فریم ورک، توسعه هر چه ساده‌تر SPA‌ها با الگوی طراحی MVC و تست پذیری هر چه آسان‌تر آن‌ها است. این فریم ورک توسط یکی از محققان Google در سال 2009 به وجود آمد. بعد‌ها این فریم ورک تحت مجوز MIT به صورت متن باز در آمد و اکنون گوگل آن را حمایت می‌کند و توسط هزاران توسعه دهنده در سرتاسر دنیا، توسعه داده می‌شود. 

بنابراین شاید بهتر باشد ذکر شود AngularJS یک Presentation Framework مخصوص برنامه‌های وب تک صفحه ای می‌باشد در حالی که KnockoutJS کتاب خانه ای با تمرکز بر Databinding می‌باشد ، بنابراین مقایسه‌ی این‌ها چندان صحیح نیست.

اگر قصد بر بررسی گزینه‌های دیگر در کنار Angular باشد ، می‌توان از Durandal نام برد. Durandal یک چارچوب SPA می‌باشد ، این چارچوب بر فراز jQuery ، RequireJS و Knockout توسعه پیدا کرده است. (سابقا برای routing از SammyJS استفاده می‌کرد که در نسخه‌های اخیر از موتور خودش استفاده می‌کند.)

Durandal از Knockout جهت Databinding و از RequireJS برای مدیریت وابستگی‌ها استفاده می‌کند.
Angular همه‌ی امکانات بالا را مستقل پیاده سازی کرده و حتی نیازی به jQuery ندارد. اگر jQuery وجود داشته باشد Angular از آن استفاده می‌کند در غیر این صورت از jQuery Lite یا jqLite استفاده می‌کند. jqLite پیاده سازی توابع متداول jQuery برای دستکاری DOM می‌باشد. اطلاعات بیشتر در اینجا

بنابراین با استفاده تنها از KnockoutJS نمی‌توان یک برنامه‌ی کامل SPA توسعه داد ، در کنار آن نیاز به کتابخانه‌های دیگری مثل jQuery برای مدیریت درخواست‌های  AJAX و استفاده از دیگر API‌ها ، Sammy برای routing و RequireJS برای مدیریت وابستگی‌ها می‌باشد.

در Knockout و در نتیجه Durandal عمل Databinding به این صورت است :
// JavaScript
var vm = {
    firstName = ko.observable('John')
};
ko.applyBindings(vm);
<!-- HTML -->
<input data-bind="value:firstName"/>
در Angular :
// JavaScript
// Inside of a personController
this.firstName = 'John';
در Angular همچنین از یک روش Controller As استفاده می‌شود :
<!-- HTML -->
<div ng-controller="personController as vm">
    <input ng-model="vm.firstName"/>
</div>
اگر تنها نیاز به یک کتابخانه‌ی Databinding باشد ، Knockout گزینه‌ی مناسبی است ، به خوبی از عمل مقید سازی داده‌ها پشتیبانی می‌کند و Syntax خوش دستی دارد اما اگر نیاز به چارچوبی برای توسعه‌ی پروژه‌های SPA می‌باشد می‌توان از Angular یا Durandal استفاده کرد. 
مقایسه‌ی Knockout با Angular همانند مقایسه‌ی موتور بنز با ماشین پورشه می‌باشد. 



مطالب
آموزش فایرباگ - #2 - تب Console
در قسمت قبلی با تب‌های فایرباگ آشنا شدیم . در این قسمت و قسمت‌های بعدی ، با هر تب به صورت کامل آشنا خواهیم شد .

در این قسمت با تب Console  آشنا خواهیم شد .
در قسمت قبل در مورد این تب گفتیم :

در این تب دو بخش وجود دارد :
در بخش Log هشدار‌ها ، پیغام‌ها ، درخواست‌های XHR و ... نمایش داده می‌شوند .
بخش دیگر هم که در سمت راست قرار دارد ، مخصوص اجرای کدهای جاوا اسکریپت می‌باشد .

 

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

اکنون 2 سوال مطرح می‌شود :
  1. برای اجرای یک کد در حالت معمول چگونه عمل می‌کنیم ؟
    پاسخ : کدهای مورد نظر را بین تگ باز و بسته‌ی script قرار می‌دهیم و صفحه مورد نظر را در مرورگر باز می‌کنیم و مرورگر کد را اجرا می‌کند .
  2. در صورتی که کدهای ما خطا داشته باشند ، چگونه خطایابی می‌کنیم ؟
    پاسخ : در بین کدهای نوشته شده چند alert قرار می‌دهیم و سعی می‌کنیم مشکل را پیدا کنیم .

همین 2 سوال اهمیت و قدرت فایرباگ و قسمت Console آن را برای ما آشکار می‌کند ، زیرا ما می‌توانیم بدون Reload کردن صفحه ، کدهایمان را اجرا و نتیجه را مشاهده کنیم یا بوسیله توابع موجود خیلی ساده کدهایمان را خطایابی کنیم .



چگونه کدهای نوشته شده را سریع اجرا کنم ؟ کلید میانبر ( HotKey ) اجرای کد‌ها چیست ؟
پاسخ : فشردن کلید CTRL + Enter

امتحان کنید :

کد زیر را در بخش کدنویسی تایپ کنید و بوسیله کلید میانبر گفته شده ، آن را اجرا کنید .
document.getElementsByTagName("div");
 
تصویر ذیل نتیجه اجرای کدی هست که اجرا کردیم .



چند نکته :
  • قسمت هایی که با رنگ قرمز و یک دات ( . ) بعد از نام تگ قرار گرفته اند ، کلاس‌های CSS ای هستند که المنت دارد .
  • قسمت هایی که با رنگ آبی تیره و یک شارپ ( # ) بعد از نام تگ قرار گرفته اند ، ID تگ‌ها هستند .
  • قسمت هایی که کمرنگ هستند ، المنت هایی هستند که در صفحه قابل نمایش نیستند .
  • اگر یک تگ چند کلاس داشته باشد ، فقط اولین کلاس نمایش داده می‌شود .


دکمه‌ها و حالت‌های نمایش بخش کد نویسی

4 دکمه در قسمت کدنویسی وجود دارد :
  1. Run : اجرای کد
  2. Clear : خالی کردن بخش کد نویسی
  3. Copy : کپی کردن کد موجود در بخش کد نویسی در حافظه
  4. History : کدهای نوشته شده در نشست ( Session ) فعلی مرورگر

بخش کد نویسی می‌تواند به 2 شکل نمایش داده شود :
  1. بصورت جعبه چند خطی ( Command Editor )
  2. بصورت تک خطی ( Command Line )
با دکمه‌ی قرمز رنگ موجود در پایین - چپ می‌توانید بین این دو حالت سوئیچ کنید .


9 دکمه هم در بالای بخش log وجود دارد ( به ترتیب از چپ به راست ) :


  • دکمه ای با عنوان “Break On All Errors” . زمانی فعال شود ، در اولین اجرای یک کد از داخل صفحه ، به تب Script منتقل می‌شود و در خطی که کد در حال اجرا است توقف می‌کند .
  • Clear : بخش log را خالی می‌کند .
  • Persist : فعال بودن این دکمه باعث می‌شود که محتویات بخش Console در بارگزاری مجدد صفحه حفظ شود .
  • Profile : بوسیله این گزینه می‌توانید کدهای اجرایی خود در مدت زمان فعال بودن این دکمه ، تحت نظر بگیرید .
    به این صورت که پس از غیر فعال کردن این دکمه ( کلیک مجدد بروی آن ) می‌توانید تابع‌های اجرا شده ، تعداد فراخوانی آنها ، مدت زمان اجرای هر یک ، میانگین زمان اجرای هر بار یک تابع و ... را مشاهده کنید .
  • 5 دکمه‌ی بعدی هم برای فیلتر کردن Log هستند .


نمایش تعداد خطاهای اتفاق افتاده ، در نوار وضعیت ( Status Bar ) :

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


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

چند متد کمکی برای نوشتن Log‌های مختلف در Console :
console.debug('This is a Debug message'); 
console.info('This is an Information'); 
console.warn('This is a Warning message'); 
console.error('This is an Error message');


در قسمت بعدی توابع مربوط به توسعه جاوا اسکریپت ( Command Line API & Console API ) در فایرباگ را بررسی خواهیم کرد .