نظرات مطالب
راه اندازی StimulSoft Report در ASP.NET MVC
در سمت سرور کلاس‌هایی به صورت زیر می‌توانید داشته باشید:
public class PlanModel
{
    public int RId { get; set; }
    public string Day { get; set; }
    public string Plan { get; set; }
    public List<RecordModel> Records { get; set; }
}

public class RecordModel
{
    public int RId { get; set; }
    public string Help { get; set; }
}
که لیستی از PlanModel پس از فراهم کردن اطلاعاتش،  به گزارش ارسال می‌کنید.
در فایل mrt گزارش خود نیز در قسمت Business Objects دو Business Object تولید می‌کنید، که مدل دوم (از نوع RecordModel) به عنوان فرزند مدل اول (از نوع PlanModel) است.

برای نمایش اطلاعات این دو مدل هم، به دو Data band احتیاج دارید.

 نکته‌ای که در اینجا مهم است باید پراپرتی Master Component، بند دوم برابر بند اول مقداردهی شود.
نمونه فایل: MultiBusinessObject.mrt 
مطالب
قابل ویرایش کننده‌ی فوق العاده x-editable ؛ قسمت اول
من در یکی از پروژه‌ها از Kendo UI Treeview استفاده کردم و قصد داشتم قابلیت تغییر نام را به گره‌ها بدهم. به همین جهت پس از جستجو به x-editable برخوردم. این کتابخانه‌ی جاواسکریپتی در ابتدا برای قالب‌های بوت استراپ طراحی شده بود که در حال حاضر اینگونه نیست و به راحتی در هر پروژه‌ای که فقط جی کوئری صدا زده شده باشد، قابل اجرا است و نسخه‌ی مخصوص Angular آن هم در این آدرس قرار دارد. همچنین این قابلیت اختیاری و پیش فرض را دارد که به طور خودکار اطلاعات تغییر یافته را به سمت url ایی که شما تعیین می‌کنید، ارسال کند. برای همین نیازی به استفاده جداگانه از jQuery Ajax برای ارسال اطلاعات نیست و البته در انتهای مقاله هم هنگام استفاده از درخت کندو به مشکلی برخوردم که آن را هم بررسی می‌کنیم.

وارد کردن کتابخانه ها
این کتابخانه شامل دو فایل css و JS می‌باشد که بسته به محیطی که در آن کار می‌کنید متفاوت هستند. در این صفحه شما می‌توانید برای 4 محیط Jquery ,JqueryUI , Bootstrap2 و Bootsrap3 بسته‌ی مخصوصش را یا به صورت دانلود فایل‌ها یا از طریق CDN دریافت نمایید. در اینجا هم یک دمو از قابلیت‌های آن قابل مشاهده است.

برای شروع، کتابخانه‌ی مورد نظر خود را دریافت و آن‌ها را به صفحه‌ی خود اضافه نمایید. در صورتیکه از Bootstrap استفاده می‌کنید، ابتدا فایل‌های زیر را اضافه کنید:
    <link href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css" rel="stylesheet">
    <script src="http://code.jquery.com/jquery-2.0.3.min.js"></script> 
    <script src="//netdna.bootstrapcdn.com/bootstrap/3.0.0/js/bootstrap.min.js"></script>
سپس فایل‌های x-editable را صدا بزنید.

اولین حالتیکه میتوانید با این کتابخانه کار کنید، استفاده از خاصیت *-data است. نمونه زیر را در نظر بگیرید:
  <a href="#" id="favsite" data-type="text" data-pk="1" data-url="@Url.Action(MVC.Categories.EditCategory())" 
data-title="Enter your favorite site">dotnettips.info</a>
به تعداد هر عنصری که نیاز است، اینکار را انجام دهید و به هر کدام یک id انتساب دهید. بعد از آن کد زیر را در قسمت script بنویسید:
 $(document).ready(function () {
        $('#favsite').editable();
    });
در صورتیکه قصد دارید خصوصیاتی از اشیاء را که برای همه‌ی عنصرهای معرفی شده یکسان است، معرفی کنید می‌توانید از کد زیر بهره ببرید:
 $(document).ready(function () {
        $.fn.editable.defaults.mode = 'inline';
        $('#favsite').editable();
    });
کد بالا حالت ویرایش تمام عناصر معرفی شده را به inline تغییر می‌دهد.

حالت بعدی که می‌توان استفاده کرد به شکل زیر است:
  <a href="#" id="favsite" >dotnettips.info</a>

  $.fn.editable.defaults.mode = 'inline';
    $(document).ready(function () {
        $('#favsite').editable({
            type: 'text',
            pk: 1,
            url: '@Url.Action(MVC.Categories.EditCategory())',
            title: 'Enter your favorite site'
        });
    });

خوبی این روش این است که می‌توان اطلاعات بیشتری چون رویدادها را به آن پاس داد. تا الان با نحوه‌ی انتساب آن به اشیاء آشنا شدیم. اجازه دهید تا با خصوصیات آن آشنا شویم.


 AjaxOptions
 همانطور که متوجه شدید به طور خودکار اطلاعات ویرایش شده، به سمت آدرس داده شده، به شیوه Post ارسال می‌گردند. در صورتیکه قصد دست بردن در نوع درخواست را دارید، می‌توانید از این ویژگی استفاده کنید:
    ajaxOptions: {
        type: 'put',
        dataType: 'json'
    }
 Anim
 این ویژگی که تنها در حالت inline پاسخ می‌دهد، می‌تواند زمان بسته شدن x-editable را تغییر دهد که به طور پیش فرض با false مقداردهی شده است. جهت تغییر زمان بسته شدن، کد زیر را وارد نمایید:
anim:'false'
//or
anim: {
                duration: 2000
            }
 autotext  در انتهای جدول آمده است.
 defaultValue 
 در صورتیکه عنصر مورد نظر محتوایی نداشته باشد و این خصوصیت را مقداردهی کنید، موقع ویرایش، این عبارت تعیین شده نمایش می‌یابد. در مثال بالا باید متن تگ a را حذف کرده تا نتیجه را ببینید: (البته فیلد value نباید مقداری داشته باشد)
defaultValue: 'default val'
//or
defaultValue: undefined
//or
defaultValue: null
در بقیه‌ی حالات، ویرایشگر خالی از متن خواهد بود و مقدار پیش فرض آن نال است.
 disabled  false کردن این ویژگی باعث غیرفعال شدن x-editable بر روی کنترل جاری میگردد.
 display 
خاصیت  display یا مقدار بولین false را دریافت می‌کند، یا نال، یا یک تابع callback را می‌توان به آن پاس داد. این خصوصت زمانی صدا زده می‌شود که اطلاعات به سمت آدرس سرور رفته و با موفقیت بازگشت داده می‌شوند (در صورتی که این ویژگی غیرفعال باشد، بلافاصه بعد از تایید کاربر، از اطلاعات وارد شده صدا زده می‌شود) و سپس متن جدید عنصر تغییر می‌یابد. حال اگر این خاصیت نال که مقدار پیش فرض آن است باشد، متن تغییر می‌یابد. ولی اگر false باشد، متن سابق باقی خواهد ماند و در صورتیکه تابعی به آن پاس داده باشید، طبق تابع شما عمل خواهد کرد.
پارامترهایی که تابع شما می‌تواند داشته باشد به شرح زیر است:
value : مقدار جدید
response : پاسخ سرور ( در صورتی که ارسال از طریق Ajax صورت گرفته باشد)

و در صورتیکه عنصر شما checlklist یا select باشد که حاوی منبعی از مقادیر هست، مقادیرشان در قالب یک آرایه با نام sourceData بازگشت خواهد خورد. برای دسترسی به آیتم‌های انتخابی هم از کد زیر استفاده می‌کنیم:
$.fn.editableutils.itemsByValue(value, sourceData)
 emptyclass  معرفی یک کلاس css برای موقعیکه عنصر خالی است.
 emptytext  در صورتی خالی بودن عنصر، این متن را برای عنصر نمایش بده.
 highlight   بعد از به روز رسانی متن عنصر، آن را با این رنگ highlight خواهد کرد و کد رنگی باید در مبنای هگز باشد. مقدار پیش فرض آن false است.
 mode
 دو حالت نمایشی دارد که پیش فرض آن popup است و با باز کردن یک پنجره، مقدار جدید را دریافت می‌کند. مورد بعدی inline است که به جای باز کردن پنجره، متن عنصر را به حالت ویرایش تغییر میدهد.
 name  نام فیلدی که مقدارش تغییر می‌کند.
 onblur زمانی که کاربر فوکوس را از ویرایشگر  می‌گیرد، ویرایشگر چه پاسخی باید به آن بدهد، باز بماند؟ ignore ، بسته شود؟ cancel و یا مقدار داده شده را تایید کند؟submit
 params
پارامترهای درخواست ایجکسی که کنترل در حالت پیش فرض ارسال می‌کند؛ شامل Pk که آن را با id رکورد پر می‌کنیم. name نام فیلدی که تغییر یافته است و value که مقدار جدید است. در صورتیکه دوست دارید اطلاعات اضافی‌تری نیز ارسال شوند، می‌توانید از این خاصیت استفاده کنید و پارامترها را در قالب Object به آن پاس کنید. ولی اگر بخواهید در کل همه‌ی پارامترها را رونویسی کنید باید یک تابع را به آن پاس کنید:

  params: function(params) {
        //در این حالت پارامترهای پیش فرض ارسال نشده 
و تنها پارامترهای معرفی شده در این تابع ارسال می‌شوند
        params.a = 1;
        return params;
    }

 pk  کلید اصلی رکورد شما در دیتابیس یا هان id است. در صورتی که از کلیدهای ترکیبی استفاده می‌کنید، نگران نباشید فکر آن را هم کرده اند.
//کلید عدد
pk:1,
//کلید رشته ای
pk:'dp123'
//کلید ترکیبی
pk:{id: 1, lang: 'en'}

//معرفی یک تابع به آن و بازگشت 
یکی از مقادیر بالا بعد از محاسبات pk:function() { }
 Placement  این ویژگی فقط به درد حالت Popup می‌خورد که پنجره را کجای عنصر نمایش دهد و شامل چهار مقدار left,right,top,bottom می‌شود.
 saveonchange  زمانی که مقدار جدید، برابر مقدار فعلی باشد و این خاصیت false باشد، هیچ تغییر‌ی رخ نخواهد داد. ولی اگر برابر true باشد ،مقدار جدید اسال و جایگزین مقدار فعلی خواهد شد. مقدار پیش فرض آن false است.
 selector
  با استفاده از این خصوصیت در عنصر انتخابی به دنبال عناصری که در selector تعیین شده می‌گردد و حالت ویرایش را روی آن‌ها فعال می‌کند.
در این حالت استفاده از خصوصیات emptytext و autotext در ابتدای امر ممکن نیست و بعد از اولین کلیک قابل استفاده هستند.
نکته بعدی اینکه شما باید کلاس‌های زیر را دستی اضافه کنید.
کلاس  editable-click برای همه کنترل‌ها وکلاس editable-empty به کنترل‌های بدون مقدار و برای مقداردهی کنترلهای بدون مقدار میتوان از خاصیت ''=data-value استفاده کرد.
    <div id="user">
      <!-- empty -->
      <a href="#" data-name="username" 
data-type="text" data-value="" 
title="Username">Empty</a>
      <!-- non-empty -->
      <a href="#" data-name="group" 
data-type="select" data-source="/groups"
 data-value="1" title="Group">Operator</a>
    </div>     
     
    <script>
    $('#user').editable({
        selector: 'a',
        url: '/post',
        pk: 1
    });
    </script>
 send  سه مقدار auto,always و never را دریافت می‌کند. موقعی که شما آن را روی auto تنظیم کنید؛ در صورتی مقادیر به سمت سرور ارسال می‌شوند که دو خاصیت url و pk تعریف شده باشند. در غیر این صورت ویرایش فقط در حالت محلی و روی سیستم کاربر رخ خواهد داد.
 showbuttons   در صورتیکه با false مقداردهی شود، تایید فرم به طور خودکار انجام می‌گیرد و اگر با یکی از مقادیر left یا Bottom پر شود، دکمه‌ها را در آن قسمت نشان می‌دهد.
 success
 اطلاعات به سمت سرور رفته و با موفقیت با کد 200 بازگشت داده شده‌اند. در مستندات نوشته است، هر کد وضعیتی غیر از 200 بازگشت داده شود، به سمت خاصیت error هدایت می‌شو.د ولی آن طور که من با httpresponsemessage تست کردم، چنین چیزی را مشاهده نکردم و مجددا success صدا زده شد. پس بهتر هست داده‌ای را که به سمت کنترل برگشت می‌دهید، خودتان کنترل کنید. به خصوص اگر انتقال اطلاعات صحیح باشد. ولی اگر در دیتابیس، تغییر با خطا روبرو گردد بهتر است نتیجه‌ی آن ارسال شده و از تغییر مقدار فعلی ممانعت به عمل آورید.
    success: function(response, newValue) {
        if(!response.success) return response.msg;
    }
 toggle  اگر قصد دارید که باز و بسته کردن ویرایشگر را بر عهده‌ی مثلا یک دکمه‌ی روی صفحه بگذارید، این خصوصیت به شما کمک می‌کند:
    $('#edit-button').click(function(e) {
        e.stopPropagation();
        $('#favsite').editable('toggle');
    });
به جای toggle نیز می‌توان از show و hide هم استفاده کرد. وجود عبارت e.stopPropagation جهت باز شدن صحیح ویرایشگر الزامی است.

 type نوع ویرایشی را که قرار است انجام گیرد، مشخص می‌کند. text برای متن، date برای تاریخ، textarea جهت متون طولانی‌تر نسبت به text و بسیاری از موارد دیگر
 unsavedclass  این کلاس موقعی اعمال می‌گردد که اطلاعاتی را ویرایش کرده‌اید، ولی اطلاعاتی به سمت سرور ارسال نشده است. مثلا pk مقداردهی نشده یا send=never قرار داید و یا اینکه به صورت محلی ذخیره می‌کنید و می‌خواهید در آخر همه‌ی اطلاعات را ارسال کنید.
این خاصیت به طور پیش فرض با کلاس editable-unsaved مقداردهی شده که می‌توانید با نال کردن، از شرش خلاص شوید.
 url
این خاصیت با آدرس سمت سرور پر می‌شود. ولی میتوان به آن یک تابع هم پاس کرد که این تابع جایگزین درخواست ایجکسی خودش خواهد شد و برای بازگشت دادن نتیجه‌ی این درخواست به سمت تابع‌های callback خودش می‌توانید یک deferred object را برگشت دهید:

 url: function(params) {
        var d = new $.Deferred;
        if(params.value === 'abc') {
//returning error via deferred object 

 return d.reject('error message'); 
        } else {
            //async saving data in js model
            someModel.asyncSaveMethod({
               ..., 
               success: function(){
                  d.resolve();
               }
            }); 
            return d.promise();
        }
    }

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

در صورتی که از نسخه‌ی 1.5.1 به بعد استفاده می‌کنید، دریافت یک object با مقادیر زیر نیز ممکن شده است:
newValue: مقدار جدید و جایگزین مقدار غیر معتبر.
msg : پیام خطا.
به کدهای زیر در سه حالت نگاه کنید:
validate: function (value) {
                if ($.trim(value) == '') {
//در تمامی نسخه‌های یک پیام متنی باز میگردد
                    return 'This field is required';
//1.5.1
//یک مقدار جدید برگشت میدهد که بلافاصله آن را
// تایید میکند و متن عنصر به روز می‌شود
                    return { newValue: 'validated' };
//متن جدید ار ارسال کرده و پیام هشدار را نشان میدهد 
//ولی تایید نمی‌کند و منتظر تایید کاربر است
                    return { newValue: 'validated',
 msg: 'This field is required' }; } }

 value مقدار پیش فرضی که در ویرایشگر نشان می‌دهد و اگر مقداردهی نشود، از متن عنصر استفاده می‌کند.
 autotext  سه مقدار دارد auto (پیش فرض)،always و never.
موقعیکه عنصر شما متنی نداشته باشد و روی auto تنظیم شده باشد، مقدار value را به عنوان متن عنصر نمایش می‌دهد.
always کاری ندارد که عنصر شما متن دارد یا خالی است؛ مقدار value به آن انتساب داده خواهد شد.
never هیچگاه.

  در قسمت بعدی که قسمت پایانی است مطالب را ادامه می‌دهیم.
مطالب
مایکرو سرویس‌ها - قسمت 1 - معرفی
در نرم افزار‌های Enterprise، توسعه محصول، چالش اصلی تیم نمی‌باشد. اصلی‌ترین چالش، بعد از استقرار نرم افزار و زیر بار رفتن آن به‌وجود می‌آید؛ مسائلی نظیر مدیریت تغییرات و scaling و چنانچه نرم افزار بصورت صحیحی توسعه نیافته باشد، می‌توان گفت که انجام موارد ذکر شده بسیار سخت یا شاید غیر ممکن شوند و باید نرم افزار، بازنویسی شود.
برای روشن شدن موضوع یک مثال میزنم.
فرض کنید یک نرم افزار جامع بیمه (Core Insurance) داریم که بصورت یک نرم افزار یکپارچه (Monolithic) ارائه شده است. بعد از چند سال قرار است در زیر سیستم‌های مختلف تغییراتی انجام شود؛ مثلا زیر سیستم‌های بیمه عمر، بیمه مسافرتی و بیمه درمان، نیاز به تغییر دارند. فرض کنید تغییرات در بیمه درمان سریعتر انجام شده و آماده استفاده برای مشتری می‌باشد؛ اما به دلیل یکپارچه بودن سیستم، این انتشار نسخه باید تا اتمام کار زیر سیستم‌های دیگر، به تعویق بیفتد. یا اینکه به دلیل بالا رفتن تعداد کاربران می‌خواهیم سیستم را  scale out کنیم. برای این منظور باید چند نسخه از کل سیستم را در پروسه‌های مجزایی قرار دهیم.
با توجه به توضیحات بالا متوجه این منظور میشویم که مدیریت تغییرات، بخاطر وابستگی‌های بیش از حد سیستم، با کندی روبه رو می‌شود و همچنین هزینه Scale سیستم با توجه به اینکه باید کل سیستم را  در پروسه‌های مختلفی نصب کرد، بالا می‌باشد.
اگر این سیستم یکپارچه به زیر سیستم‌های مجزایی شکسته می‌شد، هزینه تغییرات و Scale آن به مراتب کمتر می‌شد. حتی از این جلوتر بریم و هر کدام از این زیر سیستم‌ها قابلیت‌های کسب و کار (Business Capabilities) خودشان را به‌صورت سرویس‌های مجزایی ارائه دهند، هزینه تغییرات و نگهداری آن‌ها چگونه خواهد بود؟!
برای مثال اگر زیر سیستم بیمه عمر را تصور و آن‌را به سه قسمت درخواست بیمه نامه ، صدور بیمه نامه و بخش خسارت تقسیم کنیم که هر کدام از این قسمت‌ها به تنهایی قابل ارائه به مشتری باشند.
برای مثال درخواست بیمه نامه شامل پر کردن فرم پیشنهاد، بررسی اطلاعات وارد شده توسط پزشکان بیمه و اعلام نظر کار شناسان بیمه برای افزایش نرخ بیمه نامه بر اساس ریسک‌های پزشکی و شغلی بیمه شده می‌باشد که در نهایت بعد از چند روز، یک فرم پیشنهاد به تایید کارشناسا ن رسیده و تازه به بیمه نامه تبدیل می‌شود. همانطور که می‌بینید این بخش به تنهایی می‌تواند در اختیار نمایندگی‌های شرکت بیمه قرار گرفته و قسمت اولیه فروش بیمه نامه را پشتیبانی کند. حالا اگر نیاز به تغییرات یا scaling سیستم وجود داشته باشد، انجام دادن آن‌ها به مراتب راحت‌تر و کم هزینه‌تر می‌باشد.

مایکرو سرویس چیست ؟

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

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

چرا معماری مایکرو سرویس؟

مایکرو سرویس‌ها به شما قابلیت چابکی بیشتری می‌دهند و شما را قادر میسازند تا به‌صورت بهتری بتوانید یک سیستم بزرگ، پیچیده و در مقیاس بزرگ را نگهداری کنید.
این سرویس‌ها به تنهایی قابلیت scaling دارند و برخلاف یک سیستم یکپارچه که برای scaling باید تمام سیستم را به عنوان یک واحد scale out کرد، در مایکرو سرویس‌ها شما می‌توانید سرویس‌هایی را که بیشتر مورد استفاده قرار می‌گیرند، بصورت کاملا مستقلی scale out کنید و به این صورت چابکی شما در مواجه با تغییرات که از خصوصیات اصلی یک سیستم نرم افزاری می‌باشد، بالا می‌رود. 
با توجه به توضیحات بالا تصویر زیر گویای همه‌ی این موارد هست:

مقایسه سیستم یکپارچه با مایکروسرویسدر مطالب بعدی در موردی مشخصه‌های مایکرو سرویس‌ها صحبت خواهیم کرد.

نظرات مطالب
آشنایی با مفهوم Indexer در C#.NET
ممنون. Indexer مطلب جالبی است؛ ولی یک مورد کلا از زبان سی شارپ از قلم افتاده به نام indexed property
در حالت عادی می‌شود بر روی یک وهله از کلاس، Index اعمال کرد. اگر نخواهیم روی کل وهله اعمال شود چطور؟ برای مثال فقط روی یک خاصیت خاص.
پیاده سازی آن یک نکته کوچک دارد که به شرح زیر است:

using System;

namespace IndexedProperties
{
    public class Data
    {
        private int[] _localArray;
        private ArrayIndexer _arrayIndexer;

        public Data()
        {
            _localArray = new int[10];
            for (int i = 0; i < 10; i++)
                _localArray[i] = i + 1;
            _arrayIndexer = new ArrayIndexer(this);
        }
        
        public ArrayIndexer Number
        {
            get { return _arrayIndexer; }
        }       

        public class ArrayIndexer
        {
            private Data _arrayOwner;

            public ArrayIndexer(Data arrayOwner)
            {
                _arrayOwner = arrayOwner;
            }

            public int this[int index]
            {
                get { return _arrayOwner._localArray[index]; }
            }

            public int Length
            {
                get { return _arrayOwner._localArray.Length; }
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            var data = new Data();
            for (int i = 0; i < 10; i++)
                Console.WriteLine(data.Number[i]);
        }
    }
}
در اینجا از کلاس‌های تودرتو برای پیاده سازی Indexed property استفاده شده است.
به این ترتیب تعاریف آرایه مورد استفاده نیازی نیست مستقیما در معرض دید قرار گیرد و همچنین read-only هم تعریف شده است. به علاوه اینکه indexerها محدود به int نیستند و برای مثال می‌توان از string و غیره نیز استفاده کرد.
 
مطالب
5 قابلیت برتر جدید در ASP.NET Web API 2
ASP.NET Web API 2 بهمراه یک سری قابلیت جدید جالب منتشر شده است. در این پست 5 قابلیت برتر از این قابلیت‌های جدید را بررسی می‌کنیم.


1. Attribute Routing

در کنار سیستم routing فعلی، ASP.NET Web API 2 حالا از Attribute Routing هم پشتیبانی می‌کند. در مورد سیستم routing فعلی، می‌توانیم قالب‌های متعددی برای routing بنویسیم. هنگامی که یک درخواست به سرور میرسد، کنترلر مناسب انتخاب شده و اکشن متد مناسب فراخوانی می‌شود.
در لیست زیر قالب پیش فرض routing در Web API را مشاهده می‌کنید.
Config.Routes.MapHttpRoute(
     name: "DefaultApi",
     routeTemplate: "api/{Controller}/{id}",
     defaults: new { id = RouteParameter.Optional }
);
این رویکرد routing مزایای خود را دارد. از جلمه اینکه تمام مسیرها در یک مکان واحد تعریف می‌شوند، اما تنها برای الگوهایی مشخص. مثلا پشتیبانی از nested routing روی یک کنترلر مشکل می‌شود.
در ASP.NET Web API 2 به سادگی می‌توانیم الگوی URI ذکرد شده را پشتیبانی کنیم. لیست زیر نمونه ای از یک الگوی URI با AttributeRouting را نشان می‌دهد.
URI Pattern --> books/1/authors

[Route("books/{bookId}/authors")]
public IEnumerable<Author> GetAuthorByBook(int bookId) { ..... }

2. CORS - Cross Origin Resource Sharing

بصورت نرمال، مرورگرها اجازه درخواست‌های cross-domain را نمی‌دهند، که بخاطر same-origin policy است. خوب، (CORS (Cross Origin Resource Sharing چیست؟
CORS یک مکانیزم است که به صفحات وب این را اجازه می‌دهد تا یک درخواست آژاکسی (Ajax Request) به دامنه ای دیگر ارسال کنند. دامنه ای به غیر از دامنه ای که صفحه وب را رندر کرده است. CORS با استاندارد‌های W3C سازگار است و حالا ASP.NET Web API در نسخه 2 خود از آن پشتیبانی می‌کند.

3. OWIN (Open Web Interface for .NET) self-hosting

ASP.NET Web API 2 بهمراه یک پکیج عرضه می‌شود، که Microsoft.AspNet.WebApi.OwinSelfHost نام دارد.
طبق گفته وب سایت http://owin.org :
OWIN یک اینترفیس استاندارد بین سرور‌های دات نت و اپلیکیشن‌های وب تعریف می‌کند. هدف این اینترفیس جداسازی (decoupling) سرور و اپلیکیشن است. تشویق به توسعه ماژول‌های ساده برای توسعه اپلیکیشن‌های وب دات نت. و بعنوان یک استاندارد باز (open standard) اکوسیستم نرم افزار‌های متن باز را تحریک کند تا ابزار توسعه اپلیکیشن‌های وب دات نت توسعه یابند.
بنابراین طبق گفته‌های بالا، OWIN گزینه ای ایده آل برای میزبانی اپلیکیشن‌های وب روی پروسس هایی به غیر از پروسس IIS است. پیاده سازی‌های دیگری از OWIN نیز وجود دارند، مانند Giacomo، Kayak,Firefly و غیره. اما Katana گزینه توصیه شده برای سرور‌های مایکروسافت و فریم ورک‌های Web API است.


4. IHttpActionResult

در کنار دو روش موجود فعلی برای ساختن response اکشن متد‌ها در کنترلر ها، ASP.NET Web API 2 حالا از مدل جدیدی هم پشتیبانی می‌کند.
IHttpResponseMessage یک اینترفیس است که بعنوان یک فاکتوری (factory) برای HttpResponseMessage کار می‌کند. این روش بسیار قدرتمند است بدلیل اینکه web api را گسترش می‌دهد. با استفاده از این رویکرد، می‌توانیم response هایی با هر نوع دلخواه بسازیم.
برای اطلاعات بیشتر به how to serve HTML with IHTTPActionResult مراجعه کنید.


5. Web API OData

پروتکل (OData (Open Data Protocol در واقع یک پروتکل وب برای کوئری گرفتن و بروز رسانی داده‌ها است. ASP.NET Web API 2 پشتیبانی از expand, $select$ و value$ را اضافه کرده است. با استفاده از این امکانات، می‌توانیم نحوه معرفی پاسخ سرور را کنترل کنیم، یعنی representation دریافتی از سرور را می‌توانید سفارشی کنید.
  • expand$: بصورت نرمال، هنگام کوئری گرفتن از یک کالکشن OData، پاسخ سرور موجودیت‌های مرتبط (related entities) را شامل نمی‌شود. با استفاده از expand$ می‌توانیم موجودیت‌های مرتبط را بصورت inline در پاسخ سرور دریافت کنیم.
  • select$: از این متد برای انتخاب چند خاصیت بخصوص از پاسخ سرور استفاده می‌شود، بجای آنکه تمام خاصیت‌ها بارگذاری شوند.
  • value$: با این متد مقدار خام (raw) فیلد‌ها را بدست می‌آورید، بجای دریافت آنها در فرمت OData.


چند مقاله خوب دیگر

مطالب
OpenCVSharp #11
خوشه بندی تصویر به کمک الگوریتم K-Means توسط OpenCV

الگوریتم k-Means clustering را می‌توان به کمک یک مثال بهتر بررسی کرد. فرض کنید شرکت منسوجاتی قرار است پیراهن‌های جدیدی را به بازار ارائه کند. بدیهی است برای فروش بیشتر، بهتر است پیراهن‌هایی را با اندازه‌های متفاوتی تولید کرد تا برای عموم مردم مفید باشد. اما ... برای این شرکت مقرون به صرفه نیست تا برای تمام اندازه‌های ممکن، پیراهن تولید کند. بنابراین اندازه‌های اشخاص را در سه گروه کوچک، متوسط و بزرگ تعریف می‌کند. این گروه بندی را می‌توان توسط الگوریتم k-means clustering نیز انجام داد و به کمک آن به سه اندازه‌ی بسیار مناسب رسید تا برای عموم اشخاص مناسب باشد. حتی اگر این سه گروه ناکافی باشند، این الگوریتم می‌تواند تعداد خوشه بندی‌های متغیری را دریافت کند تا بهینه‌ترین پاسخ حاصل شود. [برای مطالعه بیشتر]

ارتباط الگوریتم k-means clustering با مباحث پردازش تصویر، در پیش پردازش‌های لازمی است که جهت سرفصل‌هایی مانند تشخیص اشیاء، آنالیز صحنه، ردیابی و امثال آن ضروری هستند. از الگوریتم خوشه بندی k-means عموما جهت مفهومی به نام Color Quantization یا کاهش تعداد رنگ‌های تصویر استفاده می‌شود. یکی از مهم‌ترین مزایای این کار، کاهش فشار حافظه و همچنین بالا رفتن سرعت پردازش‌های بعدی بر روی تصویر است. همچنین گاهی از اوقات برای چاپ پوسترها نیاز است تعداد رنگ‌های تصویر را کاهش داد که در اینجا نیز می‌توان از این الگوریتم استفاده کرد.


پیاده سازی الگوریتم خوشه بندی K-means

در ادامه کدهای بکارگیری متد kmeans کتابخانه‌ی OpenCV را به کمک OpenCVSharp مشاهده می‌کنید:
var src = new Mat(@"..\..\Images\fruits.jpg", LoadMode.AnyDepth | LoadMode.AnyColor);
Cv2.ImShow("Source", src);
Cv2.WaitKey(1); // do events
 
Cv2.Blur(src, src, new Size(15, 15));
Cv2.ImShow("Blurred Image", src);
Cv2.WaitKey(1); // do events
 
// Converts the MxNx3 image into a Kx3 matrix where K=MxN and
// each row is now a vector in the 3-D space of RGB.
// change to a Mx3 column vector (M is number of pixels in image)
var columnVector = src.Reshape(cn: 3, rows: src.Rows * src.Cols);
 
// convert to floating point, it is a requirement of the k-means method of OpenCV.
var samples = new Mat();
columnVector.ConvertTo(samples, MatType.CV_32FC3);
 
for (var clustersCount = 2; clustersCount <= 8; clustersCount += 2)
{
    var bestLabels = new Mat();
    var centers = new Mat();
    Cv2.Kmeans(
        data: samples,
        k: clustersCount,
        bestLabels: bestLabels,
        criteria:
            new TermCriteria(type: CriteriaType.Epsilon | CriteriaType.Iteration, maxCount: 10, epsilon: 1.0),
        attempts: 3,
        flags: KMeansFlag.PpCenters,
        centers: centers);
 
 
    var clusteredImage = new Mat(src.Rows, src.Cols, src.Type());
    for (var size = 0; size < src.Cols * src.Rows; size++)
    {
        var clusterIndex = bestLabels.At<int>(0, size);
        var newPixel = new Vec3b
        {
            Item0 = (byte)(centers.At<float>(clusterIndex, 0)), // B
            Item1 = (byte)(centers.At<float>(clusterIndex, 1)), // G
            Item2 = (byte)(centers.At<float>(clusterIndex, 2)) // R
        };
        clusteredImage.Set(size / src.Cols, size % src.Cols, newPixel);
    }
 
    Cv2.ImShow(string.Format("Clustered Image [k:{0}]", clustersCount), clusteredImage);
    Cv2.WaitKey(1); // do events
}
 
Cv2.WaitKey();
Cv2.DestroyAllWindows();
با این خروجی


توضیحات

- ابتدا تصویر اصلی برنامه بارگذاری می‌شود و در یک پنجره نمایش داده خواهد شد. در اینجا متد Cv2.WaitKey را با پارامتر یک، مشاهده می‌کنید. این فراخوانی ویژه‌، شبیه به متد do events در برنامه‌های WinForms است. اگر فراخوانی نشود، تمام تصاویر پنجره‌های مختلف برنامه تا زمان پایان پردازش‌های مختلف برنامه، نمایش داده نخواهند شد و تا آن زمان صرفا یک یا چند پنجره‌ی خاکستری رنگ را مشاهده خواهید کرد.
- در ادامه متد Blur بر روی این تصویر فراخوانی شده‌است تا مقداری تصویر را مات کند. هدف از بکارگیری این متد در این مثال، برجسته کردن خوشه بندی گروه‌های رنگی مختلف در تصویر اصلی است.
- سپس متد Reshape بر روی ماتریس تصویر اصلی بارگذاری شده فراخوانی می‌شود.
هدف از بکارگیری الگوریتم k-means، انتساب برچسب‌هایی به هر نقطه‌ی RGB تصویر است. در اینجا هر نقطه به شکل یک بردار در فضای سه بعدی مشاهده می‌شود. سپس سعی خواهد شد تا این MxN بردار، به k قسمت تقسیم شوند.
متد Reshape تصویر اصلی MxNx3 را به یک ماتریس Kx3 تبدیل می‌کند که در آن K=MxN است و اکنون هر ردیف آن برداری است در فضای سه بعدی RGB.
- پس از آن توسط متد ConvertTo، نوع داده‌های این ماتریس جدید به float تبدیل می‌شوند تا در متد kmeans قابل استفاده شوند.
- در ادامه یک حلقه را مشاهده می‌کنید که عملیات کاهش رنگ‌های تصویر و خوشه بندی آن‌ها را 4 بار با مقادیر مختلف clustersCount انجام می‌دهد.
- در متد kmeans، پارامتر data یک ماتریس float است که هر نمونه‌ی آن در یک ردیف قرار گرفته‌است. K بیانگر تعداد خوشه‌ها، جهت تقسیم داده‌ها است.
در اینجا پارامترهای labels و centers خروجی‌های متد هستند. برچسب‌ها بیانگر اندیس‌های هر خوشه به ازای هر نمونه هستند. Centers ماتریس مراکز هر خوشه است و دارای یک ردیف به ازای هر خوشه است.
پارامتر criteria آن مشخص می‌کند که الگوریتم چگونه باید خاتمه یابد که در آن حداکثر تعداد بررسی‌ها و یا دقت مورد نظر مشخص می‌شوند.
پارامتر attempts مشخص می‌کند که این الگوریتم چندبار باید اجرا شود تا بهترین میزان فشردگی و کاهش رنگ حاصل شود.
- پس از پایان عملیات k-means نیاز است تا اطلاعات آن مجددا به شکل ماتریسی هم اندازه‌ی تصویر اصلی برگردانده شود تا بتوان آن‌را نمایش داد. در اینجا بهتر می‌توان نحوه‌ی عملکرد متد k-means را درک کرد. حلقه‌ی تشکیل شده به اندازه‌ی تمام نقاط طول و عرض تصویر اصلی است. به ازای هر نقطه، توسط الگوریتم k-means یک برچسب تشکیل شده (bestLabels) که مشخص می‌کند این نقطه متعلق به کدام خوشه و cluster رنگ‌های کاهش یافته است. سپس بر اساس این اندیس می‌توان رنگ این نقطه را از خروجی centers یافته و در یک تصویر جدید نمایش داد.



کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.
مطالب
React 16x - قسمت 20 - کار با فرم‌ها - بخش 3 - بهبود کیفیت کدهای فرم لاگین
تا اینجا اگر به کدهای کامپوننت فرم لاگینی که ایجاد کردیم دقت کنید، تبدیل شده‌است به محلی برای انباشت حجم قابل توجهی از کد. به این ترتیب اگر قرار باشد فرم‌های جدیدی را تعریف کنیم، نیاز خواهد بود قسمت‌های عمده‌ای از این کدها را در هر جایی تکرار کنیم. بنابراین جهت کاهش مسئولیت‌های آن، نیاز است بازسازی کد (refactoring) قابل ملاحظه‌ای بر روی آن صورت گیرد.


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

قصد داریم قسمت‌هایی از کامپوننت لاگین فعلی را استخراج کرده و آن‌ها را درون یک کامپوننت با قابلیت استفاده‌ی مجدد قرار دهیم:
- خاصیت state: می‌خواهیم تمام فرم‌هایی را که تعریف می‌کنیم، دارای خاصیت errors باشند. بنابراین این خاصیت قابلیت استفاده‌ی مجدد را دارد.
- خاصیت schema: قابلیت استفاده‌ی مجدد را ندارد و مختص فرم لاگین تعریف شده‌است. این منطق از هر فرمی با فرم دیگر، متفاوت است.
- متد validate: در این متد، هیچ نوع وابستگی از آن به مفهوم لاگین وجود ندارد و کاملا قابلیت استفاده‌ی مجدد را دارد. تنها this.state.account آن وابسته‌ی به کامپوننت لاگین است و بدیهی است شیء account را در سایر فرم‌ها نخواهیم داشت و ممکن است نام آن movie یا customer باشد. بنابراین قاعده‌ای را در اینجا تعریف می‌کنیم، بر این مبنا که از این پس، تمام فرم‌های ما دارای خاصیتی به نام data خواهند بود که بیانگر اطلاعات آن فرم می‌باشد. با این تغییر، برای مثال در فرم لاگین، data به شیء account تنظیم می‌شود و در فرمی دیگر به شیء customer.
- متد validateProperty: همانند متد validate است و کاملا قابلیت استفاده‌ی مجدد را دارد.
- متد handleSubmit: قسمت ابتدایی این متد که شامل غیرفعال کردن post back به سرور و اعتبارسنجی فرم است، قابلیت استفاده‌ی مجدد را دارد. اما قسمت دوم آن مانند ارسال فرم به سرور و یا هر عملیات دیگری، از یک فرم به فرم دیگر می‌تواند متفاوت باشد.
 - متد handleChange: این متد نیز قابلیت استفاده‌ی مجدد را دارد؛ چون می‌خواهیم در تمام فرم‌ها در حین تایپ اطلاعات، کار اعتبارسنجی ورودی‌ها صورت گیرد. این متد نیز به this.state.account وابسته‌است که قاعده‌ی تعریف خاصیت data در state، می‌تواند این مشکل را حل کند.
- متد رندر: طراحی آن کاملا وابسته‌است به نوع فرمی که مدنظر می‌باشد؛ اما دکمه‌ی submit آن خیر. بجز برچسب دکمه‌ی submit، مابقی قسمت‌های آن مانند کلاس‌های CSS و منطق فعال‌سازی و غیرفعال‌سازی آن، قابلیت استفاده‌ی مجدد را دارند.

بنابراین در ادامه کار، refactoring کامپوننت فرم لاگین را برای استخراج قسمت‌های با قابلیت استفاده‌ی مجدد آن، انجام خواهیم داد.


تبدیل قسمت‌های با قابلیت استفاده‌ی مجدد کامپوننت لاگین، به یک کامپوننت عمومی

ابتدا کامپوننت عمومی Form را که قابلیت استفاده‌ی مجدد دارد، در فایل جدید src\components\common\form.jsx تعریف کرده و سپس کامپوننت فرم لاگین را طوری تغییر می‌دهیم که از آن، بجای کلاس پیش‌فرض Component، ارث بری کند. به این ترتیب تمام متدهای تعریف شده‌ی در این کامپوننت با قابلیت استفاده‌ی مجدد، در کامپوننت‌های مشتق شده‌ی از آن، در دسترس خواهند بود.

1- در ادامه همانطور که عنوان شد، خاصیت state فرم‌ها باید دارای شیء data و شیء errors باشند تا توسط آن‌ها بتوان اطلاعات کل فرم و اطلاعات خطاهای اعتبارسنجی را ذخیره کرد:
import React, { Component } from "react";

class Form extends Component {
    state = {
        data:{},
        errors:{}
     }
با این تغییر، به فرم login بازگشته و خاصیت account موجود در state آن‌را به data تغییر نام می‌دهیم. برای اینکار بهتر است دکمه‌ی F2 را بر روی نام انتخاب شده‌ی account در VSCode فشار دهید تا تکست باکس تغییر نام آن ظاهر شود. مزیت کار با این ابزار refactoring توکار، اصلاح خودکار تمام ارجاعات به account قبلی، با این نام جدید است. همچنین نام تمام خواصی و متغیرهایی را هم که به account تنظیم کرده بودیم، به data تغییر می‌دهیم تا کار به روز رسانی state بر روی data صورت گیرد و نه account قبلی. در این حالت شاید استفاده از امکانات replace کلی ادیتور، بهتر از استفاده از ویژگی F2 باشد.

2- در ادامه، کاری با خاصیت schema تعریف شده‌ی در کامپوننت لاگین نداریم؛ چون کاملا مختص به آن است. اما متدهای validate و validateProperty آن‌را طور کامل cut کرده و به کامپوننت Form، منتقل می‌کنیم. با این انتقال، چون این متدها از کتابخانه‌ی Joi استفاده می‌کنند، باید import آن‌را نیز به ابتدای ماژول جدید فرم، اضافه کرد:
import Joi from "@hapi/joi";

3- سپس متد رندر کامپوننت Form را کاملا حذف می‌کنیم؛ چون این کامپوننت قرار نیست چیزی را رندر کند.

4- در قسمت دوم متد handleSubmit، برای مثال قرار است ارسال داده‌ها به سرور صورت گیرد. به همین جهت آن‌را تبدیل به متدی مانند doSubmit کرده و سپس کل متد handleSubmit را نیز به کامپوننت Form منتقل می‌کنیم.
  doSubmit = () => {
    // call the server
    console.log("Submitted!");
  };

5- متد handleChange را نیز از کامپوننت فرم لاگین cut کرده و به کامپوننت Form منتقل می‌کنیم.

6- پس از این نقل و انتقالات، کار ارث بری از کامپوننت فرم را در کامپوننت فرم لاگین انجام می‌دهیم:
import Form from "./common/form";
// ...

class LoginForm extends Form {

اکنون اگر برنامه را ذخیره کرده و اجرا کنیم، همانند قبل و آن‌چیزی که در انتهای قسمت قبلی به آن رسیدیم، بدون مشکل کار می‌کند؛ اما کدهای کامپوننت فرم لاگین به شدت کاهش یافته و ساده شده‌است. همچنین اگر دفعه‌ی بعد، نیاز به ایجاد فرمی وجود داشت، دیگر نیازی به تکرار این حجم از کد نیست. تنها نیاز خواهیم داشت تا state را تعریف کرده و schema را اضافه کنیم و همچنین نیاز است متد doSumbit را پیاده سازی کنیم تا مشخص شود پس از تکمیل فرم و اعتبارسنجی آن، قرار است چه رخ‌دادی واقع شود.

کدهای کامل کامپوننت فرم را از پیوست انتهای بحث می‌توانید دریافت کنید؛ البته تمام متدهای آن‌را در قسمت قبل تکمیل کرده بودیم و در اینجا صرفا یکسری cut/paste صورت گرفتند.


ساده کردن و بهبود پیاده سازی متد رندر

1- در متد رندر فعلی کامپوننت فرم لاگین، اگر به دکمه‌ی submit آن دقت کنیم، بجز برچسب آن، مابقی قسمت‌های آن در تمام فرم‌های دیگری که تعریف خواهیم کرد، یکسان خواهند بود. به همین جهت این قسمت را می‌توان تبدیل به یک متد کمکی در کلاس Form کرد:
  renderButton(label) {
    return (
      <button disabled={this.validate()} className="btn btn-primary">
        {label}
      </button>
    );
  }
سپس در متد رندر کامپوننت فرم لاگین، تنها کافی است بجای المان button قبلی، از متد فوق استفاده کنیم:
{this.renderButton("Login")}

2- در قسمت‌های قبل، برچسب، فیلدهای ورودی و تگ‌ها و کلاس‌های بوت استرپی را به کامپوننت Input منتقل کردیم، تا به یک فرم ساده‌تر و با قابلیت نگهداری بالاتری برسیم. هرچند این هدف حاصل شده، اما باز هم تعاریف المان‌های Input قرارگرفته‌ی در متد رندر کامپوننت لاگین، دارای الگوی تکراری ذکر یک خاصیت مشخص، تعریف رویدادگردان‌های مشخص و اطلاعات اعتبارسنجی کاملا مشخصی هستند. به همین جهت تعریف المان Input را هم مانند متد renderButton فوق می‌توان به کلاس پایه Form انتقال داد:
  import Input from "./input";
  //...

  renderInput(name, label) {
    const { data, errors } = this.state;
    return (
      <Input
        name={name}
        label={label}
        value={data[name]}
        onChange={this.handleChange}
        error={errors[name]}
      />
    );
همانطور که مشاهده می‌کنید، با استفاده از [] و دسترسی پویای به خواص اشیاء، می‌توان رندر المان Input را تبدیل به متدی با قابلیت نگهداری بهتر کرد و از تکرار ویژگی‌های name ، label ، value ، onChange و error به ازای هر فیلد مورد نیاز، پرهیز کرد. اکنون با این تغییر، متد رندر کامپوننت فرم لاگین به صورت زیر خلاصه می‌شود که بسیار بهتر است از تعریف تعداد قابل ملاحظه‌ای div و کلاس بوت استرپی، تعریف المان‌ها، اتصال تک تک آن‌ها به خواص تعریف شده، اتصال آن‌ها به رویداد گردان‌ها و همچنین به اعتبارسنج‌ها:
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        {this.renderInput("username", "Username")}
        {this.renderInput("password", "Password")}
        {this.renderButton("Login")}
      </form>
    );
  }

3- تا اینجا فرم لاگین تعریف شده، یک مشکل کوچک را دارد: فیلد پسورد آن، از نوع text تعریف شده و اطلاعات وارد شده را همانند یک textbox معمولی نمایش می‌دهد. برای رفع این مشکل، پارامتر type را با یک مقدار پیش‌فرض پر استفاده، تعریف کرده و به المان Input اعمال می‌کنیم:
  renderInput(name, label, type = "text") {
    const { data, errors } = this.state;
    return (
      <Input
        name={name}
        type={type}
        label={label}
        value={data[name]}
        onChange={this.handleChange}
        error={errors[name]}
      />
    );
  }

سپس این type را در قسمتی که المان مرتبط را رندر می‌کنیم، با password مقدار دهی خواهیم کرد:
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        {this.renderInput("username", "Username")}
        {this.renderInput("password", "Password", "password")}
        {this.renderButton("Login")}
      </form>
    );
}
نیازی به ذکر type، در اولین renderInput ذکر شده، نیست؛ چون مقدار این پارامتر را ازمقدار پیش‌فرض text، دریافت می‌کند.

البته این تغییرات تا به اینجا کار نخواهند کرد؛ چون هنوز کلاس المان Input را جهت پذیرش ویژگی جدید type، ویرایش نکرده‌ایم. بنابراین به فایل src\components\common\input.jsx مراجعه کرده و type را به آن اعمال می‌کنیم:
import React from "react";

const Input = ({ name, type, label, value, error, onChange }) => {
  return (
    <div className="form-group">
      <label htmlFor={name}>{label}</label>
      <input
        value={value}
        onChange={onChange}
        id={name}
        name={name}
        type={type}
        className="form-control"
      />
      {error && <div className="alert alert-danger">{error}</div>}
    </div>
  );
};

export default Input;
اکنون اگر تغییرات را ذخیره کرده و به مرورگر مراجعه کنیم، فیلد کلمه‌ی عبور، دیگر حروف وارد شده را نمایش نمی‌دهد و بر اساس نوع استاندارد password، عمل می‌کند.

4- مشکل! آیا باید به ازای هر ویژگی جدیدی که قرار است به این input اعمال کنیم، مانند type در اینجا، نیاز است یک پارامتر جدید را تعریف و سپس از آن استفاده کرد؟ در این حالت اینترفیس این کامپوننت از کنترل خارج می‌شود و همچنین هربار باید آن‌را ویرایش کرد و تغییر داد. به علاوه اگر به تعریف این input دقت کنیم، نام 4 ویژگی آن، با مقادیری که دریافت می‌کنند،  هم نام هستند (ویژگی value با مقدار value و ...):
<input
  value={value}
  name={name}
  type={type}
  onChange={onChange}
  id={name}
  className="form-control"
/>
در کامپوننت جاری، منهای پارامترهایی که نام ویژگی‌های تعریف شده، با نام آن پارامترها در تمام قسمت‌های کامپوننت (نه فقط المان input)، یکی نیستند (name، label و error)، مابقی را می‌توان توسط یک «rest operator»، به این متد ارسال کرد:
import React from "react";

const Input = ({ name, label, error, ...rest }) => {
  return (
    <div className="form-group">
      <label htmlFor={name}>{label}</label>
      <input {...rest} name={name} id={name} className="form-control" />
      {error && <div className="alert alert-danger">{error}</div>}
    </div>
  );
};

export default Input;
بنابراین منهای name، label و error که در قسمت‌های دیگر کامپوننت استفاده می‌شوند، مابقی پارامترهای این کامپوننت تابعی را حذف کرده و با یک rest operator، دریافت می‌کنیم. سپس آن‌ها را به کمک یک spread operator، در المان input، گسترده و درج می‌کنیم. شبیه به اینکار را در قسمت 15 و بخش «ارسال props سفارشی در حین مسیریابی به کامپوننت‌ها» آن انجام داده بودیم. با کمک عملگرهای rest و spread، به سادگی می‌توان هرنوع ویژگی جدیدی را که برای کار با المان input نیاز داریم، به کامپوننت جاری ارسال کرد؛ بدون اینکه نیازی باشد هربار تعریف پارامترهای آن را تغییر دهیم. پارامتر rest تعریف شده، یعنی هر خاصیت دیگری را بجز سه خاصیت name، label و error، به صورت خودکار به این کامپوننت تابعی ارسال کن.
با این تغییر در کامپوننت Input، سایر قسمت‌های برنامه نیازی به تغییر ندارند. برای مثال در متد renderInput، سه ویژگی name، label و error تبدیل به سه پارامتر دریافتی از props می‌شوند (ترتیب ذکر آن‌ها اهمیتی ندارد). مابقی ویژگی‌های تعریف شده‌ی در آن، به صورت خودکار در قسمت input {...rest} درج خواهند شد.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-20.zip
مطالب
برنامه نویسی پیشرفته JavaScript - قسمت 6 - تغییر صفات Property ها

برنامه نویسی شیء گرا

در این بخش میخواهیم به بررسی یکسری از ویژگی‌ها و نکات ریز برنامه نویسی شیء گرا در جاوا اسکریپت بپردازیم که یک برنامه نویس حرفه‌ای جاوا اسکریپت حتما باید بر آن‌ها واقف باشد تا بتواند کتابخانه‌ها و Framework ‌های موثرتر و بهینه‌تری را ایجاد کند. لازم به ذکر است که در این مجموعه مقالات، پیاده‌سازی اشیاء و شیوه‌ی کد نویسی، بر اساس استاندارد ECMAScript 5 یا ES5 انجام خواهد شد. بنابراین از قابلیتهای جدیدی که در ES6 اضافه شده‌است، صحبت نخواهیم کرد. پس از پایان این مجموعه مقالات و پس از آگاهی کامل از قابلیتهای جاوا اسکریپت، در مجموعه مقالاتی به بررسی قابلیتهای جدید ES6 خواهیم پرداخت که مرتبط به مقالات جاری است.

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

بر اساس تعریفی که از اشیاء در استاندارد ECMAScript صورت گرفته است، هرشیء، شامل مجموعه‌ای از ویژگی‌هاست، که هر یک از آنها می‌تواند حاوی یک مقدار پایه، شیء و یا تابع باشد. به عبارت دیگر هر شیء شامل آرایه‌ای از مقادیر است. هر ویژگی ( Property ) یا تابع (که در برنامه نویسی شیء گرا متد نیز نامیده می‌شود) توسط نام خود شناسایی می‌شوند که به یک مقدار داده‌ای نگاشت یا Map شده‌اند. به همین دلیل میتوان هر شیء را به عنوان یک Hash Table تصور کرد که داده‌ها را به صورت یک زوج کلید مقدار یا key-value pairs نگهداری می‌نماید. در اینصورت نام ویژگی‌ها و متدها به عنوان key و مقدار آنها به عنوان value در نظر گرفته می‌شوند.


مفهوم شیء

همانطور که قبلا اشاره شد، جهت تعریف اشیاء می‌توان از دو روش استفاده نمود. در روش اول، ایجاد شیء با استفاده از شیء Object و در روش دوم، با استفاده از Object Literal Notation انجام خواهد شد. روش دوم جدیدتر و بین برنامه نویسان جاوا اسکریپت محبوب‌تر است. مثال دیگری را جهت یادآوری در این مورد ذکر می‌کنم:

var person = new Object();
person.firstName = "Meysam";
person.birth = new Date(1982, 11, 8);
person.getAge = function () {
    var now = new Date();
    return now.getFullYear() - this.birth.getFullYear();
}

alert(person.firstName + ": " + person.getAge());    // Meysam: 34
در مثال فوق، شیء person شامل دو ویژگی firstName و birth و همچنین تابع getAge() می‌باشد. در تابع getAge() از روی ویژگی birth یا تاریخ تولد، سن شخص محاسبه شده‌است. همانطور که مشاهده می‌کنید، در داخل این تابع، جهت دسترسی به ویژگی birth، از شیء this استفاده نمودیم. this به شیء ای اشاره می‌کند که تابع getAge() به آن تعلق دارد و در اینجا به شیء person اشاره می‌نماید. اگر از this استفاده نکنید، برنامه خطا می‌دهد؛ زیرا قادر به شناسایی birth نمی‌باشد. مثال فوق را میتوان با استفاده از Object Literal Notation به صورت زیر نوشت:
var person = {
    firstName: "Meysam",
    birth: new Date(1982, 11, 8),
    getAge: function () {
        var now = new Date();
        return now.getFullYear() - this.birth.getFullYear();
    }
};

alert(person.firstName + ": " + person.getAge());    // Meysam: 34

انواع Property ها

در ECMAScript 5 ، صفاتی برای Property ‌ها معرفی شده است که از طریق Attribute ‌های داخلی به Property ‌ها اختصاص می‌یابد. این Attribute ‌ها توسط موتور جاوا اسکریپت بر روی Property ‌ها پیاده سازی می‌شوند و به صورت مستقیم قابل دسترسی نمی‌باشند. در طی فرآیند آموزش این مطالب، Attribute ‌های داخلی را در [[]] قرار می‌دهیم، مثل [[Enumarable]] ، تا از سایر دستورات تفکیک شوند. به صورت کلی دو نوع ویژگی داریم که شامل Data Properties و Accessor Properties می‌باشند که به شرح آنها می‌پردازیم.


Data Properties

Data Property ‌ها، 4 صفت یا Attribute را توصیف می‌کنند که عبارتند از:

[[Configurable]]

مشخص می‌کند یک Property اجازه حذف، تعریف مجدد و یا تغییر نوع را دارد یا خیر. بصورت پیش فرض، زمانی که یک شیء بصورت مستقیم ساخته می‌شود، مقدار این ویژگی True می‌باشد.

[[Enumarable]]

مشخص می‌کند که آیا امکان پیمایش یک Property توسط حلقه for-in وجود دارد یا خیر. بصورت پیش فرض، زمانیکه یک شیء بصورت مستقیم ساخته می‌شود، مقدار این ویژگی True می‌باشد.

[[Writable]]

مشخص می‌کند که آیا مقدار یک Property قابل تغییر می‌باشد یا خیر. بصورت پیش فرض، زمانیکه یک شیء بصورت مستقیم ساخته می‌شود، مقدار این ویژگی True می‌باشد.

[[Value]]

شامل مقدار واقعی یک Property و محل مقداردهی یا برگرداندن مقدار Property ‌ها می‌باشد. مقدار پیش فرض آن نیز undefined می‌باشد.


زمانیکه یک Property به صورت عادی به یک شیء اضافه می‌شود، مانند مثال‌های قبلی، سه Attribute اول به true تنظیم می‌شوند و [[Value]]  با مقدار اولیه Property تنظیم میگردد. در این حالت آن Property ، قابل بروزرسانی و پیمایش می‌باشد. جهت تغییر ساختار یک Property و تنظیم Attribute ‌های آن، باید آن Property را با استفاده از متد defineProperty() تعریف نماییم . شکل کلی تعریف Property با استفاده از این متد به صورت زیر می‌باشد:

Object.defineProperty(obj, prop, descriptor)
آرگومان obj ، شیء ای است که Property مورد نظر باید به آن اضافه شود. آرگومان prop نام Property را مشخص می‌کند که Attribute ‌های آن باید تنظیم شوند. آرگومان descriptor  یک شیء می‌باشد که  Attribute ‌های مورد نیاز را برای Property تنظیم می‌نماید. شیء descriptor شامل ویژگی‌های configurable ، enumerable ، writable و value می‌باشد که می‌توانند برای Property تنظیم شوند. خروجی این متد شیء ای است که به عنوان آرگومان اول ارسال شده‌است. به مثال‌های زیر توجه کنید:
var person = {};
Object.defineProperty(person, "name", {
    writable: false,
    value:"Meysam"
});

alert(person.name);   // Meysam
person.name = "Arash";
alert(person.name);   // Meysam
همانطور که در مثال فوق مشاهده می‌کنید، یک Property به نام name به شیء person اضافه شده‌است که صفت writable آن به false تنظیم گردیده‌است. بنابراین امکان تغییر مقدار ویژگی name وجود ندارد و با اینکه در دستور person.name = "Arash" ، ویژگی name را تغییر داده‌ایم، دستور alert نهایی، مجددا خروجی Meysam را نمایش داده‌است.
var person = {};
Object.defineProperty(person, "name", {
    configurable: false,
    value: "Meysam"
});

alert(person.name);  // Meysam
delete person.name;
alert(person.name);  // Meysam
در مثال فوق، صفت configurable را به false تنظیم نموده‌ایم و همانطور که مشاهده میکنید امکان حذف ویژگی name توسط عملگر delete وجود ندارد و دستور alert نهایی مجددا خروجی Meysam را نمایش داده‌است. توجه داشته باشید که اگر شما بخواهید در خطوط بعدی کد، مجددا صفت configurable را به مقدار true تغییر دهید، امکان پذیر نمی‌باشد. زیرا در تعریف فوق، صفت configurable را به false تنظیم نموده‌اید و امکان بروزرسانی Attribute ‌های ویژگی name را از آن گرفته‌اید. در این حالت تنها Attribute ی را که میتوانید تنظیم کنید، صفت writable می‌باشد.

لازم به ذکر است که می‌توانید متد defineProperty() را چندین بار برای یک Property فراخوانی نموده و در هر مرحله صفات متفاوتی را تنظیم و یا صفات قبلی را تغییر دهید.

علاوه بر متد فوق، متد دیگری به نام defineProperties() وجود دارد که می‌توان چند Property را بصورت همزمان تعریف و صفات آن را تنظیم نمود. شکل کلی این متد به صورت زیر است:

Object.defineProperties(obj, props)

آرگومان props یک شیء می‌باشد که ویژگی‌های آن، نام همان Property هایی هستند که باید به obj اضافه شوند. همچنین هر ویژگی خود یک شیء می‌باشد که میتوان صفات آن ویژگی را تنظیم نمود. به مثال زیر توجه کنید:

var person = {};
Object.defineProperties(person, {
    "name": {
        configurable: false,
        value: "Meysam"
    },
    "age": {
        writable:false,
        value:34
    }
});
در مثال فوق، برای آرگومان props ، دو ویژگی name و age را تعریف نمودیم که این دو ویژگی به شیء person اضافه خواهند شد. همچنین ویژگی‌های name و age خود یک شیء می‌باشند که صفات مربوط به آنها تنظیم شده است.

Accessor Properties

این صفات شامل توابع getter و setter می‌باشند که یک یا هر دوی آنها می‌توانند برای یک Property تنظیم شوند. زمانی که مقداری را از یک Property می‌خوانید، تابع getter فراخوانی می‌شود و مقدار Property مربوطه را بر میگرداند. این تابع می‌تواند قبل از برگرداندن مقدار، پردازش هایی را بر روی آن Property انجام داده و یک نتیجه‌ی معتبر را برگرداند. زمانیکه Property را مقداردهی می‌نمایید، تابع setter فراخوانی میشود و Property را با مقدار جدید تنظیم می‌نماید. این تابع می‌تواند قبل از مقداردهی به Property ، داده‌ی مورد نظر را اعتبارسنجی نماید تا از ورود مقادیر نامعتبر جلوگیری کند. Accessor Properties شامل 2 صفت زیر می‌باشد:

[[Get]]

یک تابع می‌باشد و زمانی فراخوانی می‌گردد که مقدار یک Property را بخوانیم و مقدار پیش فرض آن undefined می‌باشد.

[[Set]]

یک تابع می‌باشد و زمانی فراخوانی می‌گردد که یک Property را مقداردهی نماییم و مقدار پیش فرض آن undefined می‌باشد. این تابع شامل یک آرگومان ورودی است که حاوی مقدار ارسالی به Property است.

مثال زیر یک پیاده سازی ساده از شیء تاریخ شمسی می‌باشد که هنوز از لحاظ طراحی دارای نواقصی هست و در ادامه کارآیی و کد آن را بهبود می‌بخشیم.

var date = {
    _year: 1,
    _month: 1,
    _day: 1,
    isLeap: function () {
        switch (this.year % 33) {
            case 1: case 5: case 9: case 13:
            case 17: case 22: case 26: case 30:
                return true;
            default:
                return false;
        }
    }
};

Object.defineProperties(date, {
    "year": {
        "get": function () { return this._year; },
        "set": function (newValue) {
            if (newValue < 1 || newValue > 9999)
                throw new Error("Year must be between 1 and 9999");
            this._year = newValue;
        }
    },
    "month": {
        "get": function () { return this._month; },
        "set": function (newValue) {
            if (newValue < 1 || newValue > 12)
                throw new Error("Month must be between 1 and 12");
            this._month = newValue;
        }
    },
    "day": {
        "get": function () { return this._day; },
        "set": function (newValue) {
            if (newValue < 1 || newValue > 31)
                throw new Error("Day must be between 1 and 31");
            if (this.month === 12 && !this.isLeap() && newValue > 29)
                throw new Error("Day must be between 1 and 29");
            if (this.month > 6 && newValue > 30)
                throw new Error("Day must be between 1 and 30");
            this._day = newValue;
        }
    }
});
در مثال فوق، 3 ویژگی با نامهای _year ، _month و _day تعریف شده‌اند. پیشوند _ مشخص می‌کند که نباید به این ویژگی در خارج از شیء دسترسی داشته باشیم. البته دسترسی را محدود نمی‌کند و برنامه نویس به راحتی می‌تواند به آن دسترسی داشته باشد. در مباحث بعدی شیوه‌ی صحیح پیاده سازی اینگونه Property ‌ها را آموزش می‌دهیم. تابعی به نام isLeap() نیز تعریف شده است که تشخیص می‌دهد سال موجود کبیسه هست یا خیر. با استفاده از تابع defineProperties() ، 3 ویژگی دیگر نیز به شیء date ، با نامهای year ، month و day اضافه نموده‌ایم که دارای Accessor ‌های get و set می‌باشند. در بخش set ورودی‌های کاربران را بررسی و اعتبار سنجی نمودیم. در صورتی که ورودی نامعتبر باشد، با استفاده از throw خطایی را به صورت دستی ایجاد می‌نماییم که در console مربوط به Browser قابل مشاهده و یا با استفاده از try…catch قابل دسترسی و مدیریت می‌باشد.

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

نکته‌ی دیگری که باید به آن توجه کنید این است که اگر یک Property با استفاده از متد defineProperty() تعریف گردد، Attribute هایی که مقداردهی نشده‌اند، مثل [[Configurable]] ، [[Enumarable]] و [[Writable]] با false مقداردهی می‌گردند و [[Value]] ، [[Get]] و [[Set]] مقدار undefined را بر می‌گردانند. در مبحث بعدی، در مورد این نکته مثالی ارائه شده است.


خواندن Attribute ‌های مربوط به یک Property

با استفاده از متد getOwnPropertyDescriptor() می‌توان، Attribute ‌های اختصاص داده شده به Property ‌ها را خواند و از مقدار آنها مطلع شد. این متد شامل 2 آرگومان می‌باشد، که آرگومان اول، شیء ای است که میخواهیم Attribute آن را بخوانیم و آرگومان دوم، نام Attribute می‌باشد. خروجی متد getOwnPropertyDescriptor() یک شیء از نوع PropertyDescriptor می‌باشد که ویژگی‌های آن، همان Attribute هایی هستند که برای یک Property تنظیم شده‌اند. به مثال زیر جهت خواندن Attribute ‌های شیء تاریخ شمسی توجه کنید:

var descriptor = Object.getOwnPropertyDescriptor(date, "_year");
alert(descriptor.value);   // 1
alert(descriptor.configurable); // true
alert(typeof descriptor.get); // undefined

descriptor = Object.getOwnPropertyDescriptor(date, "year");
alert(descriptor.value);   // undefined
alert(descriptor.configurable); // false
alert(typeof descriptor.get); // function
ویژگی _year به صورت عادی تعریف شده است. بنابراین با توجه به نکاتی که قبلا ذکر شد، مقدار اختصاص داده شده به این ویژگی، به صفت [[Value]] تعلق گرفته است. همچنین سایر صفات این ویژگی به مانند [[Configurable]] ، با مقدار true تنظیم شده‌اند. Accessor ‌های getter و setter نیز، که برای این ویژگی تنظیم نشده بودند، مقدار undefined بر می‌گردانند. ویژگی year با استفاده از متد defineProperties() تعریف شده است و چون Accessor ‌های getter و setter به آن اختصاص یافته‌اند، صفت [[Value]]، مقدار undefined را بر می‌گرداند و سایر Attribute ‌ها به مانند [[Configurable]] که تنظیم نشده‌اند، مقدار false را بر می‌گردانند. همچنین برای getter و setter نوع function برگردانده شده‌است. 
نظرات مطالب
ASP.NET MVC #2
در متن در تعریف MVC گفته بودید :
 هدف اصلی آن جدا سازی مسئولیت‌های اجزای تشکیل دهنده «لایه نمایشی» برنامه است.  
یعنی ساختار سه لایه ای که مثلا در پروژه‌های ویندوز فرمی داشتیم مثل domainModel برای تعریف اشیاء و یا Business layer برای کنترل بر روی عملیات منطقی بر روی اشیاء  و یا Data Layer که بمنظور کار با بانک اطلاعاتی بود مثل قبل وجود دارند و MVC  تنها لایه Presentation رو باز مهندسی میکنه؟
و اینکه Model  موجود در MVC مجزا از تعریف لایه DomainModel مرسوم در معماری سه لایه هست؟
مطالب
breeze js به همراه ایجاد سایت آگهی قسمت اول
با قدرت گرفتن جاوا اسکریپت، نیازهایی مانند کوئری گرفتن در سمت کلاینت، کش کردن داده‌ها در سمت کلاینت، ردیابی تغییرات، اعتبارسنجی مدلها، ذخیره کردن گروهی از عملیات‌ها (Save Batch)، تعامل با Web Api .Net یا Node Js، قابلیت کار کردن با No Sql و... افزایش یافته است و تمام این کارها توسط breeze  امکان پذیر میباشد. breeze  با هر سرویس دهنده‌ای که بتواند از  طریق http و با فرمت json عملیات خود را انجام دهد، میتواند ارتباط برقرار کند.
breeze مطابق با استاندارد ECMAScript 5 نوشته شده‌است؛ از این‌رو بر روی اکثر مرورگرهای جدید به خوبی اجرا میشود.

  برای مرورگرهای قدیمی می‌توان کتابخانه es5-shim.js and es5-sham.js را قبل از تگ اسکریپت breeze js قرار داد.
در این قسمت به برخی از  توانایی های  breeze  اشاره کوتاهی میکنیم و در مقالات بعدی پیاده سازی آن‌را در قالب angular js /Web Api .net خواهیم داشت:
1- کوئری‌های breeze اکثر امکانات  linq را دارا می‌باشد؛ مانند شرطهای ساده، شرط‌های پیچیده، Sorting ،Paging،Projections  و Filter کردن برخی از پراپرتی‌ها که بسیار مشابه  linq میباشد:
var query = breeze.EntityQuery
           .from("Customers")
           .where("CompanyName", "startsWith", "A")
           .orderBy("CompanyName");
2- کوئری‌های breeze با promises عملیات خود را انجام میدهند:
var promise = manager.executeQuery(query)
              .then(querySucceeded)
              .fail(queryFailed);
* breeze مانند  entity framework  قابلیت ردیابی تغییرات را دارا میباشد:
if (manager.hasChanges()) {
    manager.saveChanges().then(saveSucceeded).fail(saveFailed);
}
// listen for any change to a customer 
customer.entityAspect.propertyChanged.subscribe(somethingHappened);
3- انقیاد داده‌ها با Angular / Knockout  / Backbone را دارا میباشد:
<!-- Angular template -->
<li data-ng-repeat="emp in employees">
    <label>{{emp.FirstName}}</label>
    <label>{{emp.LastName}}</label>
</li>
// bound to employees from query
manager.executeQuery(breeze.EntityQuery.from("Employees"))
       .then(function(data) { $scope.employees = data.results; });
4- درbreeze امکان  کوئری گرفتن به همراه entity‌های مرتبط وجود دارد:
/* Query with related entities using expand */
// query for orders of customers whose name begins "Alfreds"
// include their customers & child details & their detail products
breeze.EntityQuery.from("Orders")
   .where("Customer.CompanyName", "startsWith", "Alfreds")
   .expand("Customer, OrderDetails.Product")
   .using(manager)
   .execute().then(querySucceeded).fail(queryFailed);
5- در breeze  کوئری‌ها میتوانند از سرور و یا کش درخواست شوند:
// execute query asynchronously on the server
manager.executeQuery(query).then(querySuccess).fail(queryFail);
 
// execute query synchronously on local cache
var customers = manager.executeQueryLocally(query)