مطالب
Microsoft Test Manager - قسمت دوم

تا اینجا متوجه شدیم که test plan چیست و چگونه ساخته می‌شود و برای نوشتن تست‌ها چه مراحلی را باید طی کنیم. در این مطلب قصد بر این است که آموزش نوشتن تست‌ها با استفاده از MTM را آموزش دهیم. در این آموزش فرض بر این است که شما آشنایی کمی با محیط این ابزار، نیازمندی‌ها و Story ها، اشکالات یا Bug‌ها و Task‌ها دارید.

در MTM سه لایه وجود دارد:

1- Test Plan:  شما در آغاز کار با MTM ابتدا باید Test Plan خود را ایجاد کنید.

2- Test Suite: در هر Test Plan شما می‌توانید چندین Test Suite ایجاد کنید.

3-  Test Case: هر Test Suite از چندین Test Case ترکیب شده است.

برای اولین بار که شما MTM را اجرا می‌کنید باید team project ی را که قرار است برای آن تست بنویسید را انتخاب کنید. می‌توانید در زیر نمایی از MTM و اتصال به team project را مشاهده کنید:

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

وقتی plan یا طرحی را انتخاب می‌کنید به صفحه testing center وارد می‌شوید که به صورت پیش فرض در کاربرگ plan و بخش contents قرار دارید.

همانطور که در تصویر بالا مشاهده می‌کنید و در سمت چپ پنجره، plan شما در ریشه قرار دارد و test suite هایی را که ایجاد می‌کنید به عنوان فرزندان plan تان قرار می‌گیرند. در سمت راست test case‌های شما قرار می‌گیرند که با توجه به test suite ی که شما در سمت چپ انتخاب کرده اید test case‌های مربوط به آن در سمت راست قابل مشاهده است. برای ایجاد test suite به plan تان، باید روی plan راست کلیک کرده و گزینه new suite را انتخاب کنید و برای آن عنوانی را وارد می‌کنید. وقتی روی plan راست کلیک می‌کنید پند گزینه وجود دارد که می‌توانید با توجه به کارتان این گزینه‌ها را انتخاب کنید:

1- وقتی new suite را انتخاب می‌کنید یک suite خالی برای شما ایجاد می‌کند.

2- وقتی گزینه new query-based suite را انتخاب می‌کنید این اجازه را به شما می‌دهد که از test case‌های موجود در پروژه خود یک یا چندین مورد تست را انتخاب نمایید که پنجره ای مانند زیر باز می‌شود که می‌توانید با اعمال فیلتر، test case‌های موجود در پروژه را پیدا و یک یا چندین مورد را به suite خود اضافه نمایید.

3- گزینه add requirement to plan این اجازه را به شما می‌دهد تا بتوانید از plan‌های موجود در TFS تان استفاده نمایید. بعد از انتخاب این گزینه پنجره ای مشابه تصویر بالا باز می‌شود که می‌توانید با اعمال فیلتر مورد‌های تست را پیدا کرده و به آن بیافزاید.

4- با انتخاب گزینه copy suite from another plan همانطور که از اسمش پیداست می‌توانید از suite‌های مربوط به plan‌های دیگر کپی برداری کنید.

مطالب
آموزش Knockout.Js #1
اگر از برنامه نویس‌های پروژه‌های WPF درباره ویژگی‌های مهم الگوی MVVM بپرسید به احتمال زیاد اولین مطلبی که عنوان می‌شود این است که هنگام کار با الگوی MVVM در WPF باید از مباحث data-binding استفاده شود. به صورت خلاصه، data-binding مکانیزمی است که عناصر موجود در Xaml را به آبجکت‌های موجود در ViewModel یا سایر عناصر Xaml مقید می‌کند به طوری که با تغییر مقدار در آبجکت‌های ViewModel، عناصر View نیز خود را به روز می‌کنند یا با تغییر در مقادیر عناصر Xaml، آبجکت‌های متناظر در ViewModel نیز تغییر خواهند کرد(در صورت تنظیم Mode = TwoWay).
Knockout.Js  چیست؟
در یک جمله Knockout.Js یک فریم ورک جاوا اسکریپ است که امکان پیاده سازی الگوی MVVM و مکانیزم data-binding را در پروژه‌های تحت وب به راحتی میسر می‌کند. به عبارت دیگر عناصر DOM را به data-model و آبجکت‌های data-model را به عناصر DOM مقید می‌کند، به طوری که با هر تغییر در مقدار یا وضعیت این عناصر یا آبجکت ها، تغییرات به موارد مقید شده نیز اعمال می‌گردد. به تصاویر زیر دقت کنید!
به روز رسانی data-model بدون استفاده از KO

به روز رسانی data-model با استفاده از KO

ویژگی‌های مهم KO
»ارائه یک راه حل بسیار ساده و واضح برای اتصال بخش‌های مختلف UI به data-model
»به روز رسانی خودکار عناصر و بخش‌های مختلف UI بر اساس تغییرات صورت گرفته در data-model
»به صورت کامل با کتابخانه و توابع javascript پیاده سازی شده است.
»حجم بسیار کم(سیزده کیلو بایت) بعد از فشرده سازی
»سازگار با تمام مروگرهای جدید(... ,IE 6+, Firefox 2+, Chrome, Safari )
»امکان استفاده راحت بدون اعمال تغییرات اساسی در معماری پروژه هایی که در فاز توسعه هستند و بخشی از مسیر توسعه را طی کرده اند
»و...
آیا KO برای تکمیل JQuery در نظر گرفته شده است یا جایگزین؟
در اینکه JQuery بسیار محبوب است و در اکثر پروژه‌های تحت وب مورد استفاده است شکی وجود ندارد ولی این بدان معنی نیست که با توجه به وجود JQuery و محبوبیت آن دیگر نیازی به KO احساس نمی‌شود. به عنوان یک مثال ساده : فرض کنید در یک قسمت از پروژه قصد داریم یک لیست از داده‌ها را نمایش دهیم. در پایین لیست تعداد آیتم‌های موجود در لیست مورد نظر نمایش داده میشود. یک دکمه Add داریم که امکان اضافه شدن آیتم جدید را در اختیار ما قرار می‌دهد. بعد از اضافه شدن یک مقدار، باید عددی که تعداد آیتم‌های لیست را نمایش می‌دهد به روز کنیم. خب اگر قصد داشته باشیم این کار را با JQuery انجام دهیم راه حل‌های زیر پیش رو است :
» به دست آوردن تعداد tr‌های جدول موجود؛
»به دست آوردن تعداد div‌های موجود با استفاده از یک کلاس مشخص css؛
» یا حتی به دست آوردن تعداد آیتم‌های نمایشی در  span  هایی مشخص.
و البته سایر راه حل ها...
حال فرض کنید دکمه‌های دیگر نظیر Delete نیز مد نظر باشد که مراحل بالا تکرار خواهند شد. اما با استفاده از KO به راحتی می‌توانیم تعداد آیتم‌های موجود در یک آرایه را به یک عنصر مشخص bind کنیم به طور با هر تغییر در این مقدار، عنصر مورد نظر نیز به روز می‌شود یا به بیانی دیگر همواره تغییرات observe خواهند شد. برای مثال:
 Number of items :<span data-bind="text: myList().count"></span>
در نتیجه برای کار با KO وابستگی مستقیم به استفاده از JQeury وجود ندارد ولی این امکان هست که بتوانیم هم از JQuery و هم از KO در کنار هم به راحتی استفاده کنیم و از قدرت‌های هر دو فریم ورک بهره ببریم و البته KO جایگزینی برای JQuery نخواهد بود.
در پست بعد، شروع به کار با KO آموزش داده خواهد شد.
ادامه دارد...

 
مطالب
الگوی PRG در ASP.NET MVC
تا حالا با این پنجره حتما مواجه شدین:


دارید اطلاعات یک فرم داخل صفحه رو به سمت سرور میفرستید و پس از اتمام عملیات، صفحه دوباره نمایش داده میشه. در این حالت اگه دکمه F5 یا دکمه Refresh مرور گر رو بزنید، با این پنجره مواجه میشید که میگه دارید اطلاعات قبلی رو دوباره به سمت سرور میفرستید. بعضی وقت‌ها کاربران به هر دلیل دوباره صفحه رو Refresh میکنند و با این پنجره روبرو میشن بدون اینکه بدونن جریان از چه قراره، دوباره اطلاعات رو به سمت سرور میفرستن و این کار باعث ثبت اطلاعات تکراری میشه. برای جلوگیری از این کار الگویی به نام Post/Redirect/Get هست که راه حلی رو برای اینکار پیشنهاد میده. 

راه حل به این صورت هست که پس از پست شدن فرم به سمت سرور و انجام عملیات، بجای اینکه صفحه، دوباره با استفاده از متد GET به کاربر نشون داده بشه، کاربر Redirect بشه به صفحه. برای توضیح این مسئله به سراغ AccountController که بصورت پش فرض وقتی یک پروژه ASP.NET MVC رو از نوع Internet ایجاد میکنید، وجود داره. 

 اکشن Register از نوع GET صفحه ثبت نام کاربر رو نمایش میده. 
[HttpGet]
[AllowAnonymous]
public ActionResult Register()
{
      return View();
}
پس از اینکه کاربر اطلاعات داخل فرم رو پر کرد و به سمت سرور فرستاد و صحت اطلاعات فرستاده معتبر و عمل ثبت موفقیت آمیز بود برای ادامه کار به دو روش میتوان عمل کرد:
 ١- کاربر به صفحه دیگری منتقل بشه و در اون صفحه پیام موفقیت آمیز بودن عملیات نشون داده بشه. مثلا معمولا پس از انجام عمل ثبت نام، کاربر به صفحه شخصی یا صفحه اصلی سایت منتقل میشه  و یا در موقع ویرایش اطلاعات پش از انجام عمل ویرایش کاربر به صفحه دیگری که لیستی از آیتمها که کاربر یکی از آنها را ویرایش کرده باز گردانده میشه.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        try
        {
            WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
            WebSecurity.Login(model.UserName, model.Password);
            ViewBag.Message = "Successfully Registered!";

            // PRG has been maintained
            return RedirectToAction("Index", "Home");
        }
        catch (MembershipCreateUserException e)
        {
            ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}
٢- نمایش دوباره صفحه ولی با تغییر هدر صفحه به کد 303 . کد 303 به مروگر اعلام میکنه صفحه ریدایرکت شده است
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
    if (ModelState.IsValid)
    {
        // Attempt to register the user
        try
        {
            WebSecurity.CreateUserAndAccount(model.UserName, model.Password);
            WebSecurity.Login(model.UserName, model.Password);
            ViewBag.Message = "Successfully Registered!";

            // PRG has been maintained
            return RedirectToAction("Register");
        }
        catch (MembershipCreateUserException e)
        {
            ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
        }
    }
    // If we got this far, something failed, redisplay form
    return View(model);
}
در این حالت دوباره صفحه ثبت نام نمایش داده میشه ولی با زدن دکمه رفرش، اطلاعات دوباره به سمت سرور فرستاده نمیشه
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 17 - بررسی فریم ورک Logging
ASP.NET Core به همراه یک فریم ورک توکار ثبت وقایع (Logging) ارائه شده‌ی توسط تزریق وابستگی‌ها است که به صورت پیش فرض نیز فعال است.


این تصویر را پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویس‌ها و تزریق وابستگی‌ها» مشاهده کرده‌اید. در اینجا لیست سرویس‌هایی را مشاهده می‌کنید که به صورت پیش فرض، ثبت شده‌اند و فعال هستند و ILogger و ILoggerFactory نیز جزئی از آن‌ها هستند. بنابراین نیازی به فعال سازی آن‌ها نیست؛ اما برای استفاده‌ی از آن‌ها نیاز به انجام یک سری تنظیمات است.


پیاده سازی ثبت وقایع در ASP.NET Core

اولین قدم کار با فریم ورک ثبت وقایع ASP.NET Core، معرفی ILoggerFactory به متد Configure کلاس آغازین برنامه است:
public void Configure(ILoggerFactory loggerFactory, IApplicationBuilder app, IHostingEnvironment env)
{
   loggerFactory.AddConsole(Configuration.GetSection("Logging"));
   loggerFactory.AddDebug();
متد Configure امضای مشخصی را ندارد و در اینجا به هر تعداد سرویسی که نیاز باشد، می‌توان اینترفیس‌های آن‌ها را جهت تزریق وابستگی‌های متناظر توسط IoC Containser توکار ASP.NET Core، معرفی کرد. در اینجا برای تنظیم ویژگی‌های سرویس ثبت وقایع، تزریق وابستگی ILoggerFactory  صورت گرفته‌است.
سطر اول متد، تنظیمات ثبت وقایع را از خاصیت Logging فایل appsettings.json برنامه می‌خواند (در مورد خاصیت Configuration، در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایل‌های config» بیشتر بحث شد) و لاگ کردن ویژه‌ی در کنسول NET Core. را فعال می‌کند:
{
    "Logging": {
        "IncludeScopes": false,
        "LogLevel": {
            "Default": "Debug",
            "System": "Information",
            "Microsoft": "Information"
        }
    }
}
در مورد Log Level و یا سطوح ثبت وقایع، در ادامه‌ی مطلب بحث خواهد شد.

و سطر دوم سبب نمایش اطلاعات لاگ شده در کنسول دیباگ ویژوال استودیو می‌شود.
متد AddDebug برای شناسایی، نیاز به افزودن وابستگی‌های ذیل در فایل project.json برنامه را دارد:
{
    "dependencies": {
        //same as before 
        "Microsoft.Extensions.Logging": "1.0.0",
        "Microsoft.Extensions.Logging.Console": "1.0.0",
        "Microsoft.Extensions.Logging.Debug": "1.0.0" 
    }
}
پس از این تنظیمات، برنامه را اجرا کنید.


در اینجا می‌توانید ریز وقایعی را که توسط ASP.NET Core لاگ شده‌است، مشاهده کنید. برای مثال چه درخواستی صورت گرفته‌است و چقدر اجرای آن زمان‌برده‌است.
این فعال سازی مرتبط است به متد AddDebug که اضافه شد. اگر می‌خواهید خروجی AddConsole را هم مشاهده کنید، از طریق خط فرمان، به پوشه‌ی اصلی پروژه وارد شده و سپس دستور dotnet run را اجرا کنید:


دستور dotnet run سبب راه اندازی وب سرور برنامه بر روی پورت 5000 شده‌است که در تصویر نیز مشخص است.
بنابراین اینبار برای دسترسی به برنامه باید مسیر http://localhost:5000 را در مرورگر خود طی کنید. در اینجا نیز می‌توان حالت‌های مختلف اطلاعات لاگ شده را مشاهده کرد و تمام این‌ها مرتبط هستند به ذکر متد AddConsole .


کار با سرویس ثبت وقایع ASP.NET Core از طریق تزریق وابستگی‌ها

برای کار با سرویس ثبت وقایع توکار ASP.NET Core در قسمت‌های مختلف برنامه، می‌توان از ترزیق وابستگی ILogger آن استفاده کرد:
[Route("[controller]")]
public class AboutController : Controller
{
    private readonly ILogger<AboutController> _logger;
 
    public AboutController(ILogger<AboutController> logger)
    {
        _logger = logger;
    }
 
    [Route("")]
    public ActionResult Hello()
    {
        _logger.LogInformation("Running Hello");
        return Content("Hello from DNT!");
    }
در این کنترلر، وابستگی اینترفیس ILogger با پارامتری از نوع کنترلر جاری به سازنده‌ی کلاس تزریق شده‌است. علت ذکر این پارامتر جنریک این است که ILoggerFactory بداند چگونه باید متد CreateLogger خود را در پشت صحنه وهله سازی کند.
سپس با توجه به اینکه این سرویس جزو سرویس‌های از پیش ثبت شده‌ی ASP.NET Core است، امکانات آن بدون نیاز به تنظیمات بیشتری در دسترس است. برای مثال از متد LogInformation آن در اکشن متد Hello استفاده شده‌است و خروجی عبارت لاگ شده‌ی آن‌را در اینجا می‌توانید مشاهده کنید:



سطوح مختلف ثبت وقایع

اینترفیس ILogger به همراه متدهای مختلفی است؛ مانند LogError، LogDebug و غیره. معانی آن‌ها به شرح زیر هستند:
Debug (1): ثبت واقعه‌ای است با بیشترین حد جزئیات ممکن که عموما شامل اطلاعات حساسی نیز می‌باشد. بنابراین نباید در حالت ارائه‌ی نهایی برنامه فعال شود.
(2) Verbose: ثبت وقایعی مفصل، جهت بررسی مشکلات در حین توسعه‌ی برنامه. تنها باید حاوی اطلاعاتی برای دیباگ برنامه باشند.
(3) Information: عموما برای ردیابی قسمت‌های مختلف برنامه مورد استفاده قرار می‌گیرند.
(4) Warning: جهت ثبت واقعه‌ای نامطلوب در سیستم بکار می‌رود و سبب قطع اجرای برنامه نمی‌شود.
(5) Errors: مشکلات برنامه را که سبب قطع سرویس دهی آن شده‌اند را ثبت می‌کند. هدف آن ثبت مشکلات واحد جاری است و نه کل برنامه.
Critical (6): هدف آن ثبت مشکلات بحرانی کل سیستم است که سبب از کار افتادن آن شده‌اند.

برای مثال در حین تنظیم متد AddDebug که سبب نمایش اطلاعات لاگ شده در کنسول دیباگ ویژوال استودیو می‌شود، می‌توان حداقل سطح ثبت وقایع را نیز ذکر کرد:
 loggerFactory.AddDebug(minLevel: LogLevel.Information);
این حداقل مرتبط است با اعدادی که در کنار سطوح فوق ملاحظه می‌کنید. برای مثال اگر حداقل سطح ثبت وقایع به Information تنظیم شود، چون سطح آن 3 است، دیگر سطوح پایین‌تر از آن لاگ نخواهند شد. اهمیت این مساله در اینجا است که اگر صرفا نیاز به اطلاعات Critical داشتیم، نیازی نیست تا با انبوهی از اطلاعات لاگ شده سر و کار داشته باشیم و به این ترتیب می‌توان حجم اطلاعات نمایش داده شده را کاهش داد.

البته ترتیب واقعی این سطوح را در enum مرتبط با آن‌ها بهتر می‌توان مشاهده کرد:
  public enum LogLevel
  {
    Trace,
    Debug,
    Information,
    Warning,
    Error,
    Critical,
    None,
  }

یک نکته: زمانیکه متد AddDebug را بدون پارامتر فراخوانی می‌کنید، حداقل سطح ثبت وقایع آن به Information تنظیم شده‌است. یعنی در این لاگ، خبری از اطلاعات Debug نخواهد بود (چون سطح دیباگ پایین‌تر است از Information).  بنابراین اگر می‌خواهید این اطلاعات را هم مشاهده کنید باید پارامتر minLevel آن‌را به LogLevel.Debug تنظیم نمائید.


امکان استفاده‌ی از پروایدرهای ثبت وقایع ثالث

تا اینجا، دو نمونه از پروایدرهای توکار ثبت وقایع ASP.NET Core را بررسی کردیم. اگر نیاز به ثبت این اطلاعات با فرمت‌های مختلف و یا در بانک اطلاعاتی وجود دارد، می‌توان به تامین کننده‌های ثالثی که قابلیت کار با ILoggerFactory را دارند نیز مراجعه کرد. برای مثال:
- elmah.io - provider for the elmah.io service
- Loggr - provider for the Loggr service
- NLog - provider for the NLog library
- Serilog - provider for the Serilog library
نظرات مطالب
EF Code First #12
- من در مورد الگوی مخزن در قسمت 11 این سری بحث کردم (کامنت‌های آن‌را هم بخوانید)؛ همچنین این مباحث در مورد EF Code first است و نه db first و نه EF 4. به علاوه این لینک‌هایی که مطرح کردید مثلا نمونه code project، داخل به اصطلاح BLL خودش، پر است از وهله سازی Context و من در این مطلب توضیح دادم که چرا اینکار غلط است و چگونه استفاده از یک تراکنش برای چندین عملیات مرتبط را زیر سؤال می‌برد.
- اون اینترفیس IUnitOfWork مطرح شده در مثال جاری، وابستگی خاصی به DataLayer نداره. می‌تونه در لایه سرویس هم تعریف بشه (منظور این است می‌تونه در یک اسمبلی و پروژه جداگانه هم قرار بگیره و مشکلی نیست). اما DataLayer باید بتونه در حین تزریق وابستگی‌ها وهله‌ای از IUnitOfWork رو فراهم کنه تا به اون معنا ببخشه؛ به همین جهت Context برنامه باید آن‌را پیاده سازی کند تا توسط StructureMap قابل شناسایی و استفاده شود.
اما نهایتا وهله سازی اینترفیس یاد شده توسط DAL صورت می‌گیره. uow به خودی خود موجودیتی نداره. در اینجا مثلا EF هست که به اون معنا می‌بخشه و سبب وهله سازی آن خواهد شد. هرچند به ظاهر برنامه با اینترفیس‌ها کار می‌کند اما تزریق وابستگی‌ها است که به این اینترفیس‌ها موجودیت می‌بخشد و سبب دسترسی به وهله‌ای که قرار داد ارائه شده توسط آن‌ها را پیاده سازی کرده می‌شود.
- در یک سیستم nTier هم مباحث ذکر شده در این قسمت، جاری است. مثلا یک WCF Service قرار گرفته روی یک سرور مجزا هم می‌تونه از DataLayer و ServiceLayer مثال جاری استفاده کند. استفاده کننده نهایی برای نمایش آن در UI با هیچکدام از دو مورد ذکر شده کاری ندارد و فقط با قراردادهای WCF Service کار می‌کنه.
مطالب
چگونه نرم افزارهای تحت وب سریعتری داشته باشیم؟ قسمت هفتم
قسمت ششم 

20.اسکریپت در پایین صفحه
لینک‌های مربوطه به javascript‌های خود را تا جای ممکن در پایین صفحه قرار دهید. وقتی parser مرورگر به فایل‌های javascript می‌رسد، تمامی فعالیت‌ها را متوقف کرده و سعی در دانلود و سپس اجرای آن دارد. برخلاف اینکه مرورگرها امکان دانلود چند فایل را به صورت همزمان از سرور دارند، هنگامی که به اسکریپت‌ها می‌رسند، تنها یک فایل را دانلود می‌کنند. یعنی اجرای برنامه و دانلودهای مرتبط با صفحه شما متوقف شده و پس از دانلود و اجرای اسکریپت اجرای آنها ادامه پیدا می‌کند. این مسئله وقتی نمود بیشتری پیدا می‌کند که شما فایل هغای اسکریپت با حجم و تعداد بالا در برنامه خود استفاده می‌کنید.
برای فرار از این مشکل می‌توانید تگهای مربوط به اسکریپت را در آخر صفحات خود بگذارید. فقط دقت کنید اگر نیاز است که قبل از نمایش صفحه تغییری ذر DOM ایجاد کنید، باید حتما اسکریپت‌های مربوطه را بالای صفحه قرار دهید.
روش دیگر دانلود فایل‌های اسکریپت به وسیله AJAX است که انشاء الله در آینده مقاله ای در این رابطه خواهم نوشت.

21.CDN
CDN یا Content Delivery Network سرورهای توزیع شده ای در سطح دنیا هستند که یک نسخه از برنامه شما برای اجرا بر روی آن قرار دارد. هنگامی که کاربر می‌خواهد به سایت شما دسترسی پیدا کند به صورت خودکار به نزدیکترین سرور منتقل می‌شود تا بتواند سرعت بیشتری را تجربه کند. علاوه بر این CDN باعث بالانس شدن بار ترافیک شبکه شما شده خط حملات D.O.S و D.D.O.S را به حداقل می‌رساند.

زمانیکه شما یک سیستم CDN را فعال میکنید تاثیر آن بصورت زیر خواهد بود:
۱- شبکه توزیع محتوا یا همان CDN تمامی سرورهای شبکه جهانی اینترنت را پوشش میدهد. بنابراین زمانیکه شما این سیستم را برای سایت خود فعال میکنید اطلاعات شما بر روی تمامی این سرورها کپی و ذخیره میشود و زمانیکه یک بازدیدکننده به سایت یا وبلاگ شما وارد میشود محتوای سایت شامل تصاویر و متون را از نزدیک‌ترین سرور نزدیک به خود دریافت میکند و مستقیما به هاست یا سرور شما متصل نمیشود. این کار موجب بهبودی چشمگیر در عملکرد سایت شما خواهد شد.
۲- CDN تمام اطلاعات ثابت شما مانند تصاویر، کدهای CSS و javascript، mp3، pdf و فایلهای ویدئویی شما را پشتیبانی میکند و تنها اطلاعاتی که قابل تغییر و بروزرسانی هستند مانند متون و کدهای HTML از سرور اصلی شما فراخوان میشوند. با این کار مصرف پهنای باند هاست شما کاهش یافته و هزینه ای که سالانه برای آن میپردازید کاهش چشمگیری خواهد داشت.
۳- تفاوت سرعت و عملکرد برای خودتان یا افرادی که در نزدیکی سرور اصلی شما هستند تفاوت زیادی نخواهد داشت ولی برای کسانی که ار نقاط مختلف جهان به سایت شما وارد میشوند این افزایش سرعت ناشی از CDN کاملا محسوس خواهد بود، با توجه به اینکه سایتهای ایرانی معمولا سرور و هاست خود را از خارج و کشورهایی مانند آلمان و آمریکا تهیه میکنند و عموم بازدیدکنندگان از داخل کشور هستند استفاده از CDN میتواند بسیار موثر باشد. برای تعیین تاثیر CDN بر سرعت سایت میتوانید عملکرد خود را با ابزارهایی مانند Pingdom و GTmetrix بعد و قبل از فعال سازی CDN بررسی و مقایسه کنید. 
ابزارها، تکنیک‌ها و روش‌های متفاوتی برای راه اندازی CDN وجود دارد که نیازمند مقاله ای مجزا می‌باشد.
مطالب
MongoDb در سی شارپ (بخش اول)
MongoDb  یک دیتابیس Nosql سندگراست که توسط ++C نوشته شده است و از پشتیبانی خوبی در بسیاری از زبان‌ها برخوردار است. مونگو از ساختاری به نام Bson که ساختاری مشابه Json را دارد استفاده می‌کند؛ با این تفاوت که در Json مبحث دیتاتایپ یا نوع داده وجود ندارد، ولی در Bson دیتاتایپ‌ها تعریف می‌شوند. برای دیدن نوع‌های Bson و نحوه نوشته شدن سند آن می‌توانید مقاله MongoDb#7 را مطالعه بفرمایید.


برای آغاز به کار با این دیتابیس ابتدا باید آن را از سایت اصلی دریافت و بر روی سیستم نصب نمایید. متاسفانه سایت مونگو برای کشور ایران محدودیتی قرار داده است و باید از روش‌های دیگری آن را دریافت نمایید و بر روی سیستم خود نصب نمایید. نحوه نصب این دیتابیس را میتوانید در مقاله MongoDb#3 مشاهده نمایید.

شاید نیاز باشد بجای کار کردن با محیط کنسول این دیتابیس، با یک محیط گرافیکی شبیه آن چیزی که Raven دارد کار کنید وتغییرات را مشاهده نمایید؛ برای همین به این آدرس رفته و محیط دلخواه خود را انتخاب نمایید.

  یک پروژه از نوع کنسول را در ویژوال استادیو ایجاد کنید و سپس درایور رسمی مونگو را از این آدرس یا از طریق nuget نصب نمایید:
Install-Package mongocsharpdriver

ابتدا سه مدل را به شکل زیر ایجاد میکنیم:
  public class Author
    {
        public ObjectId Id { get; set; }
        public string Name { get; set; }
    }

public class Language
    {
        public ObjectId Id { get; set; }
        public string Name { get; set; }
    }

  public class Book
    {
        public ObjectId Id { get; set; }
        public string Title { get; set; }
        public string ISBN { get; set; }
        public int Price { get; set; }
        public List<Author> Authors { get; set; }
        public Language Language { get; set; }
    }

نوع ObjectId، نوعی است که توسط مونگو برای مشخص کردن کلید یکتای سند معرفی میشود.

در خطوط اولیه کد زیر، یک شیء از مدل بالا را ساخته و آن را مقداردهی می‌کنیم:
           var book =new Book()
           {
               Title = "Gone With Wind",
               ISBN = "43442424",
               Price = 50000,
               Language = new Language()
               {
                  Name = "Persian"
               },
               Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Margaret Mitchell"
                   },
                     new Author()
                   {
                       Name = "Ali Mahboobi (Translator)"
                   },
               }
           };

بعد از آن یک شیء کلاینت از نوع mongoClient میسازیم که نوع خروجی آن یک اینترفیس میباشد که توسط کلاسی از جنس آن مقداردهی شده است. بیشتر خروجی‌های مونگو در این کتابخانه از نوع اینترفیس هستند. شیء کلاینت وظیفه دارد تا ارتباط شما را با سرور مونگو برقرار کند:
var client = new MongoClient();
البته در این حالت سرور اتصالی مونگو، سیستم جاری و پورت شماره 27017 فرض میشود. در صورتیکه بخواهید آدرسی غیر از آن را بدهید یا حتی همین آدرس را به طور دستی تعیین کنید، از طریق زیر امکان پذیر است. پارامترهای سازنده این شیء کلاینت میتوانند به صورت رشته‌ای، رشته اتصال را دریافت کنند و یا از طریق شیء MangoClientSettings آن را پاس کنید.
 string connectionString = "mongodb://localhost:27017";
            MongoClientSettings settings = MongoClientSettings.FromUrl(new MongoUrl(connectionString));
            var client = new MongoClient(settings);

در قسمت بعد لازم است که از سرور جاری، دیتابیس خود را دریافت کنیم. در صورتیکه دیتابیس درخواستی وجود نداشته باشد، یک دیتابیس جدید با آن نام ساخته خواهد شد:
var db = client.GetDatabase("publisher");
در نمونه کدهای قدیمی مونگو، قبل از دریافت سرور بایستی شیء Server را از طریق متد GetServer نیز دریافت میکردید که از نسخه دو به بعد، آن را منسوخ اعلام کرده‌اند و همین تنظیمات بالا کفایت میکند.
در مونگو اصطلاحی به نام collection وجود دارد که اسناد در آن قرار گرفته و ارتباط با اسناد از طریق آنها انجام می‌پذیرد. پس در اینجا قبل از هر کاری باید یک collection را ایجاد کرد و در صورتیکه کالکشن درخواستی وجود نداشته باشد، آن را تولید و ارتباط با آن را برخواهد گرداند.
var collection = db.GetCollection<Book>("books");

در اینجا کالکشنی با نام books با تبدیلاتی بر اساس مدل Book ایجاد میشود. در مرحله بعد لازم است که شیء ایجاد شده بر اساس کلاس مدل را با استفاده از متدهای insert شیء کالکشن، در دیتابیس ارسال کنیم.
شی‌ءهای درج یک سند جدید به دیتابیس حالات مختلفی را دارد: افزودن تک سند، افزودن چند سند و دو مورد قبلی به صورت غیر همزمان میباشند:
collection.InsertOneAsync(book);
متد بالا سند جدید را به صورت غیرهمزمان در سیستم درج میکند. نمونه ذخیره شده این سند را که توسط برنامه Mongo Compass نمایش داده شده است، می‌توانید در زیر ببینید:

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


عملیات خواندن

برای خواندن یک یا چند سند از دیتابیس میتوانید از دو شیوه موجود Linq و queryBuilder‌ها استفاده کرد. از آنجائیکه با کار با Linq آشنایی داریم، ابتدای شیوه کوئری بیلدر را مورد بررسی قرار میدهیم و سپس نحوه کار با لینک را بررسی میکنیم.
قبل از هر چیزی برای اینکه در مانور دادن بر روی داده‌ها راحت باشیم و اطلاعات را با فیلترهای متفاوتی واکشی کنیم، 7 عدد کتاب را با مشخصات زیر اضافه میکنیم. دو فیلد سال و تاریخ آخرین موجودی انبار را هم اضافه می‌کنیم.
       var client = new MongoClient();
            var db = client.GetDatabase("publisher");
            db.DropCollection("books");
            var collection = db.GetCollection<Book>("books");
            
            var book =new Book()
           {
               Title = "Gone With Wind",
               ISBN = "43442424",
               Price = 50000,
               Year = 1936,
               LastStock = DateTime.Now.AddDays(-13),
               Language = new Language()
               {
                  Name = "Persian"
               },
               Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Margaret Mitchell"
                   },
                     new Author()
                   {
                       Name = "Ali Mahboobi (Translator)"
                   },
               }
           };

            var book2 = new Book()
            {
                Title = "Jane Eyre",
                ISBN = "87897897",
                Price = 60000,
                Year = 1847,
                LastStock = DateTime.Now.AddDays(-5),
                Language = new Language()
                {
                    Name = "English"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Charlotte Brontë"
                   },
                   
               }
            };


            var book3 = new Book()
            {
                Title = "White Fang",
                ISBN = "43442424",
                Price = 50000,
                Year = 1936,
                LastStock = DateTime.Now.AddDays(-13),
                Language = new Language()
                {
                    Name = "English"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Jack London"
                   },
                     new Author()
                   {
                       Name = "Philippe Mignon"
                   },
               }
            };

            var book4 = new Book()
            {
                Title = "The Lost Symbol",
                ISBN = "43442424",
                Price = 3500000,
                Year = 2009,
                LastStock = DateTime.Now.AddDays(-17),
                Language = new Language()
                {
                    Name = "Persian"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Dan Brown"
                   },
                     new Author()
                   {
                       Name = "Mehrdad"
                   },
               }
            };

            var book7 = new Book()
            {
                Title = "The Lost Symbol",
                ISBN = "43442424",
                Price = 47000000,
                Year = 2009,
                LastStock = DateTime.Now.AddDays(-56),
                Language = new Language()
                {
                    Name = "Persian"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Dan Brown"
                   },
                     new Author()
                   {
                       Name = "Mehrdad"
                   },
               }
            };
            var book5= new Book()
            {
                Title = "The Help",
                ISBN = "45345e3er3",
                Price = 9000000,
                Year = 2009,
                LastStock = DateTime.Now.AddDays(-2),
                Language = new Language()
                {
                    Name = "Enlish"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Kathryn Stockett"
                   },
                    
               }
            };

            var book6 = new Book()
            {
                Title = "City of Glass",
                ISBN = "454534545",
                Price = 500000,
                Year = 2009,
                LastStock = DateTime.Now,
                Language = new Language()
                {
                    Name = "Persian"
                },
                Authors = new List<Author>()
               {
                   new Author()
                   {
                       Name = "Cassandra Clare"
                   },
                   new Author()
                   {
                       Name = "Ali"
                   },

               }
            };
            var books = new List<Book> {book, book2, book3, book4, book5, book6,book7};

            collection.InsertManyAsync(books);

برای واکشی دیتاها کالکشنی از آن نوع را همانند قبل درخواست می‌کنیم. بعد از آن نیاز است که فیلتری برای واکشی اطلاعات تعریف کنیم که این فیلتر در قالب یک کلاس به نام BsonDocument ایجاد می‌شود که ما در اینجا، به دلیل اینکه میخواهیم همه اسناد را واکشی کنیم ، این سند Bson را مقداردهی نمیکنیم و توسط متد Find آن را در واکشی دیتاها شرکت میدهیم و سپس با صدا زدن متد ToList، عملیات واکشی را انجام میدهیم، برای اینکار می‌توانیم از عملیات غیرهمزمان هم استفاده کنیم.
            var client = new MongoClient();
            var db = client.GetDatabase("publisher");
            var collection = db.GetCollection<Book>("books");
            
            var filter=new BsonDocument();
            var docs = collection.Find(filter).ToList();
            foreach (var book in docs)
            {
                Console.WriteLine(book.Title + " By "+ book.Authors[0].Name);
            }

با اجرای کد بالا به نتایج زیر میرسیم:
Gone With Wind By Margaret Mitchell
Jane Eyre By Charlotte Brontë
White Fang By Jack London
The Lost Symbol By Dan Brown
The Help By Kathryn Stockett
City of Glass By Cassandra Clare
The Lost Symbol By Dan Brown

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

در خطوط بالا ابتدا یک بیلدر را برای کلاس مورد نظر ایجاد کرده و از خصوصیت Filter آن استفاده میکنیم و این خصوصیت شامل متدهای فراوانی است که میتوانید برای ایجاد شرط یا فیلتر استفاده کنید. تعدادی از متدهای پر استفاده آن همانند eq (برابری) ، gt (برزگتر از ...) ، gte (بزرگتر مساوی ...) و طبیعتا خانواده lt و ... موجود هستند.
var filter = Builders<Book>.Filter.Eq("Year", 2009);

            var docs = collection.Find(filter).ToList();
            foreach (var book in docs)
            {
                Console.WriteLine(book.Title + " By "+ book.Authors[0].Name);
            }
در کد بالا ما از متد eq برای بررسی برابر بودن استفاده کردیم و درخواست اسنادی را کردیم که دارای فیلد سال انتشار هستند و مقدار آن برابر 2009 میباشد و نتیجه آن به صورت زیر نمایش داده میشود:
The Lost Symbol By Dan Brown
The Help By Kathryn Stockett
City of Glass By Cassandra Clare
The Lost Symbol By Dan Brown
حتی می‌توانید با استفاده از شیء فیلتر، ترکیبات شرطی زیر را نیز اعمال نمایید:
   // var filter=new BsonDocument();
            var filterBuilder = Builders<Book>.Filter;
            var filter= filterBuilder.Eq("Year", 2009) | filterBuilder.Gte("Price",700000);

            var docs = collection.Find(filter).ToList();
            foreach (var book in docs)
            {
                Console.WriteLine(book.Title + " By "+ book.Authors[0].Name);
            }
در بالا ابتدا نوعی از شیء Filter را از کلاس Builder دریافت میکنیم و سپس با استفاده عملیات بیتی آن‌ها را با یکدیگر Or میکنیم. در این پرس و جو باید کتابهایی که در سال 2009 منتشر شده‌اند یا قیمتی کمتر از پنجاه هزار ریال یا برابر را دارند، نمایش داده شوند.

Gone With Wind By Margaret Mitchell
White Fang By Jack London
The Lost Symbol By Dan Brown
The Help By Kathryn Stockett
City of Glass By Cassandra Clare
The Lost Symbol By Dan Brown

برای اینکه بتوانید از linq به جای queryBuilder استفاده کنید، میتوانید از خصوصیت AsQueryable استفاده کنید. خط زیر همان شرط یا فیلتر بالا را توسط Linq اعمال میکند
var docs = collection.AsQueryable().Where(x => x.Year == 2009 || x.Price <= 50000).ToList();

Sort کردن داده‌ها
 
برای مرتب سازی اطلاعات به شیوه کوئری بیلدر، همانند فیلتر که از کلاس Builder استفاده میکردیم، از همین شیء استفاده میکنیم؛ با این تفاوت که بجای استفاده از خصوصیت Filter، از Sort استفاده میکنیم و شیء ایجاد شده را به متد Sort میدهیم:
  var sort = Builders<Book>.Sort.Ascending("Title").Descending("Price");
            var docs = collection.Find(filter).Sort(sort).ToList();
            foreach (var book in docs)
            {
                Console.WriteLine(book.Title + " By "+ book.Authors[0].Name);
            }
در نتیجه خطوط بالا کتاب‌ها ابتدا بر اساس نام به صورت صعودی و سپس بر اساس قیمت به صورت نزولی لیست می‌شوند.
City of Glass By Cassandra Clare
Gone With Wind By Margaret Mitchell
The Help By Kathryn Stockett
The Lost Symbol By Dan Brown
The Lost Symbol By Dan Brown
White Fang By Jack London

توجه داشته باشید که متد sort بعد از فیلتر گذاری، یعنی عمل Find در دسترس میباشد.

در قسمت بعدی به روزرسانی، حذف و ایندکس گذاری را مورد بررسی قرار می‌دهیم.
مطالب
PHP سریعتر از ASP.NET! افسانه یا واقعیت؟

چرا افسانه‌ای که می‌گوید PHP از ASP.NET سریعتر است اینقدر شایع است؟ در این مقاله به بیان حقایقی می‌پردازیم که این افسانه را زیر سوال می‌برد؟

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

حال بجای اینکه این موضوع را بارها و بارها در جاهای مختلف بیان کنیم، این مقاله را نوشته و در هر کجا که لازم باشد به آن ارجاع خواهیم داد. باید توجه کنید این حقیقت که زبان PHP یک زبان اصیل و قدرتمند است هیچ شکی در آن نیست اما اینکه بخواهیم بصورت مغرضانه و به این دلیل که ما از این زبان استفاده می‌کنیم، آنرا از هر لحاظ برتر از سایر زبانها بدانیم (کمی که نه) بسیار اغراق آمیز است.

این مقاله برای این نیست که ما هریک از این زبانها را زیر سوال ببریم. بلکه برای آن است که این موضوع را با دلایل منطقی و حقیقی بررسی کنیم که آیا اینکه می‌گویند PHP از ASP.net سریعتر است واقعیت دارد یا نه؟

Compiled در مقابل Interpreted Languages:

قبل از هرچیز ذکر این نکته الزامی است که این دو زبان تفاوتهای اساسی در base دارند. ASP.net یک زبان بهینه سازی و کامپایل شده است، به این معنی که کدهای نوشته شده در این زبان قبل از اینکه قابل اجرا شوند، به مجموعه ای از دستورالعمل‌های خاص ماشین تبدیل می‌شوند. از سوی دیگر PHP یک زبان تفسیر شده است، به این معنی که کدهای نوشته شده به همان شکل ذخیره شده و در زمان اجرا این کدها تفسیر می‌شوند. این موضوع بطور گسترده‌ای پذیرفته شده و ثابت شده است که برنامه‌های کامپایل شده به مراتب سریعتر از برنامه‌های تفسیر شده اجرا می‌شوند، به این دلیل که برنامه‌های تفسیر شده نیاز دارند تا در زمان اجرا به دستورالعملهای ماشین تبدیل شوند.

در اینجا به یک نقل قول از دانشنامه آزاد ویکی پدیا اشاره می‌کنم که میزان سریعتر بودن برنامه‌های کامپایل شده را نشان می‌دهد:

"A program translated by a compiler tends to be much faster than an interpreter executing the same program: even a 10:1 ratio is not uncommon. The mixed solution's efficiency is typically somewhere in between."

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

اعداد و ارقام:

حال که تئوری خود را مبنی بر دلیل سریعتر بودن ASP.net بیان کردیم بیایید با هم نگاهی به برخی آمارها بیاندازیم تا این تئوری را در عمل هم نشان داده باشیم.

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

نمودار اول: زمان صرف شده برای تولید ونمایش نتایج برای جستجوی وب سایت‌های کوچک


همانطور که میبینید زمان متوسط برای سایت PHP، 0.1500 ثانیه و برای سایت ASP.net، 0.0150 ثانیه است. یک تفاوت بزرگ: PHP ده برابر نسبت به ASP.net کندتر است!

نمودار دوم: زمان صرف شده برای تولید و نمایش نتایج برای جستجوی وب سایت‌های متوسط


PHP، 1.0097 ثانیه طول می‌کشد در حالی که ASP.net، 0.0810 ثانیه زمان نیاز دارد. می‌بینیم که PHP دوازده بار بیشتر از ASP.net زمان می‌برد.

درحال حاضر این آزمون با یک کد مشابه در زبانهای برنامه نویسی مختلف پیاده سازی و اجرا شد و نتیجه را مشاهده نمودید. حال این موضوع پیش میاید که این اجرای کدها بر روی سیستم عامل ویندوزی بوده است و این می‌تواند به نفع ASP.net باشد، پس همین آزمون را بر روی سیستم عامل لینوکسی مشاهده می‌کنیم.

آمارهای زیر از سایت معتبر shootout.alioth.debian.org گرفته شده است. این آمارها نحوه اجرای همان کد را بر روی سیستم عامل لینوکسی برای هردو زبان نشان می‌دهد: 

 

همانطور که مشاهده می‌کنید در سیستم لینوکسی نیز همچنان ASP.net سریعتر از PHP عمل می‌کند.

نتیجه گیری:

همین حالا جمله‌ی "asp.net vs php speed" را در google جستجو کنید. خواهید دید که در اکثر پست‌ها گفته شده که PHP از ASP.net سریعتر است اما دلیلی بر این ادعا نخواهید یافت و فقط در حد حرف است. مشکل این است که اکثر مردم وقتی چیزی را زیاد می‌بینند یا زیاد می‌شنوند بدون آنکه دلیل بخواهند آنرا می‌پذیریند و حتی بعضی اوقات از آن نیز دفاع می‌کنند که واقعا جای تاسف دارد.

توسعه وب بوسیله PHP کار خوبی است، بسیاری از اپلیکیشن‌ها و وبسایتهای شگفت انگیز توسط این زبان نوشته شده اند. اگر احساس می‌کنید PHP یک زبان برتر است از آن استفاده کنید اما این دلیل نمی‌شود که اطلاعات غلط را به دیگران القاء کنید و بدون دلیل و مدرک این زبان را از هر لحاظ برتر بدانید حال آنکه در این مقاله دیدیم که براساس چیزی که ارائه شد، ASP.net سرعت بیشتری نسبت به PHP دارد.

اگر با من در این امر موافق نیستید می‌توانید با نظرهای مستدل خود ما را راهنمایی کنید. 

مطالب
توسعه برنامه‌های Cross Platform با Xamarin Forms & Bit Framework - قسمت هجدهم
در این قسمت می‌خواهیم با Rest Api ارتباط برقرار کنیم. به جای نوشتن سمت سرور، از یک سرور آماده استفاده می‌کنیم که مثال اول آن، LIST USERS است و لیست کاربران را نمایش می‌دهد. توضیحات این قسمت به فراخوانی سرویس‌های Rest ارتباط دارد، با پروتکل HTTP و دیتای JSON. البته فراخوانی سرویس‌های SOAP نیز ساده است که در این آموزش به آنها نمی‌پردازیم.
برای این کار از HttpClient استفاده می‌کنیم. استفاده کردن از WebClient و WebRequest اشتباه محض هست و این دو را کلا فراموش کنید. مطمئن باشید هر کدی که با آن دو در اینترنت پیدا می‌کنید، با HttpClient هم قابلیت پیاده سازی را دارند و مطمئن باشید که اگر از آن دو کلاس استفاده کنید، حتما به دردسر بدی میافتید. در زمان استفاده از HttpClient هم در نظر بگیرید که نباید مدام HttpClient را new و dispose کنید. این کار اشتباه است و یک HTTP client برای شما کافی است. ساختن HTTP client نکات  بسیاری دارد که در همین سایت به آنها پرداخته شده‌است. در Xamarin دغدغه‌های استفاده از Network Stack هر سیستم عامل نیز به لیست مواردی که باید به آنها دقت کنید اضافه می‌شوند. می‌توانید درگیر تمامی این موارد شوید، یا برای سادگی بیشتر، ضمن نصب پکیج Bit.CSharpClient.Rest که کدهای آن نیز در GitHub قرار داده شده‌اند، صرفا HTTP Client را بگیرید و به هر جایی که دوست دارید Request بزنید. لزومی به اینکه در سمت سرور از Bit استفاده کرده باشید تا بتوانید از Bit.CSharpClient.Rest استفاده کنید نیست.

خب، پس Package مربوطه را نصب و در App.xaml.cs کدهای زیر را استفاده کنید:
//قرار دهید containerBuilder.RegisterRequiredServices(); این دو را بعد از
containerBuilder.RegisterHttpClient();
containerBuilder.RegisterIdentityClient();
در View Model ای که قصد استفاده از Http Client را دارید، یک Property از جنس Http Client تعریف کنید و برای خواندن اطلاعات مثال، کد زیر را بزنید:
توضیحات این کد در ادامه آمده است.
public virtual HttpClient HttpClient { get; set; } 

async Task CallUsersListApiUsingHttpClient()
{
    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://reqres.in/api/users");
    // Use request.Headers to set jwt token, ...
    // Use request.Content to send body. You can use StringContent, StreamContent, etc.
    HttpResponseMessage response = await HttpClient.SendAsync(request);
    response.EnsureSuccessStatusCode();
    using (StreamReader streamReader = new StreamReader(await response.Content.ReadAsStreamAsync()))
    using (JsonReader jsonReader = new JsonTextReader(streamReader))
    {
        List<UserDto> users = (await JToken.LoadAsync(jsonReader))["data"].ToObject<List<UserDto>>();
    }
}
برای درک بهتر این کد، بعد از Clone/Pull کردن آخرین وضعیت پروژه XamApp به سراغ کلاس RestSamplesViewModel بروید. فراخوانی https://reqres.in/api/users چنین JSON ای را بر می‌گرداند: 
{
    "page": 2,
    "per_page": 3,
    "total": 12,
    "total_pages": 4,
    "data": [
        {
            "id": 4,
            "first_name": "Eve",
            "last_name": "Holt",
            "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/marcoramires/128.jpg"
        },
        {
            "id": 5,
            "first_name": "Charles",
            "last_name": "Morris",
            "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/stephenmoon/128.jpg"
        }
    ]
}

قسمت‌های مختلف این JSON برای ما اهمیتی ندارند و تنها قسمت data آن که اطلاعات user‌ها را شامل می‌شود، برای ما اهمیت دارند. صد البته که هر سروری، دیتای JSON را با ساختاری که دوست داشته باشد بر می‌گرداند. در کدی که نوشته‌ایم، ابتدا یک HttpRequestMessage را ساخته‌ایم. این HttpRequestMessage از نوع Get و به آدرس https://reqres.in/api/users است. می‌توان روی HttpRequestMessage هم هدرهای مختلفی را تنظیم نمود و هم می‌توان به آن Content داد.
سپس آن را با HttpClient.SendAsync ارسال می‌کنیم و با فراخوانی EnsureSuccessStatusCode مطمئن می‌شویم که خطا نداده‌است. برای خواندن Response با بالاترین Performance ممکن، ابتدا از StreamReader برای خواندن Stream دریافتی استفاده می‌کنیم. با توجه به JSON بودن Response دریافتی، از JsonTextReader و JToken استفاده می‌کنیم (این مورد هیچ ربطی به JWT یا Json Web Token ندارد!). بعد از Load کردن آن، قسمت ["data"] را به لیستی از کلاس UserDto تبدیل می‌کنیم. Dto مخفف Data Transfer Object است و دیتایی است که ما یا ارسال می‌کنیم یا در همین سناریو مثال، از سرور دریافت می‌کنیم. کد کلاس UserDto:
public class UserDto
{
    [JsonProperty("id")]
    public int Id { get; set; }
    [JsonProperty("first_name")]
    public string FirstName { get; set; }
    [JsonProperty("last_name")]
    public string LastName { get; set; }
    [JsonProperty("avatar")]
    public string Avatar { get; set; }
}
البته Http Client فقط برای ارسال یا دریافت JSON نیست. می‌توان با آن فایل و Xml و ... نیز ارسال و دریافت نمود. در این قسمت مهم نبود که سرور شما با چه تکنولوژی ای توسعه داده شده‌است. صرف بودن سرور روی پروتکل Http کافی است. واضح است که شما دارید از HttpClient استفاده می‌کنید. در صورتیکه سرور TCP باشد، شما در CSharp می‌توانید از TcpClient و Socket استفاده کنید. اگر سمت سرور شما Wcf یا OData یا Graphql باشد نیز کلاینت‌های خودشان را در CSharp دارید و می‌توانید در پروژه‌تان از تمامی آنها استفاده کنید که آموزش همه این موارد از حوصله این قسمت خارج است؛ اما در صورتیکه سمت سرور شما نیز با Bit توسعه داده شده باشد، می‌توانید با روش‌های خیلی بهتری به سرور خود وصل شوید که این موضوع قسمت‌های بعدی آموزش است.
مطالب
دو تنظیم ضروری VS.NET جهت کار با WPF و Silverlight

تنظیم اول: تغییر نحوه‌ی نمایش پیش فرض فایل‌های XAML

اگر فایل XAML شما اندکی حجیم شود نمایش آن در VS.NET کمی طولانی خواهد شد و حالت پیش فرض نمایش در VS.NET هم split view mode است (نمایش XAML و پیش نمایش آن با هم). این مورد هم پس از مدتی تبدیل به عذاب می‌شود. برای رفع آن می‌توان حالت پیش فرض نمایش یک فایل XAML را به XAML View تنها تغییر داد.
برای این منظور به منوی Tools ، گزینه‌ی Options و سپس قسمت تنظیمات Text editor مراجعه کنید. در اینجا در قسمت XAML ، گزینه‌ی Miscellaneous را انتخاب کرده و سپس "Always open documents in full XAML view" را تیک بزنید.



حتی ممکن است این مورد هم رضایت بخش نباشد. در این حالت می‌توان ویرایشگر پیش فرض را کلا تغییر داد. Design tab را در پایین صفحه از دست می‌دهیم اما هنوز intellisense کار می‌کند و اگر نیاز به designer بود فقط کافی است کلیک راست کرده و گزینه‌ی View designer را انتخاب کرد:
روی یک فایل XAML دلخواه کلیک راست کرده و گزینه‌ی Open with را انتخاب کنید. سپس "Source Code (Text) Editor" را انتخاب کرده و روی دکمه‌ی Set as Default کلیک کنید. تمام!
هر چند Blend این مشکلات را ندارد و با فایل‌های حجیم XAML به خوبی کاری می‌کند.


تنظیم دوم: تغییر نحوه‌ی نمایش مشکلات ناشی از Binding

عموما اگر مشکلاتی در حین عملیات Binding در WPF یا Silverlight وجود داشته باشند، خطاها در Debugger Output Window نمایش داده می‌شوند. حالت پیش فرض هم فقط روی Error تنظیم شده است به این معنا که warning ها را مشاهده نخواهید کرد. برای تغییر این مورد باید به صورت زیر عمل کرد:
به منوی Tools ، گزینه‌ی Options و سپس قسمت تنظیمات Debugging مراجعه کنید. گزینه‌ی Output Window -> WPF Trace Settings را انتخاب نمائید. سپس در اینجا قسمت WPF trace settings را یافته و مقدار پیش فرض Data binding را که به Error تنظیم شده است، به Warning تنظیم نمائید.