مطالب
توسعه برنامه های Cross Platform با Xamarin Forms & Bit Framework - قسمت دوم
در قسمت اول در مورد ابزار انتخابی برای توسعه برنامه‌های Cross Platform صحبت کردیم. در این قسمت به آموزش نصب و راه اندازی محیط توسعه می‌پردازیم.

شما می‌توانید در هر یک از سیستم عامل‌های Mac - Windows - Linux و با هر یک از IDE‌های Visual Studio - Visual Studio for mac - Rider کار کنید. برای این که بتوانیم آموزش را کاملا عملی پیش ببریم و وارد جزئیات شویم، در عمل باید یکی را انتخاب و آموزش دهیم و آن Windows - Visual Studio است؛ اگرچه باقی تفاوت خیلی زیادی ندارند.

با توجه به این که کد نوشته شده برای UI و Logic برای هر سه پلتفرم Windows - Android - iOS یکی است و کدهای منحصر به هر پلتفرم، سهم اندکی از پروژه را تشکیل می‌دهد و همچنین تست برنامه برای Windows آسان‌تر و سریع‌تر بوده و امکانات بیشتری را دارد، توصیه اکید می‌کنم برنامه را روی نسخه Windows توسعه دهید و تست کنید و پس از انجام کارهای اصلی پروژه، آن را بر روی Android و iOS نیز تست کنید. این مورد شباهت به برنامه نویسی وب برای مرورگرها را دارد. خیلی از افراد، سایت را بر روی یک مرورگر مثل Chrome یا Firefox توسعه می‌دهند و در نهایت کار را بر روی مرورگرهای موبایل و IE - Edge - Safari و ... تست می‌کنند. همانطور که می‌شود در مرورگر Chrome هم Touch را تست کرد و هم سایزهای مختلف را، همین کارها را در تست نسخه ویندوزی نیز می‌توانید انجام دهید. در کنار این با توجه به رشد فروش تبلت‌های ویندوزی، برای خیلی از برنامه‌ها، ارائه نسخه ویندوزی می‌تواند مفید نیز باشد.

برای شروع بهتر است نسخه‌ای به روز از ویندوز 10 را داشته باشید، یا Pro یا Enterprise. برای بررسی، ابتدا Command Line را باز کنید و دستور ver (مخفف version) را اجرا کنید. چیزی مشابه مقدار
Microsoft Windows Version 10.0.17134.345
را مشاهده خواهید نمود که باید عدد پنج رقمی آن (در این مثال 17134) از 16299 کمتر نباشد. اگر فرض کنیم که فقط یک سیستم داریم که بدون سیستم عامل است، تا این جا یک ISO نصب ویندوز دانلود کرده‌ایم، به حجم 3.7 گیگ که بعد از نصب، 9.5 گیگ از فضای هارد را می‌گیرد. کمی حوصله به خرج دهید (!) و اگر می‌خواهید همه چیز را تمیز انجام دهید، با یک ویندوز تمیز شروع کنید!

آخرین نسخه پایدار ویژوال استودیو در زمان نگارش این مقاله، 2017 - 15.8.7 هست که ما نیاز به نصب Workload های زیر داریم:
Universal Windows Platform development 
Mobile development with .NET 
نصب این دو احتیاج به 5 گیگ دانلود و 14 گیگ فضای روی هارد را دارد که علاوه بر خود Visual Studio و محیط توسعه آن، موارد زیر را نیز برای شما نصب می‌کند:
 Android SDK - Android NDK - JDK (Java) - Windows SDK - iOS SDK
نکات مهم:
۱- اگر قبلا یکی از SDK‌های ذکر شده را دانلود کرده‌اید، لطفا بی خیال آن شوید! اجازه دهید تا ویژوال استودیو همه چیز را دانلود و نصب و کانفیگ کند. Android SDK، برای مثال، بالغ بر 70 گیگ فایل، برای 28 ورژن اندروید است که اگر یکی از آن‌ها را داشته‌اید که برای تست کد نویسی با Java و Android Studio جواب می‌داده، هیچ دلیلی ندارد دقیقا همان نسخه به درد Xamarin هم بخورد!
۲- ترجیحا نسخه Enterprise را نصب کنید.
۳- قسمت‌های عمده فایل‌های دانلودی از سرورهای مایکروسافت دانلود می‌شوند که محدودیتی برای کاربران ایرانی ندارد، ولی قسمت هایی نیز مستقیما از سرورهای گوگل دانلود می‌شوند که متاسفانه روی کاربرهای ایرانی بسته است. با توجه به این که ممکن است استفاده از روش‌های دور زدن تحریم مانند VPN باعث کندی سرعت اینترنت و دانلود شوند، توصیه می‌کنم که ابتدا "Universal Windows Platform development" را نصب کنید (زیرا تماما از سرورهای مایکروسافت دانلود می‌شود) و سپس مجدد Installer را باز کرده و "Mobile development with .NET" را انتخاب کنید و این بار از ابزارهای دور زدن تحریم استفاده کنید.
۴- در سمت راست گزینه‌های قابل نصب، تیک موارد "Google Android Emulator API Level 27" و "Intel Hardware Accelerated Execution Manager (HAXM) global install" را بردارید. در پستی جداگانه آپشن‌های متنوع Emulator‌های اندرویدی را بررسی خواهیم نمود.
۵- بهتر است Administrator سیستم خود باشید.

بعد از اتمام نصب باید Developer mode را فعال کنید که نحوه انجام آن در این لینک شرح داده شده است. به صورت خلاصه به Settings بروید، سپس Update & Security، سپس For developers و در نهایت انتخاب Developer mode از بین گزینه‌های موجود.
ضمن استفاده از ابزارهای دور زدن تحریم (فقط برای ساختن و بیلد کردن اولین پروژه)، ویژوال استودیو را به صورت Run as admin باز کنید و از منوی File > New > Project قسمت Cross-Platform برای CSharp، یک Mobile app Xamarin Forms بسازید که ضمن انگلیسی بودن نام پروژه و فاقد Space بودن آن، ترجیحا در فولدری باشد که مسیر آن فولدر نیز طولانی نباشد، Space و کارکترهای فارسی نیز نداشته باشد.

تنظیماتی که در پنجره New Cross Platform App هستند مناسب بوده و Ok را بزنید! اولین بیلد به علت نیاز به دانلود طول می‌کشد و در صورت بیلد شدن موفقیت آمیز پروژه شما دومین قسمت را با موفقیت طی کرده اید. در قسمت بعدی ساختار پروژه‌های Xamarin Forms را بررسی می‌کنیم و یک مثال ساده می‌نویسیم که لااقل روی ویندوز قابلیت تست را داشته باشد. دقت کنید که همان کد روی Android / iOS نیز کار می‌کند، ولی در پست هایی جداگانه باید در مورد راه اندازی Emulator‌های Android و iOS آموزش هایی را ببینید. در صورت وجود هر گونه مشکل یا سوال نیز در قسمت نظرات همین صفحه در خدمت شما عزیزان هستیم. 
مطالب
استراتژی‌های کش کردن اطلاعات - قسمت 1
یکی از مواردی که می‌تواند بهبود سرعت یک نرم‌افزار را تضمین کند، caching می‌باشد. cache یکی از مواردی است که شاید در توسعه یک نرم افزار خیلی کمتر برای آن وقت میگذاریم و زیاد مورد توجه قرار نمی‌گیرد.  لازم به ذکر است که داشتن یک سیستم cache برای نرم افزارهای در مقایس بزرگ، نیازی ضروری به حساب می‌آید تا بتوان با سرعتی بهتر و قابل قبول‌تر (به نسبت سیستم بدون cache) به درخواست‌های کاربران جواب داد و یک حس خوب از نرم افزار را به کاربران منتقل کرد.
قبل از شروع به پیاده سازی یک سیستم caching، ابتدا نیاز است با روش‌های مختلف آن آشنا شویم و سپس اقدام به پیاده سازی و یا استفاده از یک سیستم cache کنیم. بدون شک انتخاب روشی مناسب، تاثیر چشم گیری را بر روی نتیجه‌ی خروجی خواهد داشت.
 
سوال: چرا باید از cache استفاده کنیم؟
پایین آوردن زمان پاسخ  (Response Time) به درخواست‌های ارسالی کاربران و پایین آوردن بار ترافیکی بر روی دیتابیس.
استراتژی‌های مختلفی که در ادامه در مورد آنها صحبت خواهیم کرد، وابستگی به نوع data و چگونگی دسترسی به آنها دارد؛ برای مثال اطلاعات چگونه ذخیره و خوانده می‌شوند که میتوان به چند مثال زیر اشاره کرد :
- آیا نرم افزار مورد نظر، اطلاعات زیادی را در دیتابیس ذخیره میکند و به نسبت آن، کمتر واکشی (read) اطلاعات را داریم؟ (مانند ثبت وقایع )
- آیا اطلاعات، یک بار نوشته خواهند شد و به کرّات واکشی می‌شوند؟ (مانند پرفایل کاربران یا اطلاعات یک کالا در یک فروشگاه اینترنتی)

Cache-Aside 


می‌توان این روش را یکی از متداول‌ترین و یا آشنا‌ترین روش‌های caching دانست و شاید حداقل یک بار، کارکردن با آن را تجربه کرده‌ایم.
در این رویکرد، برنامه به صورت مستقیم هم با دیتابیس اصلی کار میکند و هم cache.



نحوه‌ی کار به این صورت می‌باشد که:

1- برنامه ابتدا cache را بررسی می‌کند میکند و اگر اطلاعات مورد نظر در cache یافت شود، اطلاعات به کاربر برگشت داده می‌شوند.

2- اگر اطلاعات مورد نظر در cache یافت نشود، برنامه همان درخواست را به دیتابیس می‌فرستد و اطلاعات را به کاربر برمیگرداند؛ همچنین موظف است اطلاعات دریافتی از دیتابیس را در cache ذخیره کند تا در دفعات بعدی آن‌را از cache، واکشی کند.

مزایا و معایب
-  اگر cache به هر دلیلی از کار بیفتد، سیستم می‌تواند به کار خود ادامه دهد.
- اگر در نرم افزار شما درخواست‌های خواندن اطلاعات، بیشتر است و اطلاعات حالت استاتیک (به ندرت تغییر میکنند) را دارند، این مدل می‌تواند راه حل خوبی باشد. موارد پیشنهادی برای این حالت، Redis و Memcached هستند.
- یکی از معایبی که به این روش گرفته می‌شود، تا حدودی افزایش پیچیدگی کار برنامه می‌باشد؛ از این نظر که موظف است با دیتابیس و cache کار کند.

در این حالت بعد از به روزرسانی اطلاعات در دیتابیس، برای جلوگیری از ناسازگاری داده‌ها (inconsistent)  دو رویکرد برای همان اطلاعات در cache موجود است: 
1- برای رفع این مشکل از TTL  (Time to Live) استفاده می‌شود که بعد از مدت زمانی مشخص، اطلاعات در cache به صورت خودکار پاک خواهند شد. لازم به ذکر است باید در مقدار تعیین شده‌ی برای TTL، حداکثر دقت را انجام داد. تنظیم آن با مقدار کم می‌تواند باعث بالابردن درخواست‌های به دیتابیس شود.
2- بعد از به روزرسانی اطلاعات در دیتابیس، با استفاده از key مربوط به آن رکورد، اطلاعات موجود در cache، به حالت نامعتبر تبدیل شوند.
نمونه‌ای از کارکرد این روش می‌تواند شبیه به کد زیر باشد:
public  object GetMyEntity(int key)
{    
  // Try to get the entity from the cache.
  var value =  cache.StringGet(key);
  
  if (value == null) // Cache miss
  {
    // If there's a cache miss, get the entity from the original store and cache it.
    value = db.StringGet(key);

    // Avoid caching a null value.
    if (value != null)
    {
      // Put the item in the cache with a custom expiration time that 
       cache.StringSetAsync(key, JsonConvert.SerializeObject(value));
    }
  }
 return value;
}

Read-Through Cache 


در این حالت دیتابیس و cache به صورت پشت سر هم (in-line) قرار دارند و نحوه کار به این صورت است که همیشه درخواست‌ها در ابتدا به cache ارسال می‌شوند. تنها تفاوت این روش با روش قبل این است که برنامه‌ی ما همیشه با cache صحبت میکند.



مزایا و معایب
- یکی از مزایای این روش، کاهش پیچیدگی برنامه است؛ به این صورت که برنامه همیشه فقط با سیستم cache در ارتباط است.
- یکی از معایب این روش، اولین درخواست است. همیشه برای اولین درخواست‌ها چون اطلاعاتی در cache موجود نیست، باعث یک افزایش زمان پاسخ خواهد شد. برای رفع این مشکل از اصطلاحی تحت عنوان گرم کردن (warming) استفاده می‌شود. در اینجا برنامه نویس به صورت دستی درخواست‌هایی و یا کوئری‌هایی را اجرا خواهد کرد، صرفا به این دلیل که اطلاعات در cache قرار گیرند.

Write-Through Cache 

در این حالت اطلاعات ابتدا در cache ذخیره خواهند شد و بعد از آن در دیتابیس قرار خواهند گرفت. همچنین همانند روش Read-Through، برنامه همیشه با cache صحبت میکند.


این روش همه‌ی مزایای روش Read-Through را دارد به علاوه رفع معایب آن از جمله:
- ناسازگاری داده‌ها نمی‌توانند اتفاق بیفتند؛ زیرا اطلاعات همیشه ابتدا در cache نوشته خواهند شد و بعد در دیتابیس؛ به همین خاطر اطلاعات در هر دو نسخه یکسان هستند.
- بعد از درج اطلاعات جدید، نیازی به warming نیست. به این دلیل که در ابتدا در cache ذخیره خواهند شد.
مطالب
Vue Lifecycle hooks
هر وهله از Vue از یک‌سری مراحل یا (initialization steps) عبور خواهد کرد به عنوان مثال مراحلی از قبیل کامپایل شدن تمپلیت، mount شدن وهله به DOM و یا بروزرسانی DOM زمانیکه داده‌ها تغییر پیدا می‌کنند و ... در حین طی کردن این مسیر یکسری توابع ویژه با نام lifecycle hooks فراخوانی خواهند شد. بنابراین درون این توابع می‌توانیم در هر مرحله کدهای موردنیازمان را قرار دهیم:

همانطور که مشاهده می‌کنید این چرخه حیات با وهله‌سازی شیء Vue شروع می‌شود. در این مرحله اولین تابع یعنی beforeCreate فراخوانی می‌شود. در این مرحله کار مقداردهی اولیه (initialization) ایونت‌های پاس داده شده به Vue instance انجام خواهد گرفت. در مرحله بعد تابع created فراخوانی و در ادامه تمپلیت (اگر تعیین شده باشد) کامپایل و بعد از آن تابع beforeMount فراخوانی خواهد شد. این تابع دقیقاً قبل از اینکه تمپلیت به DOM اضافه شود فراخوانی می‌شود. در این‌حالت المنت تعیین شده در قسمت el با محتوای تمپلیت مقداردهی می‌شود. البته تا اینجا هنوز خروجی به DOM اضافه نشده است؛ تنها اعمال بایندینگ، string interpolation بر روی تمپلیت صورت خواهند گرفت تا خروجی به صورت یک HTML آماده تحویل به مرحله بعد گردد. در ادامه خروجی تهیه شده به DOM اضافه (mount) خواهد شد. در این مرحله اگر دیتایی تغییر کند، تابع beforeUpdate فراخوانی خواهد شد. بعد از اینکه تغییری توسط Vue مشاهده شد، تابع updated فراخوانی خواهد شد. در نهایت توابع beforeDestroyed و destroyed را داریم. درون این توابع فرصت آزادسازی منابع استفاده شده را خواهیم داشت. 
به عنوان مثال بعد از اینکه یک وهله از Vue ایجاد شد، تابعی با نام created فراخوانی خواهد شد: 
new Vue({
    el: '#app',
    data() {
        return {
            a: 1
        };
    },
    created: function () {
        // `this` points to the vm instance
        console.log('a is: ' + this.a)
    }
});

در حالت کلی می‌توانیم hookها را به چهار دسته‌بندی زیر تقسیم کنیم:
(Creation (Initialization
این نوع hook در واقع اولین توابعی هستند که درون یک کامپوننت فراخوانی خواهند شد. در اینجا می‌توانیم قبل از اینکه کامپوننت به DOM اضافه شود، اکشن مورد نیازمان را قرار دهیم. باید دقت داشته باشید که درون این توابع، به target element (همان المنت‌ی که وهله‌ی Vue به آن متصل خواهد شد) و همچنین DOM دسترسی ندارید و مقادیر آن‌ها در این فاز، undefined خواهند بود: 
new Vue({
    el: '#app',
    data() {
        return {
            name: 'Sirwan'
        };
    },
    beforeCreate: function () {
        console.log('name is: ' + this.name);
        console.log(`We don't have access to target element at this point: ${this.$el}`);
    },
    created: function () {
        // `this` points to the vm instance
        console.log('name is: ' + this.name);
        console.log(`We don't have access to target element at this point: ${this.$el}`);
    }
});

(Mounting (DOM Insertion
در این مرحله می‌توانیم بلافاصله قبل و بعد از اولین رندر، به کامپوننت دسترسی داشته باشیم. می‌توانیم از این hookها برای تغییر کامپوننت، به محض رندر شدن استفاده کنیم. بنابراین در این فاز به target element نیز دسترسی خواهیم داشت: 
beforeMount: function () {
    console.log(`this.$el doesn't exist yet, but it will soon!`);
},
mounted: function () {
    console.log(this.$el);
}

(Updating (Diff & Re-render
این توابع هنگامیکه داده‌های تعریف شده در قسمت data تغییر پیدا کنند، فراخوانی خواهند شد. یعنی دقیقاً قبل از اینکه DOM به‌روزرسانی شود، فراخوانی خواهند شد. در مثال زیر به محض ایجاد شدن وهله‌ی Vue، یک تایمر ایجاد شده است. این تایمر هر ثانیه یکبار مقدار دیتای counter را یک واحد افزایش خواهد داد. در حالت عادی اگر دیتایی درون DOM استفاده نشده باشد، درون کنسول خروجی را دریافت نخواهید کرد. در نتیجه این توابع تنها در صورتیکه از دیتا درون DOM استفاده شده باشد، فراخوانی خواهند شد: 
new Vue({
    el: '#app',
    data() {
        return {
            name: 'Sirwan',
            counter: 0
        };
    },
    created: function () {
        setInterval(() => {
            this.counter++
        }, 1000)
    },
    beforeUpdate: function () {
        console.log(this.counter); // Logs the counter value every second, before the DOM updates.
    },
    updated: function () {
        console.log(`new value: ${this.counter}`);
    }
});

(Destruction (Teardown
این توابع، قبل و بعد از تخریب شدن (حذف شدن از DOM) کامپوننت فراخوانی خواهند شد:
new Vue({
    el: '#app',
    data() {
        return {
            name: 'Sirwan',
            counter: 0
        };
    },
    beforeDestroy: function () {
        
    },
    destroyed() {
        console.log(this) // There's practically nothing here!
    }
});
 
 
Vue.js چگونه تغییرات داده‌ها را متوجه خواهد شد؟
شاید تصور کنید که Vue.js به صورت مداوم تغییرات را مشاهده کرده و در صورت وجود تغییری بر روی دیتا، این تغییرات را مستقیماً به DOM اعمال خواهد کرد. هر پراپرتی دارای یک watcher است؛ یعنی وقتی یک شیء را به سازنده Vue ارسال می‌کنیم، یک watcher برای تمامی پراپرتی‌های تعریف شده، درون پراپرتی data ایجاد خواهد کرد. این watcher در واقع مسئول کنترل تغییرات پراپرتی‌ها می‌باشد و اگر تغییری صورت بگیرد، آن را به DOM اعمال خواهد کرد. اما نکته اینجاست که این تغییرات بلافاصه بر روی DOM اعمال نخواهند شد؛ زیرا از لحاظ کارآیی، اینکار بهینه نیست. فرض کنید یک پراپرتی با نام message درون قسمت data داریم که مقدار آن Hello است. اکنون همین مقدار توسط UI مجدداً به Hello ست خواهد شد. در این‌حالت اعمال تغییرات نباید به DOM منعکس شود، زیرا مقدار message به همان مقدار قبلی تنظیم شده است. در عوض Vue.js از مفهومی با نام Virtual DOM استفاده می‌کند که در واقع یک کپی از DOM اصلی است؛ با این تفاوت که دسترسی به آن خیلی سریعتر از DOM اصلی می‌باشد. بنابراین Vue.js تغییرات صورت گرفته را مستقیماً به Virtual DOM اعمال می‌کند. در نهایت آن را با DOM اصلی مقایسه خواهد کرد و در صورت وجود تغییر، آن را با DOM اصلی ادغام خواهد کرد. 
مطالب
مقدمه‌ای بر داکر، قسمت سوم
در قسمت قبلی با Volume آشنا شدیم و نحوه‌ی اجرا کردن یک Source Code را درون Container یاد گرفتیم. در این قسمت میخواهیم یک Image شخصی ساخته، آن‌را اجرا و درون Docker hub ارسال نماییم.


Dockerfile چیست؟
Dockerfile عملا چیزی بیشتر از یک دستور العمل از نوع متنی برای build و ساخت یک docker image از آن نمیباشد. ضمن اینکه مراحل build شدن، cache شده و build‌های بعدی با سرعت خیلی بیشتری اجرا خواهند شد. بعد از نوشتن چند dockerfile متوجه خواهیم شد که مراحلش بسیار ساده است.
ساخت اولین Dockerfile
قبل از ساخت dockerfile، مثل جلسه‌ی قبل یک پروژه‌ی ساده‌ی nodejsی را با فایل index.js میسازیم:
const express = require('express')
const app = express()
const PORT = 3000;
app.get('/', (req, res) => {
  res.send('Hello World')
})
app.listen(PORT, () => {
  console.log(`listening on port ${PORT}!`)
})
درون  package.json هم این قسمت را تغییر میدهیم:
"scripts": {
    "start": "node index"
  },
حال فایل Dockerfile را ساخته و دستورالعمل‌های زیر را درون آن مینویسیم:
FROM node
ENV NODE_ENV=production
COPY . /var/www
WORKDIR /var/www
RUN npm i
EXPOSE 3000
ENTRYPOINT npm start
توضیحات دستورات فوق
1) FROM node یک imageی است که برنامه‌ی شما از آن استفاده میکند.
2) از environment variable استفاده کرده و نوع آن را روی production میگذاریم.
3) COPY کردن تمام فایل‌های دایرکتوری جاری پروژه درون فایل سیستم container به آدرس فوق.
4) عوض کردن work directory روی آدرسی که پروژه کپی شده است.
5) اجرا کردن دستور npm i برای نصب شدن Dependencies‌های پروژه.
6) EXPOSE کردن یک port برای ایجاد دسترسی.
7) نقطه‌ی شروع برنامه و دستور npm start که درون package.json قبل تعریف نموده بودیم.
 
بعد از ساخت Dockerfile فوق نوبت به build گرفتن از آن میرسد که با استفاده از دستور زیر میباشد:
docker build -f Dockerfile -t alikhll/testnode1 .
نکته: اگر image node را روی سیستم خود نداشته باشید ابتدا بصورت خودکار آن را pull مینماید.
1) پرچم f- که برای شناساندن فایل Dockefile میباشد، بدلیل این است که نام این فایل قابل تغییر میباشد.
2) پرچم t- برای نام image ساخته شده میباشد. همچنین . نیز به دایرکتوری جاری اشاره میکند.
بعد از ساخته شدن image با استفاده از دستور docker images میتوانید آن را مشاهده نمایید.
برای اجرای image نیز از دستور زیر استفاده میکنیم:
docker run -d -p 8080:3000 alikhll/testnode1
حال با استفاده از port 8080 میتوانید اپلیکیشن را اجرا نمایید.
از آنجایی که اکثر خوانندگان این مجموعه برنامه نویسان دات نت هستند یک Dockerfile دات نتی نیز برای تسلط بیشتر مینویسیم.
ابتدا دستورات زیر را درون ترمینال خود وارد کرده و یک پروژه‌ی وب از نوع Net Core. را میسازیم:
dotnet new web
dotnet restore
dotnet run
حال روی localhost قابلیت اجرا خواهد داشت؛ اما میخواهیم این app را بر روی container اجرا کنیم. بنابراین Dockerfile را اینگونه مینویسیم:
FROM microsoft/dotnet
ENV ASPNETCORE_URLS http://*:3000
COPY . /var/www
WORKDIR /var/www
RUN dotnet restore
EXPOSE 3000
ENTRYPOINT dotnet run
همه چیز خیلی شبیه به داکرفایل قبلی است، با این تفاوت که از ایمیج microsoft/dotnet استفاده کرده‌ایم (از imageهای سبکتری برای محیط production استفاده میکنیم! ضمن اینکه image فوق از Debian استفاده میکند. image جدیدی روی توزیع Alpine ایجاد شده است که حجم خیلی پایینی داشته و برای مطالعه بیشتر به اینجا رجوع کنید).
نکته‌ی مهم ASPNETCORE_URLS میباشد چون میخواهیم بتوانیم از خارج از محیط container با استفاده از IP، به آن دسترسی داشته باشیم (محیط local نیست).
 دستورات زیر را برای build و اجرا وارد مینماییم:
docker build -f Dockerfile -t alikhll/testasp1 .
docker run -d -p 8080:3000 alikhll/testasp1
اکنون app شما باید روی پورت خارجی 8080 قابل اجرا باشد.
نکته: من container قبلی nodejsی را stop کرده بودم وگرنه این پورت قابل استفاده‌ی مجدد نبود!
پابلیش کردن روی Docker Hub
انتشار دادن روی Docker hub ّبسیار ساده است. میتوانید یک اکانت به صورت رایگان ساخته و image‌های خود را بر روی آن انتشار دهید.
نکته: پروژه‌های تستی خود را میتوانید آنجا انتشار داده که البته قابلیت private بودن را ندارند. در صورتیکه برای یک پروژه‌ی واقعی که image‌های عمومی نیستند و فقط درون سازمان باید به آن دسترسی داشته باشند، میتوانید اکانت enterprise تهیه کرده و image‌های خود را درون آن مدیریت نمایید. همچنین از سرویس‌های ابری Azure, Amazon نیز برای انتشار دادن imageهای خصوصی میتوان استفاده نمود.
دستور زیر برای انتشار دادن imageی که ساختیم روی docker hub میباشد. ابتدا login کرده user/password را وارد کرده سپس push مینماییم:
docker login
docker push alikhll/testnode1
نکته: به جای alikhll باید username شخصی خود را وارد نمایید.
اکنون به راحتی با استفاده از دستور زیر روی یک ماشین دیگر که داکر روی آن نصب شده است، میتوانید image را pull کرده و اجرا نمایید:
docker pull alikhll/testnode1
مطالب
بررسی Bad code smell ها: الگوی Shotgun Surgery
برای مشاهده طبقه بندی Bad code smell‌ها می‌توانید به اینجا مراجعه کنید.
زمانیکه به ازای هر تغییر، نیاز باشد تغییرات کوچکی در تعداد کلاس‌های زیادی انجام شود، این بوی بد کد بوجود آمده است. این الگو از دسته بندی «جلوگیری کنندگان از تغییر» است. نام این دسته بندی به طور واضح گویای مشکلی است که این الگوی بد ایجاد می‌کند. 

چرا چنین بویی به راه می‌افتد؟ 

یکی از نشانه‌های وجود چنین الگوی بدی در کدها، مشاهده کدهای تکراریست. ریشه اصلی این بوی بد، پراکنده کردن مسئولیت‌ها در کلاس‌های مختلف است. مسئولیت‌هایی که بهتر بود در یک کلاس جمع شوند. معمولا برای رفع این بوی بد اقدام به جمع کردن مسئولیت‌ها از نقاط مختلف به یک کلاس می‌کنند.  
با توجه به توضیحات ارائه شده، این بوی بد عملا یکی از علایم اجرایی نکردن اصل Single responsibility  و Open closed از اصول طراحی شیء گرایی است. موارد دیگری که در ایجاد چنین مشکلی کمک می‌کنند به صورت زیر هستند:
  • استفاده نادرست از الگوهای طراحی شیء گرا 
  • عدم درک درست مسئولیت‌های کلاس‌های ایجاد شده 
  • عدم تشخیص مکانیزم‌های مشترک در کد و جداسازی مناسب آنها 
برای بررسی بیشتر این موضوع فرض کنید کلاس‌هایی در نرم افزار خود دارید که شماره تلفن کاربر را به صورت ورودی دریافت و روی آن کار خاصی را انجام می‌دهند. در ابتدای تولید نرم افزار فرمت صحیح شماره تلفن به صورت "04135419999" تشخیص داده شده است و مکانیزم اعتبارسنجی آن نیز با استفاده regular express‌ionها پیاده سازی شده‌است.  بعدا نیازمندی دیگری بوجود می‌آید که شماره تلفن‌هایی با کد بین المللی نیز در نرم افزار قابل استفاده باشند. مانند "984135410000+" دو نوع پیاده سازی (از میان روش‌های فراوان پیاده سازی) برای تشریح این موضوع می‌توان متصور بود. فرض کنید در دو موجودیت «کاربر» و «آدرس» نیاز به ذخیره سازی شماره تلفن وجود دارد. 

اول: هر جائیکه نیاز به اعتبارسنجی شماره تلفن وجود داشته باشد؛ این کار تماما در همان مکان انجام شود.
public class UserService 
{ 
        public void SaveUser(dynamic userEntity) { 
            var regEx = "blablabla"; 
            var phoneIsValid = Regex.IsMatch(userEntity.PhoneNumber, regEx); 
            if (!phoneIsValid) 
                return; 
            // ... 
        } 
}  

public class AddressService 
{ 
        public void SaveAddress(dynamic addressEntity) 
        { 
            var regEx = "blablabla"; 
            var phoneIsValid = Regex.IsMatch(addressEntity.PhoneNumber, regEx); 
            if (!phoneIsValid) 
                return; 
        } 
}
در این روش پیاده سازی اگر دقت کرده باشید روال مربوط به اعتبارسنجی در دو متد «ذخیره کاربر» و «ذخیره آدرس» تکرار شده‌است . این الگوی کد نویسی، علاوه بر این که خود نوعی بوی بد کد محسوب می‌شود، باعث ایجاد الگوی Shotgun surgery نیز است.  
در اینجا اگر قصد اعمال تغییری در منطق مربوط به اعتبارسنجی شماره تلفن وجود داشته باشد، نیاز خواهد بود تمامی مکان‌هایی که این منطق پیاده سازی شده‌است، بسته به شرایط جدید تغییر کند. یعنی برای تغییر یک منطق اعتبارسنجی نیاز خواهد بود کلاس‌های زیادی تغییر کنند.  

دوم: راه بهتر در انجام چنین کاری، جداسازی منطق مربوط به اعتبارسنجی شماره تلفن و انتقال آن به کلاسی جداگانه‌است؛ به صورت زیر: 
public class PhoneValidator
{ 
        public bool IsValid(string phoneNumber) 
        { 
            var regEx = "blablabla"; 
            var phoneIsValid = Regex.IsMatch(phoneNumber, regEx); 
            if (!phoneIsValid) 
                return false; 
            return true; 
        } 
 } 
 
public class UserService 
{ 
        public void SaveUser(dynamic userEntity) 
        { 
            var validator = new PhoneValidator(); 
            var phoneIsValid  = validator.IsValid(userEntity.PhoneNumber); 
            if (!phoneIsValid) 
                return; 
            // ... 
        } 
 } 
 
public class AddressService 
{ 
        public void SaveAddress(dynamic addressEntity) 
        { 
            var validator = new PhoneValidator(); 
            var phoneIsValid = validator.IsValid(addressEntity.PhoneNumber); 
            if (!phoneIsValid) 
                return; 
           // ... 
        } 
}

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

شکل دیگر 

شکل دیگر این بوی بد کد، Divergent Change است. با این تفاوت که در الگوی Divergent Change تغییرات در یک کلاس اتفاق می‌افتند نه در چندین کلاس به طور همزمان. 

جمع بندی

تشخیص چنین الگوی بد کد نویسی ای همیشه به این سادگی نیست. یکی از راه‌های تشخیص سریع چنین بوی بد کدی این است که به کارهای تکراری عادت نکنید! و زمانیکه متوجه شدید کار خاصی را در کد به صورت تکراری انجام می‌دهید، دقت لازم را برای تغییر آن داشته باشید؛ به صورتیکه نیاز به اعمال تغییرات تکراری در مکان‌های مختلف کد وجود نداشته باشد. راه دیگر زمانی است که کدی تکراری را مشاهده کردید. زمانیکه کدی تکراری در کدها وجود داشته باشد، اطمینان داشته باشید هنگام تغییر آن به این مشکل دچار خواهید شد. برای رفع موضوع کد تکراری می‌توانید از روش‌های مختلفی که عنوان شد استفاده کنید. 
مطالب
ویژگی های کمتر استفاده شده در NET. - بخش پنجم

Nullable<T>.GetValueOrDefault Method

با استفاده از متد GetValueOrDefault مقدار فعلی یک شیء Nullable و یا مقدار پیش فرض آن را می‌توان بدست آورد. این متد از عملگر ?? سریع‌تر است.
float? yourSingle = -1.0f;
Console.WriteLine( yourSingle.GetValueOrDefault() );

yourSingle = null;
Console.WriteLine( yourSingle.GetValueOrDefault() );

// assign different default value
Console.WriteLine( yourSingle.GetValueOrDefault( -2.4f ) );

// returns the same result as the above statement
Console.WriteLine( yourSingle ?? -2.4f );

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

شما می‌توانید برای دیکشنری نیز یک متد Get امن ایجاد کنید (در صورت عدم وجود کلید، بجای پرتاب استثناء، مقدار پیش فرض بازگشت داده شود).

public static class DictionaryExtensions
{
    public static TValue GetValueOrDefault< TKey, TValue >( this Dictionary< TKey, TValue > dic,
                                                            TKey key )
    {
        TValue result;
        return dic.TryGetValue( key,
                                out result )
            ? result
            : default(TValue);
    }
}

و روش استفاده

var names = new Dictionary< int, string >
            {
                { 0, "Vahid" }
            };
Console.WriteLine( names.GetValueOrDefault( 1 ) );


ZipFile in .NET

با استفاده از کلاس ZipFile ( رفرنس به اسمبلی System.IO.Compression.FileSystem ) می‌توان عملیات بازکردن، ایجاد و استخراج فایل‌های Zip را انجام داد.
var startPath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "Start" );
var resultPath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "Result" );
var extractPath = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.Desktop ), "Extract" );
Directory.CreateDirectory( startPath );
Directory.CreateDirectory( resultPath );
Directory.CreateDirectory( extractPath );

var zipPath = Path.Combine( resultPath, Guid.NewGuid() + ".zip" );
ZipFile.CreateFromDirectory( startPath, zipPath );
ZipFile.ExtractToDirectory( zipPath, extractPath );

C# Preprocessor Directives

با استفاده از  warning#  می توان یک هشدار را در یک قسمت خاص از کد تولید کرد.
#if DEBUG
#warning DEBUG is defined
#endif
و خروجی آن

با استفاده از  error#  می توان یک خطا را در یک جای خاصی از کد تولید کرد.
#if DEBUG
#error DEBUG is defined
#endif
در صورتی که کد بالا را اجرا کنید (در حال دیباگ) کامپایلر با نمایش DEBUG is defined در پنجره Error List، جلوی اجرای برنامه را می‌گیرد. اما در حالت ریلیز، برنامه بدون هیچ مشکلی اجرا می‌شود.

با استفاده از  line#  می توانید شماره خط کامپایلر و نام فایل خروجی (اختیاری) را برای خطاها و هشدارها تغییر دهید.

در مثال زیر، در صورتیکه در خط اول break point قرار دهید و با کلید F10 برنامه را اجرا کنید، مشاهده می‌کنید که دیباگر، خطی را که بعد از دستور line hidden# نوشته شده است، در نظر نمی‌گیرد (برای دیباگ) اما اجرا می‌شود و دیباگر بر روی دستور بعد از line default# قرار می‌گیرد.

    Console.WriteLine("Normal line #1."); // Set break point here.
#line hidden
    Console.WriteLine("Hidden line.");
#line default
    Console.WriteLine("Normal line #2.");


Stackalloc

کلمه کلیدی stackalloc برای اختصاص یک بلاک از حافظه در stack، در زمینه کد غیرامن (unsafe code) استفاده می‌شود.
مثال زیر 20 عدد اول دنباله فیبوناچی را تولید می‌کند. هر عدد از مجموع دو عدد قبلی به دست می‌آید. در این مثال، یک بلاک از حافظه به اندازه 20 عدد از نوع int را در stack (نه heap) اختصاص می‌دهد. (تفاوت stack با heap)
static unsafe void Fibonacci()
{
    const int arraySize = 20;
    int* fib = stackalloc int[arraySize];
    var p = fib;
    *p++ = *p++ = 1;

    for ( var i = 2; i < arraySize; ++i, ++p )
    {
        *p = p[-1] + p[-2];
    }

    for ( var i = 0; i < arraySize; ++i )
    {
        System.Console.WriteLine( fib[i] );
    }
}
آدرس بلاک حافظه در اشاره گر fib ذخیره می‌شود. این متغیر توسط GC جمع آوری نمی‌شود و طول عمر آن محدود به متدی است که در آن تعریف شده است و شما نمی‌توانید قبل از بازگشت متد، حافظه را آزاد کنید.
تنها دلیل استفاده از stackalloc، عملکرد بهتر آن است (برای محاسبات و یا ردوبدل اطلاعات). با استفاده از stackalloc به جای اختصاص دادن آرایه (heap)، فشار کمتری را بر GC وارد می‌کنید (نیاز کمتری به اجرای GC وجود دارد). در نتیجه سرعت اجرای بالاتری خواهید داشت.
توجه: برای اجرای مثال بالا باید پنجره خصوصیات پروژه را باز کنید و در بخش Build، گزینه Allow unsafe code را تیک بزنید.
مطالب
مروری بر کتابخانه ReactJS - قسمت هشتم - آخرین قسمت - چرخه حیات کامپوننت‌ها

هر کامپوننتی در React یک چرخه زندگی دارد. زمانیکه یک کامپوننت را به روش React.createClass یا React.Component تعریف میکنیم و در ReactDOM.render نمونه‌ای از کامپوننت را برای نمایش در مرورگر می‌سازیم، چرخه حیات آن شروع میشود. 


ReactDOMServer

کتابخانه ReactDOMServer جهت ساخت یا render کردن کامپوننت‌ها در سمت سرور استفاده میشود. توسط این کتابخانه میتوانیم کامپوننت‌ها را در سمت سرور ایجاد کنیم و نتیجه آن را که تگ‌های HTML هستند به مرورگر ارسال کنیم. این روش جهت داشتن صفحه‌های وب سریع‌تر و اهداف SEO مفید است. جهت اطلاعات بیشتر و روش‌های استفاده به مستندات آن رجوع کنید. در مثال زیر روش استفاده از این کتابخانه به اختصار آمده.

var persons = [
    { id: 1, personName: "Parham", personContact: "parhamda@gmail.com" },
    { id: 2, personName: "Roham", personContact: "roham@yahoo.com" },
    { id: 3, personName: "Raha", personContact: "raha@live.com" }
];

class Person extends React.Component{
    render(){
        return (
            <div>
                <p>{this.props.personName}</p>
                <p>{this.props.personContact}</p>
            </div>
        )
    }
}

let person1 = persons[0];
let personElement = <Person personName={person1.personName} personContact={person1.personContact}/>
console.log(ReactDOMServer.renderToStaticMarkup(personElement));

در کد بالا مواردی که جدید هستند، یکی ساخت یک نمونه از کامپوننت Person است و دیگری ساخت آن در سمت سرور، بدون آن که فعلا نمایشی در مرورگر داشته باشیم. در کنسول میتوانیم خروجی کتابخانه را که تگ‌های HTML هستند ببینیم. ReactDOMServer دو متد را فراهم کرده که کارکردی مشابه دارند؛ اما در جزئیات متفاوت هستند. 

  • renderStaticMarkup یک خروجی استاتیک و بدون attributeهای اضافه را تولید میکند که بیشتر برای بررسی یا استفاده در صفحه‌های وب ایستا مفید هستند.
  • renderToString یک خروجی به صورت HTML String ایجاد میکند که برای HTML DOM در سمت کاربر سازگار‌تر است و مناسب برای صفحات پویا. 

در نهایت خروجی از هر نوع که بود، برای اینکه در سمت کاربر قابل مشاهده باشد باید از همان متد ReactDOM.render استفاده کنیم. از آنجایی که این مجموعه جهت معرفی و بررسی ابزارهای اصلی React به صورت مختصر است، از آوردن مثال‌های زیاد و پیچیده پرهیز میکنم. در اینجا میتوانید یک نمونه ساده برای استفاده از ReactDOMServer به صورت استاندارد و با جزئیات را بررسی کنید.


متدهای چرخه حیات در React

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


 componentWillMount: این متد قبل از اینکه کامپوننت، تگ‌های متد render را بسازد اجرا میشود. این متد هم در سمت کلاینت کاربرد دارد و هم در سمت سرور. به همین جهت برای گرفتن log از داده‌های کامپوننت و کار با پایگاه داده مکان مناسبی است. به عنوان مثال در قطعه کد زیر داده‌های کامپوننت، توسط Ajax ارسال شده‌اند. 

componentWillMount() {
   Ajax.post("/componentLog", {
     name: this.constructor.name,
     props: this.props
   });
}

componentDidMount: این متد بعد از اینکه بخش render اجرا شد فراخوانی میشود. همچنین فقط در سمت کلاینت و زمانیکه از ReactDOM.render استفاده میکنیم کاربرد دارد. این متد مناسب برای تعامل کامپوننت با افزونه‌ها و API‌ها است؛ مانند دریافت اطلاعات مورد نیاز کامپوننت از سایتی دیگر توسط یک API. از  این متد در قسمت چهارم مثالی آورده شده. 


(componentWillReciveProps(nextProps: این متد زمانی اجرا میشود که داده‌های ورودی کامپوننت با مقادیری جدید تغییر کنند.

componentWillReceiveProps(nextProps) {
    // Do something with new received data and change the state. 
}

ReactDOM.render(
    <TestComponent someData={newDataEveryFiveSecond()}/>,
    document.getElementById("divTest")
);

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


(shouldComponentUpdate(nextProps, nextState: این متد شبیه به متد componentWillReceiveProps است، البته با تفاوت‌هایی. این متد هم مقدار ورودی جدید برای پارامتر‌های کامپوننت میگیرد و هم مقداری برای وضعیتی که کامپوننت دارد. این متد باید یک مقدار بازگشتی false یا  true داشته باشد. با این مقدار بازگشتی میتوان کنترل کرد که آیا کامپوننت بر اساس داده‌های جدید بروز بشود یا نه. 

class ComponentExample extends React.Component {
    shouldComponentUpdate(nextProps, nextState) {
        return notEqual(this.props, nextProps) ||
            notEqual(this.state, nextState);
    }
}

در مثال بالا پارامترها و وضعیت جاری کامپوننت، با مقدارهای تازه تغییر یافته و وضعیت جدید مقایسه میشوند. اگر مقادیر مقایسه شده برابر نباشند (یعنی داده تکراری وارد نشده) مقدار بازگشتی true خواهد بود و React کامپوننت را بر اساس وضعیت جدید و داده‌های تازه دوباره میسازد.


(componentWillUpdate(nextProps, nextState: این متد زمانیکه کامپوننت ساخته شده، داده‌های جدیدی را دریافت کند و یا وضعیت آن تغییر کند و دقیقا قبل از اجرای render فراخوانی میشود. اگر از متد shouldComponentUpdate مقدار false بازگشت داده شود، این متد دیگر اجرا نخواهد شد. باید توجه داشته باشیم که setState را نمیشود در این متد پیاده‌سازی کرده. به این علت که، زمانیکه وضعیت کامپوننت تغییر میکند، React متد componentWillUpdate و بلافاصله بعد از آن render را اجرا میکند و برای تغییر وضعیت دیگر دیر شده! تفاوت componentWillUpdate با componentWillMount  این است که Will Mount در اولین وهله سازی از کامپوننت اجرا میشود، ولی Will Update بعد از هر دوباره سازی (rerender). 


(componentDidUpdate(prevProps, prevStat: احتمالا میشود به راحتی حدس زد که این متد دقیقا بعد از دوباره سازی کامپوننتی که ساخته شده فراخوانی میشود.


componentWillUnmount: این متد زمانی اجرا میشود که یک کامپوننت از DOM پاک شود. برای پاک کردن نمونه‌ای از یک کامپوننت که در DOM در حال نمایش است میتوانیم از دستور زیر استفاده کنیم. 

ReactDOM.unmountComponentAtNode(document.getElementById("react"));
مطالب
دیباگ سرویس‌های ویندوز
یکی از سخت‌ترین چالش‌های تهیه سرویس‌های ویندوز، دیباگ آن‌ها است. برای تست و دیباگ کد‌ها در ویندوز سرویس‌ها، راهکارها و ابزارهای متفاوتی ارائه شده‌اند که در این مقاله قصد دارم یکی از آن‌ها را معرفی کنم.

برای تست کدها در ویندوز سرویس، اولین راه پیشنهادی همیشه این بوده که سرویس را موقتا به Console Application تبدیل کنیم و با تهیه یک متد در سرویس و فراخوانی آن در متد Main برنامه کنسولی، بتوانیم به دیباگ برنامه بپردازیم. مثال: 
تغییرات مورد نیاز در سرویس : 
 public Service1(string[] args)
        {
            this.args = args;
        }

        internal void TestStartupAndStop(string[] args)
        {
            this.OnStart(args);
            Console.ReadLine();
            this.OnStop();
        }
        protected override void OnStart(string[] args)
        {
            Console.WriteLine("Service Started");
            //Do Somthing
        }
تغییرات مورد نیاز در متد Main : 
static void Main(string[] args)
        {
            if (Environment.UserInteractive)
            {
                Service1 service1 = new Service1(args);
                service1.TestStartupAndStop(args);
            }
            else
            {
                // Put the body of your old Main method here.
                ServiceBase[] ServicesToRun;
                ServicesToRun = new ServiceBase[]
                {
                new Service1()
                };
                ServiceBase.Run(ServicesToRun);
            }
        }
برای کسب اطلاعات بیشتر در مورد این روش برای دیباگ، میتوانید به این لینک مراجعه نمایید.
این راه حل کلی، عموما جوابگوی نیازها هست و تست و دیباگ را میتوانیم از همین طریق انجام دهیم. اما مشکلات زیادی هم دارد. به طور مثال یکی از مشکلات هنگام استفاده از این راه حل، تفاوت دسترسی‌ها و کاربر اجرا کننده برنامه، در حالت کنسول و سرویس ویندوز است. به همین دلیل بسیار با این مشکل مواجه خواهید شد که هنگام دیباگ مشکلی وجود نداشته ولی بعد از نصب سرویس، برنامه بدرستی کار نکند. به طور مثال وقتی شما سرویس را به صورت Network Service ویا Local System نصب کنید، دسترسی برنامه به کلیدهای رجیستری , فایل سیستم و Environment تغییر میکند. در نتیجه اگر مانند روشی که در بالا ذکر شد دیباگ را انجام دهید، نمیتوانید مشکلات را شناسایی و رفع کنید. همینطور روش قبلی صرفا برای دیباگ نیازمند تغییر در سورس کد اصلی برنامه است که خود میتواند مشکلات یا محدودیت‌هایی را هنگام کار به صورت تیمی ایجاد کند.

برای دیباگ سرویس نصب شده چه باید کرد ؟
در این مطلب نمیخواهم وارد جزئیات نحوه نصب ویندوز سرویس‌ها شوم؛ چراکه خودش مبحث گسترده‌ایست. برای کسب اطلاعات بیشتر در مورد نحوه‌ی نصب سرویس‌ها میتوانید به این لینک مراجعه نمایید.
برای اینکه بعد از نصب سرویس بتوانیم به دیباگ آن بپردازیم، مراحل زیر را طی کنید.
1- اول در ابتدای متد OnStart سرویس، تکه کد زیر را وارد نمایید و یک BreakPoint کنارش قرار دهید:
System.Diagnostics.Debugger.Launch(); // Start Debugger
البته پیشنهاد میکنم که این بخش را به صورت زیر وارد کنید تا فقط درصورتیکه سرویس شما در حالت Debug ساخته شده باشد، اجرا شود.
#if DEBUG
      System.Diagnostics.Debugger.Launch(); // Start Debugger
#endif
2- سرویس را با استفاده از installutil نصب کرده و استارت نمایید.
هنگام استارت سرویس، پنجره زیر نمایان میشود.


درصورتی که Yes را انتخاب نمایید، میتوانید در یک Solution جدید به دیباگ برنامه بپردازید و دیباگر به صورت خودکار کدهای مورد نیاز را برای شروع کار، برای شما بارگذاری خواهد کرد. اما در پروژه‌های بزرگ‌تر به دلیل وابستگی‌های سرویس به کتابخانه‌های متعدد، امکان دیباگ کامل را هنوز در اختیار نداریم و دیباگ ما در اینجا، محدود به خود سرویس است. برای اینکه بتوانیم دیباگ را کامل در Solution اصلی برنامه انجام دهید، قبل پاسخ دادن به پنجره بالا یک مرحله دیگر از کار باقی مانده است.
3- در Solution اصلی برنامه خود، کلید‌های Ctrl+Alt+P را فشار دهید؛ یا از طریق منوی Debug > Attach to process، پنجره Attack to Process را باز نمایید.

همانطور که در تصویر مشاهده میکنید، ID پروسه را در هر دو عکس برایتان مشخص کرد‌ه‌ام (هایلایت زرد) که به راحتی میتوانید پروسه سرویستان را از این طریق پیدا کنید.
کافیست روی کلید Attach کلیک نمایید تا در Solution اصلی برنامه بتوانید به صورت کامل به دیباگ سرویس نصب شده بپردازید. پس از اتچ کردن پروسه به Solution خود، پنجره اول (Visual Studio Just-In Time debugger) را ببندید یا گزینه No را انتخاب نمایید.
نکته: برای استفاده از این روش باید Visual Studio به صورت Administrator اجرا شده باشد.
مطالب
معرفی Local Storage و چند کتابخانه مرتبط
یکی از راه‌های ذخیره داده‌ها در سمت کلاینت، استفاده از کوکی‌ها می‌باشد که تقریبا همه با آن کم و بیش آشنایی داریم و هر کدام بسته به نیاز، تا حدودی از آن استفاده کرده‌ایم. اما می‌توان local storage را در یکی دو مورد نسبت به کوکی برتر شمرد؛ مواردی مثل حجم داده‌های ذخیره شده که نسبت به کوکی بیشتر می‌باشد و عدم ارسال با درخواست‌های ارسالی سمت سرور.

Local Storage
با استفاده از Local Storage قادر خواهیم بود تا داده‌هایی را در سمت کلاینت ذخیره کنیم؛ بدون اینکه بر عملکرد سایت تاثیر بگذارد. منظور از تاثیر بر روی عملکرد این است که در هر رفت به سمت سرور لازم نیست با درخواست ارسالی، داده‌های اضافه‌ای ارسال شوند.
با استفاده از Local Storage قادر خواهیم بود حداقل 5 مگابایت داده را ذخیره کنیم.

  استفاده از Local Storage با استفاده از دو شیء زیر امکان پذیر می‌باشد:
  • window.localStorage  : اطلاعات ذخیره شده در آن فاقد تاریخ انقضاء می‌باشند و تا زمانی که اقدام به حذف آن ننمایید، داده‌های آن معتبر می‌باشند.
  • window.sessionStorage : اطلاعات ذخیره شده در آن تا زمان بسته شدن تب مرورگر، معتبر می‌باشند. با بسته شدن تب فعلی مرورگر، تمامی اطلاعات ذخیره شده از بین خواهند رفت.

نحوه استفاده:

- نحوه افزودن داده در Local Storage به صورت کلید/ مقدار می‌باشد.

ابتدا نگاهی داشته باشیم به اینترفیس Storage:

interface Storage {
  readonly attribute unsigned long length;
  DOMString? key(unsigned long index);
  getter DOMString? getItem(DOMString key);
  setter void setItem(DOMString key, DOMString value);
  deleter void removeItem(DOMString key);
  void clear();
}
برای مطمئن شدن از پشتیبانی مرورگر از Local storage، بهتر است ابتدا اقدام به بررسی پشتیبانی مرورگر مربوطه کرده و در صورت پشتیبانی، اقدام به ذخیره سازی داده کنیم. نحوه بررسی پشتیبانی مرورگر به صورت زیر می‌باشد:
if (typeof(Storage) !== "undefined") {
    // do ...
}

شیء window.localStorage
همانطور که بیان کردیم، در این روش داده دارای تاریخ انقضاء نمی‌باشد.
() SetItem :
متد setItem برای ذخیره اطلاعات است و نحوه استفاده از آن به صورت زیر می‌باشد:
localStorage.setItem("lastpost", "localstorage");
setItem ابتدا مقدار کلید را بررسی کرده و در صورت عدم موجود بودن، اقدام به ذخیره مقدار با کلید مورد نظر خواهد کرد.
localStorage.setItem("visitorCount",15 );
localStorage.setItem("visitorCount", 16);
در کد فوق مقدار visitorcount برابر با 16 خواهد بود.

() getItem  :
برای دسترسی به مقدار ذخیره شده می‌توانیم از متد getItem به همراه مقدار کلید استفاده کنیم. در صورت موجود نبودن مقدار، null برگردانده خواهد شد.
 localStorage.getItem("lastpost");
لازم به ذکر است، میتوانیم به صورت زیر هم اقدام به ذخیره و بازیابی داده‌ها کنیم:
localStorage.lastpost = "localstorage";
document.getElementById("result").innerHTML = localStorage.lastpost;
در این حالت شبیه به استفاده از ViewBag  می‌باشد.

و برای حذف اطلاعات ذخیره شده:
localStorage.removeItem("lastpost");
- کلید و مقدار به صورت رشته‌ای ذخیره خواهند شد. در صورتی که قصد ذخیره سازی با فرمت‌های دیگری را داشته باشید، باید ابتدا اقدام به convert نمایید. البته در ادامه کتابخانه‌هایی را برای این مقصود معرفی خواهیم کرد.

خصوصیت length :
تعداد جفت کلید / مقدار‌های جاری را بر میگرداند.

متد Key :
این متد مقداری را از ورودی دریافت کرده و اسم کلید مورد نظر را بر میگرداند. ایندکس آن از صفر شروع خواهد شد. قطعه کد زیر باعث برگرداندن مقدار lastname می‌شود:
 localStorage.setItem("name","uthman" );
   localStorage.setItem("lastname","24" );

   alert(localStorage.key(1));
متد () clear :
این متد باعث پاک شدن تمامی داده‌های ذخیره شده خواهد شد.

شی sessionStorage :
از نظر syntax دستوری همانند localStorage می‌باشد و تفاوت آن فقط در زمان ذخیره سازی است؛ با بسته شدن تب مرورگر، تمامی داده‌های ذخیره شده پاک خواهند شد.

کتابخانه‌های مرتبط :
Locker :
با استفاده از این کتابخانه ، قادر خواهید بود تا اطلاعات را با فرمت‌های مورد نظر، بدون convert آنها ذخیره نمایید. نمونه‌ای از آن به صورت زیر خواهد بود:
Lockr.set('website', 'SitePoint'); // string
Lockr.set('categories', 8); // number
Lockr.set('users', [{ name: 'John Doe', age: 18 }, { name: 'Jane Doe', age: 19 }]);
کتابخانه فوق دارای امکاناتی بیشتری می‌باشد که در صورت تمایل می‌توانید به گیت هاب آن مراجعه نمایید.

secStore.js  :
با استفاده از کتابخانه SJCL امنیت داده‌های ذخیره شده، بالا می‌روند.
var storage = new secStore
  , options = {
    encrypt: true,
    data: {
      key: 'some data that is somewhat private'
    }
  };


storage.set(options, function(err, results){
  if (err) throw err;
  console.log(results);
});

و سایر کتابخانه‌های مرتبط :

//lz-string example

var string = 'A string to test our compression.';
console.log(string.length); // 33 
var compressed = LZString.compress(string);
console.log(compressed.length); // 18
string = LZString.decompress(compressed);
 
مطالب
آشنایی با CLR: قسمت بیست و دوم
در ادامه قسمت قبلی، به خصوصیات و ویژگی‌های اسمبلی قوی می‌پردازیم:

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

موقعیکه برنامه نیاز داشته باشد به اسمبلی نام قوی بایند یا متصل شود، از 4 ویژگی گفته شده‌ی در قسمت قبلی استفاده می‌کند تا آن را در GAC بیابد. اگر اسمبلی درخواستی موجود باشد، زیر دایرکتوری آن برگشت داده خواهد شد؛ ولی اگر آن را نیابد، ابتدا در داخل دایرکتوری برنامه و سپس در مسیرهایی که در فایل پیکربندی ذکر شده‌اند، به دنبال آن خواهد گشت و در نهایت اگر برنامه توسط فایل MSI نصب شده باشد، محل‌های توزیع را از طریق آن جویا خواهد شد و اگر باز به نتیجه‌ای نرسد، اتصال ناموفق گزارش شده و خطای زیر را ایجاد خواهد کرد:
System.IO.FileNotFoundException
هر بار که برنامه اسمبلی را از غیر از مسیر GAC بخواند، ابتدا هش آن اعتبارسنجی می‌شود و اگر هش آن غیرقابل شناسایی باشد، خطای زیر را صادر می‌کند:
System.IO.FileLoadExceptio
یک اسمبلی با نام قوی مزایای زیر را به همراه دارد:
  1. جلوگیری از دستکاری و حفظ امنیت آن
  2. این اسمبلی تنها یکبار از حافظه‌ی فیزیکی استفاده میکند؛ برعکس توزیع خصوصی که برای هر برنامه باید یک فضای دیسکی داشته باشد.
  3. توزیع ساده‌تر برای نسخه‌های آینده فراهم می‌شود که بعدا در مورد آن توضیح می‌دهیم.
با این حال GAC، یک سیستم امنیتی است که تنها باید توسط مدیر سیستم کنترل شود و هم اینکه توزیع آن‌ها به راحتی توزیع اسمبلی‌های خصوصی نیست. توصیه می‌شود که از اسمبلی‌های نام قوی، زمانی در GAC استفاده کنید که نیاز است توسط چندین برنامه به طور مشترک استفاده شود؛ در غیر این صورت از همان شیوه‌ی توزیع خصوصی استفاده شود. چون در این حالت هم بهترین حالت ایزوله بودن مهیاست، هم نصب آن ساده‌تر است و از آنجا که محل GAC در شاخه system32 است مرتبا از فضای دیسک آن ناحیه کم می‌شود.

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

کنترل مدیریت پیشرفته (پیکربندی)
در قسمت بیستم با ساخت فایل پیکریندی و نحوه تنظیم کردن اسکن اسمبلی‌ها در CLR آشنا شدیم. این بار قصد داریم در مورد المان‌های دیگر این فایل پیکریندی صحبت کنیم. فایل پیکربندی زیر را بررسی می‌کنیم:
 
<?xml version="1.0"?>
  <configuration>
     <runtime>
        <assemblyBinding xmlns="urn:schemas­microsoft­com:asm.v1">
           <probing privatePath="AuxFiles;bin\subdir" />
             <dependentAssembly>
                <assemblyIdentity name="SomeClassLibrary"  publicKeyToken="32ab4ba45e0a69a1" culture="neutral"/>
                <bindingRedirect  oldVersion="1.0.0.0" newVersion="2.0.0.0" />
                <codeBase version="2.0.0.0"  href="http://www.Wintellect.com/SomeClassLibrary.dll" />
             </dependentAssembly>
             <dependentAssembly>
                <assemblyIdentity name="TypeLib"  publicKeyToken="1f2e74e897abbcfe" culture="neutral"/>
                <bindingRedirect   oldVersion="3.0.0.0­3.5.0.0" newVersion="4.0.0.0" />
                <publisherPolicy apply="no" />
             </dependentAssembly> 
         </assemblyBinding>
     </runtime> 
 </configuration>
 Probing  در قسمت بیستم گفتیم که در این المان، مکان‌هایی را که CLR برای پیدا کردن اسمبلی‌های با نام ضعیف، باید اسکن کند، وارد می‌کنیم که هر مسیر با , از هم جدا شده‌است. برای اسمبلی‌های با نام قوی CLR باید داخل GAC را نگاه کند و در URL هایی که از طریق المان CodeBase مشخص کرده‌ایم. اگر المان CodeBase مشخص نشود، CLR برای پیدا کردن اسمبلی‌های با نام قوی، داخل دایرکتوری‌های این المان را اسکن خواهد کرد.
 Dependent Assembly اول
 در اولین فرزند این المان، Assembly Identity یک اسمبلی با مشخصاتی مثل Culture و توکن عمومی معرفی می‌شود و در BindingRedirect به CLR اطلاع می‌دهد موقعی که به دنبال نسخه یک این اسمبلی است، نسخه‌ی دو آن را برای استفاده جایگزین کند.
 Code Base
 این المان می‌گوید که وقتی CLR سعی در پیدا کردن نسخه‌ی 2 اسمبلی را دارد، آن را از طریق آدرس مورد نظر پیدا کند. این المان میتواند برای اسمبلی‌های با نام ضعیف هم کار کند.
  Dependent Assembly دوم  این مورد هم همانند سابق است با این تفاوت که گستره‌ی نسخه 3 تا 3.5 را به نسخه‌ی 4 تغییر میدهد.
Publisher Policy
اگر سازمان تولید کننده این اسمبلی فایلی برای تعیین Policy به همراه اسمبلی ارائه کرده باشد، این المان باعث می‌شود این فایل ندیده گرفته شود (در مورد فایل Policy در آینده صحبت می‌کنیم).
اکنون هر موقع CLR ارجاعی از جدول AssemblyDef را برای بارگذاری یک اسمبلی دریافت کند، ابتدا فایل پیکربندی را بررسی میکند و بر اساس آن اسمبلی مورد نظر را بارگذاری خواهد کرد. در این لحظه اسمبلی مورد نظر در GAC وجود دارد و آن را بار می‌کند؛ یا اگر یافت نشد المان CodeBase را بررسی میکند و اگر این المان تعریف نشده باشد، در ادامه سیاست‌های قبلی، پوشه کاری برنامه و دایرکتوری‌های private را اسکن میکند.

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

نکته : اگر مدیر بخواهد تمام برنامه‌های موجود از این اسمبلی جدید استفاده کنند باید فایل machine.config را ویرایش کند.