مطالب
مفاهیم پایه سیستم های کنترل نسخه؛ قسمت اول : گیت
در این مقاله با دو سیستم کنترل نسخه  git  و  SVN  آشنا شده و تفاوت‌های آن‌ها را برای تازه‌کاران بررسی می‌کنیم. ایده اولیه نوشتن این مقاله زمانی بود که برای یک پروژه‌ای، اعضای تیم ما دور هم جمع شده و در مورد ابزارهای مورد استفاده بحث کردند و یک عده از گیت و عده‌ای از SVN صحبت می‌کردند. بر این شدم که مقاله‌ای نوشته و ابتدا به معرفی آن‌ها و سپس به مزایا و معایب هر کدام بپردازیم.  
امروزه، استفاده از سیستم‌های کنترل نسخه ( Version Control System ) رواج زیادی پیدا کرده است. این سیستم‌ها به شما اجازه می‌دهند تا تغییراتی را که در پروژه ایجاد می‌شوند، ضبط و ثبت کرده تا از تغییراتی که در سطح پروژه اتفاق می‌افتد آگاه شوید. با ذکر یک نمونه این تعریف را باز میکنم:
شما به صورت تیمی در حال انجام یک پروژه هستید و باید نسبت به تغییراتی که اعضای تیم در یک پروژه می‌دهند، آگاه شوید. هر برنامه نویس بعد از انجام تغییرات باید این تغییرات را در سیستم کنترل نسخه به روز کند تا بتوان به سوالات زیر پاسخ داد:
 آیا اگر در بین راه به مشکل برخوردید می‌توانید پروژه خود را به یک یا چند گام عقب‌تر برگردانید؟ آیا می‌توانید به هر یک از اعضاء تیم دسترسی‌هایی را به قسمت هایی از پروژه تعیین کنید؟ می‌توانید تفاوت فایل‌های تغییر یافته را بیابید؟ آیا میتوان خطاهای یک برنامه را گزارش داد و به بحث در مورد آن پرداخت؟ چه کسی کدها را تغییر داده است؟ روند کار و تغییرات به چه صورت است؟ (این مورد برای به روز کردن نمودارهای burndown در توسعه چابک می‌تواند بسیار مفید باشد.)
پی نوشت: نه تنها در یک تیم بلکه بهتر هست در یک کار انفرادی هم از این سیستم‌ها استفاده کرد تا حداقل بازبینی روی پروژه‌های شخصی خود هم داشته باشیم.

سیستم کنترل گیت: این سیستم در سال 2005 توسط لینوس توروالدز خالق لینوکس معرفی شد و از آن زمان تاکنون یکی از پر استفاده‌ترین سیستم‌های کنترل نسخه شناخته شده است. ویکی پدیا گیت را به این شکل تعریف می‌کند: «یک سیستم بازبینی توزیع شده با تاکید بر جامعیت داده‌ها، سرعت و پشتیبانی جهت توزیع کار.»
از معروف‌ترین سیستم‌های هاستینگ که از گیت استفاده می‌کنند، می‌توان به گیت هاب اشاره کرد.
اکثر سیستم‌های هاستینگ گیت، دو حالت را ارائه می‌دهند:
عمومی : در این حالت کدهای شما به عموم بازدیدکنندگان نمایش داده می‌شود و دیگران هم می‌توانند در تکمیل و ویرایش کدهای شما مشارکت کنند و این امکان به صورت رایگان فراهم است. سیستم گیت هاب به دلیل محبوبیت زیادی که دارد، در اکثر اوقات انتخاب اول همه کاربران است.
خصوصی: در این حالت کد متعلق به شما، یا شرکت یا تیم نرم افزاری شماست و غیر از افراد تعیین شده، شخص دیگری به کدهای شما دسترسی ندارد. اکثر سیستم‌های مدیریتی این مورد را به صورت premium پشتیبانی می‌کنند. به این معنا که باید اجاره آن را به طور ماهانه پرداخت کنید. سیستم گیت هاب ماهی پنج دلار بابت آن دریافت می‌کند. سیستم دیگری که در این زمینه محبوبیت دارد سیستم BitBucket هست که که اگر تیم شما کوچک است و در نهایت پنج نفر هستید، می‌توانید از حالت خصوصی به طور رایگان استفاده کنید ولی اگر اعضای تیم شما بیشتر شد، باید هزینه‌ب اجاره آن را که از 10 دلار آغاز می‌گردد، به طور ماهیانه پرداخت کنید.
پی نوشت: میتوانید از سیستم‌های متن باز رایگان هم که قابل نصب بر روی هاست ها هم هستند استفاده کنید که در این حالت تنها هزینه هاست یا سرور برای شما می‌ماند.

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

Fork: هر کاربری که قصد تغییر را بر روی سورس کدی، داشته باشد، ابتدا باید پروژه‌ی نویسنده اصلی پروژه را به یک مخزنی که متعلق به خودش هست انتقال دهد. به این عمل Fork کردن می‌گویند. حال کاربر تغییرات خودش را اعمال کرده و لازم هست که این تغییرات با پروژه‌ی اصلی که به آن Master می‌گوییم ادغام شوند. بدین جهت کاربر فرمان pull request را می‌دهد تا به نویسنده‌ی اصلی پروژه این موضوع اطلاع داده شود و نویسنده‌ی اصلی در صورت صلاحدید خود آن را تایید کند. 

Branching یا شاخه بندی: نویسنده‌ی مخزن اصلی می‌تواند با مفهومی با نام شاخه بندی کار کند. او با استفاده از این مفهوم، پروژه را به قسمت یا شاخه‌های مختلف تقسیم کرده و همچنین با ایجاد دسترسی‌های مختلف به کاربران اجازه تغییرات را بدهد. به عنوان مثال بخش‌های مختلف پروژه از قبیل بخش منطق برنامه، داده ها، رابط کاربری و ... می‌تواند باشد. بعد از انجام تغییرات روی یک شاخه می‌توانید درخواست merge ادغام شدن یا کل پروژه را داشته باشید. در عمل شاخه بندی، هیچ کدام از شاخه‌های بر روی یک دیگر تاثیر یا دخالتی ندارند و حتی می‌توانید چند شاخه را جدا از بخش master با یکدیگر ادغام کنید.

به غیر از ارتباط خط فرمانی که میتوان با گیت هاب برقرار کرد، میتوان از یک سری ابزار گرافیکی خارجی هم جهت ایجاد این ارتباط، استفاده کرد:
GitHub For Windows : نسخه‌ی رسمی است که از طرف خود گیت هاب تهیه گردیده است و استفاده از آن بسیار راحت است. البته یک مشکل کوچک در دانلود آن وجود دارد که دانلود آن از طریق یک برنامه‌ی جداگانه صورت گرفته و اصلا سرعت خوبی جهت دانلود ندارد.
Visual Studio .Net : (+ ) خود ویژوال استودیو شامل سیستمی به اسم Microsoft Git Provider است که در بخش تنظیمات می‌توانید آن را فعال کنید (به طور پیش فرض فعال است) و به هر نوع سیستم گیتی می‌توانید متصل شوید. تنها لازم است که آدرس Url گیت را وارد کنید.
SourceTree: از آن دست برنامه‌های محبوبی است که استفاده آسانی دارد و خودم به شخصه از آن استفاده می‌کنم. شامل دو نسخه‌ی ویندوز و مک است و میتوانید با چندین سیستم گیت مثل «گیت هاب» و «بیت باکت» که در بالا به آن‌ها اشاره شد، به طور همزمان کار کند.
 
نظرات مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت هفتم - بررسی مفاهیم Mobx
- در Redux فقط یک store سراسری وجود دارد که در برگیرنده‌ی تمام اشیاء حالت‌های کامپوننت‌های مختلف برنامه است. علتش را هم در قسمت اول این سری مطالعه کنید که اصلا چرا به آن نیاز هست. علت انجام اینکارها چی هست. چه مشکلی وجود داشته که نیاز به store سراسری حس شده که بعد از آن مفاهیم مدیریت حالت سراسری ارائه شده‌اند.
- در Mobx می‌توان از چند store سراسری استفاده کرد. طراحی این storeها با خود شما است و برخلاف Redux که دسترسی مستقیمی به آن نیست، در اینجا می‌توان مستقیما خواص observable آن‌را تغییر داد و یا متدهایی را که به صورت action علامتگذاری شده‌اند، فراخوانی کرد و store را به حالت دیگری تغییر داد. یعنی در کل طراحی شیءگرای store در MobX، کاملا به برنامه نویس واگذار شده‌است. می‌توانید چندین store را به ازای اشیاء حالت کامپوننت‌های مختلف ایجاد کنید و سپس آن‌ها را به عنوان خاصیت‌های عمومی store اصلی که توسط Provider به لایه‌های زرین ارسال می‌کنید، معرفی کنید.
مطالب
نکته ای برای استفاده راحت تر ECMA Scripts در توسعه تحت شیرپوینت
هنگام کدنویسی زبان‌های سمت کاربر (Client Side)  اگر به عنوان برنامه نویس و توسعه دهنده ، با کتابخانه‌های آن آشنا 
باشید و آنها را حفظ باشید خیلی خوب است ولی اگر همین کتابخانه‌ها و توابع به ابعاد شیرپوینت گسترش یابد چه؟ مسلما حفظ کردن تمام آنها کار دشواری است ؛ راه حلی که در اینجا ارائه می‌شود ،بار گذاری این توابع در Intellisense  ویژوال استودیو است . برای این کار در صورتی که در حال نوشتن Visual WebPart هستید ، می‌توانیدبه روش زیر عمل کنید :  

ابتدا بالای صفحه مورد نظر کد زیر را وارد کنید : 
<% #if SOME_UNDEFINED_CONSTANT %>
 <script type="text/javascript" src="/_layouts/MicrosoftAjax.js" ></script>
 <script type="text/javascript" src="/_layouts/SP.debug.js"></script>
<% #endif %>
در این لحظه ویژوال استودیو آغاز به روز رسانی متادیتای Intellisense  خود می‌کند که می‌توانید این پیغام را در زیر نوار وضعیت این برنامه مشاهده کنید . 



حال می‌توانید به راحتی از این امکانات لذت ببرید و به توسعه خود سرعت دهید.

مطالب دوره‌ها
ماژول ها و فضای نام(namespace)
در #F ماژول به گروهی از کدها، توابع، انواع داده‌ها و شناسه‌ها گفته می‌شود و کاربرد اصلی آن برای قرارگیری کد‌ها مرتبط به هم در یک فایل است و هم چنین از تناقص نام‌ها جلوگیری می‌کند. در #F در صورتی که توسط برنامه نویس ماژول تعریف نشود هر source file یک ماژول در نظر گرفته می‌شود. برای مثال:
// In the file program.fs. 
let x = 40
بعد از کامپایل تبدیل به کد زیر می‌شود.
module Program
let x = 40
هم چنین امکان تعریف چند ماژول در یک source file نیز میسر است. به این صورت که باید برای هر ماژول محلی یک نام اختصاص دهید. در مثال بعدی دو تا ماژول را در یک فایل به نام mySourceFile قرار می‌دهیم.
module MyModule1 =
     let module1Value = 100

    let module1Function x =
        x + 10

// MyModule2 
module MyModule2 =

    let module2Value = 121

    let module2Function x =
        x * (MyModule1.module1Function module2Value)
در آخرین خط همان طور که مشاهده می‌کنید با استفاده از نام ماژول می‌توانیم به تعاریف موجود در ماژول دسترسی داشته باشیم.( MyModule1.module1Function ).

استفاده از یک ماژول در فایل‌های دیگر.
گاهی اوقات نیاز به استفاده از تعاریف و توابع موجود در ماژولی داریم که در یک فایل دیگر قرار دارد. در این حالت باید به روش زیر عمل کنیم.
فرض بر این است ماژول زیر در یک فایل به نام ArithmeticFile قرار دارد.
module Arithmetic

let add x y =
    x + y

let sub x y =
    x - y
حال قصد استفاده از توابع بالا رو در یک فایل و ماژول دیگر داریم.
#1 روش اول (دقیقا مشابه روش قبل از نام ماژول استفاه می‌کنیم)
let result1 = Arithmetic.add 5 9
#2 روش دوم(استفاده از open)
open Arithmetic
let result2 = add 5 9
ماژول‌های تودرتو
در #F می‌توانیم بک ماژول را درون ماژول دیگر تعریف کنیم یا به عبارت دیگر می‌توانیم ماژولی داشته باشیم که خود شامل چند تا ماژول دیگر باشد. مانند:
module Y =
    let x = 1 

    module Z =
        let z = 5
روش تعریف ماژول‌های تودرتو در #F در نگاه اول کمی عجیب به نظر میرسه. جداسازی ماژول‌های تودرتو به وسیله دندانه گذاری یا تورفتگی انجام می‌شود. ماژول Z در مثال بالا به اندازه چهار فضای خالی جلوتر نسبت به ماژول Y قرار دارد در نتیجه به عنوان ماژول داخلی Y معرفی می‌شود.
module Y =
    let x = 1 

module Z =
    let z = 5
در مثال بالا به دلیل اینکه ماژول Z و Y از نظر فضای خالی در یک ردیف قرار دارند در نتیجه  ماژول تودرتو نیستند. حال به مثال بعدی توجه کنید.
module Y =
module Z =
    let z = 5
در این مثال ماژول X به عنوان ماژول داخلی Y حساب می‌شود. دلیلش هم این است که ماژول Y بدنه ندارد درنتیجه مازول Z بلافاصله بعد از آن قرار میگیرد که کامپایلر اونو به عنوان مازول داخلی حساب می‌کنه. اما برای اینکه مطمئن شود که قصد شما تولید ماژول تودرتو بود یک Warning میدهد. برای اینکه Warningv رو مشاهده نکنیم می  تونیم کد بالا رو به صورت زیر بازنویسی کنیم:
module Y =
    module Z =
        let z = 5
فضای نام (namespace)
مفهوم فضای نام کاملا مشابه مفهوم فضای نام در #C است و راهی است برای کپسوله سازی کد‌ها در برنامه. مفهوم namespace  با مفهوم module کمی متفاوت است.

ساختار کلی
namespace [parent-namespaces.]identifier
چند نکته درباره namespace
#1 اگر قصد داشته باشید که از فضای نام در کد‌های خود استفاده کنید باید اولین تعریف در source file برنامه تعریف namespace باشد.
#2 امکان تعریف شناسه یا تابع به صورت مستقیم در namespace وجود ندارد بلکه این تعاریف باید در ماژول‌ها یا type‌ها نظیر تعریف کلاس قرار گیرند.
#3 امکان تعریف فضای نام با استفاده از تعاریف ماژول نیز وجود دارد( در ادامه به بررسی یک مثال در این زمینه می‌پردازیم)

تعریف namespace به صورت مستقیم:

namespace Model

type Car =
    member this.Name = "BMW" 

module SetCarName =
    let CarName = "Pride"
تعریف namepsace به صورت غیر مستقیم(استفاده از module)
module Model.Car

module SetCarName =
    let CarName = "Pride"
فضای نام‌های تودرتو
همانند ماژول‌ها امکان تعریف فضای نام تودرتو نیز وجود دارد. یک مثال در این زمینه:
namespace Outer

    type OuterMyClass() =
       member this.X(x) = x + 1

namespace Outer.Inner

    type InnerMyClass() =
       member this.Prop1 = "X"
همانند فضای نام‌های در #C با استفاده از (.) می‌توانیم فضای نام‌های تودرتو ایجاد کنیم. در مثال بالا فضای نام Inner به عنوان فضای نام داخلی Outer تعریف شد است. برای دسترسی به کلاس InnerMyClass باید تمام مسیر فضای نام رو ذکر کنیم.
Outer.Inner.InnerMyClass
مطالب
نصب Mono 3.0 بر روی Ubuntu
با استفاده از مونو امکان اجرای برنامه‌های دات نت تحت لینوکس وجود دارند. در ادامه سعی خواهیم کرد تا نگارش 3 آن‌را بر روی اوبونتو نصب کنیم. مونو 3 تا دات نت 4 و نیم را پشتیبانی می‌کند.

دریافت اوبونتو
برای دریافت اوبونتو به آدرس ذیل مراجعه نمائید.
نسخه سرور آن GUI ندارد (هرچند بعدا در طی یک بسته 450 مگابایتی قابل نصب است). نسخه دسکتاپ آن به همراه GUI نیز هست. البته برای نصب دات نت بر روی آن این مساله تفاوتی نمی‌کند. برای نصب آزمایشی و مجازی آن هم می‌توانید برای مثال از VMWare workstation استفاده کنید؛ بدون اینکه نیاز داشته باشید این توزیع خاص لینوکس را واقعا بر روی کامپیوتر خود نصب کنید.

در تمام قسمت‌های ذیل فرض بر این است که ترمینال خط فرمان لینوکس را گشوده‌اید و همچنین سیستم به اینترنت وصل است.



دریافت Git و Curl

ابتدا دستور زیر را در خط فرمان لینوکس اجرا کنید تا سیستم بسته‌های لینوکس به روز شده و همچنین یک سری پیشنیاز مانند git ، curl و امثال آن نصب شوند (کتابخانه curl جهت استفاده در محیط‌های برنامه نویسی کاربرد دارد و همچنین برنامه پیشرفته‌ای است برای کار با وب و دریافت فایل‌ها):


 sudo apt-get update && sudo apt-get -y install git-core curl python-software-properties
sudo apt-get install build-essential automake checkinstall intltool git

نصب آخرین نگارش Mono و وابستگی‌های آن

در ادامه نوبت به نصب آخرین نگارش مونو است. از روش متداول ذیل برای نصب مونو استفاده نکنید :
 sudo apt-get install mono-complete
این دستور نگارش 2.10.8.1 را تا این تاریخ بر روی سیستم شما نصب خواهد کرد و اگر پیشتر مونو را به این روش نصب کرده‌اید، با استفاده از دستور ذیل آن‌را حذف کنید:
 sudo apt-get purge mono-runtime
برای دسترسی به آخرین نگارش نگارش مونو، نیاز است آن‌را از روی سورس آن کامپایل کنیم. اسکریپت کامل نصب آن‌را در آدرس ذیل می‌توانید پیدا کنید:
و یا اگر آدرس فوق برقرار نبود از اینجا: install_mono-3.0-sh
برای نمونه جهت نصب mono نگارش 3 از اسکریپت install_mono-3.0.sh به نحو ذیل استفاده خواهیم کرد (این دستورات را به ترتیب در ترمینال لینوکس اجرا کنید):
 mkdir mono-3.0
cd mono-3.0
wget --no-check-certificate https://github.com/nathanb/iws-snippets/raw/master/mono-install-scripts/ubuntu/install_mono-3.0.sh
chmod 755 install_mono-3.0.sh
./install_mono-3.0.sh
این پروسه مدتی طول خواهد کشید (تا تمام بسته‌های لازم از اینترنت دریافت شوند). استفاده از اسکریپت فوق کار را بسیار ساده کرده و بسیاری از مراحل لازم نصب مونو را یکجا در خود به همراه دارد. مونو 3 تا دات نت 4 و نیم را پشتیبانی می‌کند.


بعد از اجرای فرمان فوق به خطای ذیل خواهید رسید:
 config.status: error: cannot find input file: `po/mcs/Makefile.in.in'
این مورد مشکلی است که در نگارش 3.0.10 رخ داده و فراموش کرده‌اند که یک پوشه را کپی کنند (در نگارش‌های قبلی آن این مشکل وجود نداشته و با توجه به آگاه شدن از آن، در نگارش‌های بعدی نیز نباید مشکلی باشد).
برای رفع آن ابتدا به مسیر ذیل وارد شوید (پوشه build/mono-3.0.10/po)، فایل mcs را حذف (این مورد در اصل یک پوشه است و نه یک فایل) و سپس بسته اصلی mono را از github دریافت کنید. آن‌را unzip کرده و کل پوشه mcs داخل آن‌را به درون پوشه po جاری کپی کنید. سپس فایل zip دریافت شده را حذف کنید:
 cd mono-3.0/build/mono-3.0.10/po
rm mcs
wget https://github.com/mono/mono/archive/master.zip
unzip master.zip
mv mcs/ mono-3.0/build/mono-3.0.10/po
rm -rf mono-master master.zip
البته برای اینکه وقت شما زیاد تلف نشود، پوشه mcs نگارش 3.0.10 را از آدرس ذیل دریافت و پس از unzip درون پوشه mono-3.0/build/mono-3.0.10/po کپی کنید. (6 سطر فوق هم نیازی به اجرا ندارند)

پس از باز سازی پوشه مفقود mcs، باید مرحله «building mono packages» موجود در فایل install_mono-3.0.sh اجرا شود. برای این منظور، فایل final-build-mono-3.0.sh را  از آدرس ذیل دریافت و در کنار فایل install_mono-3.0.sh موجود کپی کنید.


سپس در خواص این فایل، مجوز execute را نیز فعال نمائید. اکنون آن‌را اجرا کنید:
 ./final-build-mono-3.0.sh
فایل final-build-mono-3.0.sh در حقیقت همان فایل install_mono-3.0.sh اصلی است. با این تفاوت که قسمت ابتدای فایل که در آن وابستگی‌های لازم از اینترنت دریافت می‌شدند، حذف شده است. چون پیشتر اینکار را انجام داده بودیم (با اجرای اولیه آن).


اکنون مدتی صبر کنید تا کار کامپایل نهایی تمام بسته‌های دریافت شده پس از اجرای اسکریپت final-build-mono-3.0.sh انجام شود.


آزمایش Mono نصب شده

برای اینکه مطمئن شویم، Mono درست نصب شده است، دستور زیر را در خط فرمان صادر کنید:
 /opt/mono-3.0/bin/mono -V


برای اینکه این مسیر را به Path لینوکس اضافه کنیم تا قادر شویم فرمان mono را در هر مسیری اجرا کنیم، ابتدا دستور ذیل را اجرا کرده
 sudo nano /etc/environment
و سپس در ادیتور باز شده، مسیر و عبارات ذیل را به انتهای مقدار جاری اضافه کنید:
 :/opt/mono-3.0/bin


بعد ctrl+x را زده، به پیام ذخیره سازی تغییرات پاسخ مثبت دهید. سپس نیاز است یکبار logoff و login کنید تا این تغییرات اعمال شوند.


یک نکته تکمیلی:
اگر به صفحه نگارش‌های رسمی Mono 3.x مراجعه کنید، نگارش‌های جدیدتری را نیز می‌توانید ملاحظه کنید. فایل‌های قابل نصب آن‌ها نیر در آدرس‌های ذیل قرار دارند:
برای استفاده از اسکریپت install_mono-3.0.sh با این نگارش‌های جدیدتر فقط کافی است تعاریف ذیل را بر اساس شماره نگارش بسته‌های جدید اصلاح کنید:
PACKAGES=("mono-3.0.10"
"libgdiplus-2.10.9"
"gtk-sharp-2.12.11"
"xsp-2.10.2"
"mod_mono-2.10")

URLS=("http://download.mono-project.com/sources/mono/mono-3.0.10.tar.bz2"
"http://download.mono-project.com/sources/libgdiplus/libgdiplus-2.10.9.tar.bz2"
"http://download.mono-project.com/sources/gtk-sharp212/gtk-sharp-2.12.11.tar.bz2"
"http://download.mono-project.com/sources/xsp/xsp-2.10.2.tar.bz2"
"http://download.mono-project.com/sources/mod_mono/mod_mono-2.10.tar.bz2")
مطالب
Functional Programming - قسمت پنجم - وسواس استفاده از نوع های اولیه
در ادامه سری مقالات مرتبط با برنامه نویسی تابعی ، قصد دارم به استفاده کردن یا نکردن از نوع‌های داده اولیه (Primitive Types) را بررسی کنیم. پیشنهاد میکنم در صورتی که قسمت‌های قبلی را مطالعه نکرده اید ابتدا قسمت‌های قبل را بخوانید.

در طراحی مدل دامین، بیشتر مواقع از نوع‌های اولیه مانند int , string,… استفاده میکنیم و به عبارتی میتوانیم بگوییم در استفاده از این نوع داده وسواس داریم. قطعه کد زیر را در نظر بگیرید:
public class UserFactory
{
    public User CreateUser(string email) {
        return new User(email);
    }
}
کلاس UserFactory، یک متد به نام CreateUser دارد که یک رشته را به عنوان ورودی میگیرد و یک شیء از کلاس User را بر می‌گرداند. خوب مشکل این متد کجاست؟
اگر به خاطر داشته باشید، در قسمت‌های قبلی در مورد مفهومی به نام Honesty صحبت کردیم. به طور ساده باید بتوانیم از روی امضای تابع، کاری را که تابع انجام میدهد و خروجی آن را ببینیم. این تابع Honest نیست؛ شرایطی که string می‌تواند درست نباشد، خالی باشد، طول غیر مجاز داشته باشد و ... را نمیتوانیم از امضای تابع حدس بزنیم.

برای روشن‌تر شدن بحث، مثال بالا را همیشه در ذهن خود داشته باشید. در این مثال، در تابع Divide که عمل تقسیم را انجام می‌دهد، پارامتر y که یک عدد از نوع int است، میتواند مقدار صفر را داشته باشد و باعث یک exception شود.و از آنجائیکه نوع خروجی این متد هم int است، انتظار دریافت یک exception را نداریم. در مورد exception‌ها به طول مفصل در قسمت قبلی صحبت کردیم. در مثال بالا تصور کنید که بجای یک ایمیل، از چند ایمیل به عنوان ورودی می‌خواهید استفاده کنید. آیا منطق Validation را به ازای هر پارامتر ورودی باید تکرار کنید؟

به طور کلی استفاده‌ی نابجا و بیش از حد از نوع‌های داده‌ی اولیه، باعث می‌شود تا Honesty متد‌ها را از دست بدهیم و قاعده‌ی DRY را نقض کنیم.

صحبت در مورد استفاده کردن یا نکردن، جنبه‌های زیادی دارد و یکی از مواردی است که در معماری DDD تحت عنوان Value Object به آن پرداخته شده. هدف ما در این قسمت از مقاله، صرفا پرداختن به گوشه‌ای از این مورد هست. ولی شما میتوانید برای مطالعه بیشتر و اطلاعات تکمیلی کتاب Domain-Driven Design: Tackling Complexity in the Heart of Software نوشته Eric Evans را مطالعه کنید.


به جای نوع‌های اولیه از چی استفاده کنیم؟

جواب خیلی ساده‌است؛ شما نیاز دارید تا یک Type اختصاصی را ایجاد کنید. برای مثال بجای استفاده از نوع string برای یک ایمیل، می‌توانید یک کلاس را به عنوان Email ایجاد کنید که مشخصه‌ای به نام Value دارد. این کار به روش‌های مختلفی قابل انجام است؛ اما پیشنهاد من استفاده از این روش هست:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ValueOf
{
    public class ValueOf<TValue, TThis> where TThis : ValueOf<TValue, TThis>, new()
    {
        private static readonly Func<TThis> Factory;

        /// <summary>
        /// WARNING - THIS FEATURE IS EXPERIMENTAL. I may change it to do
        /// validation in a different way.
        /// Right now, override this method, and throw any exceptions you need to.
        /// Access this.Value to check the value
        /// </summary>
        protected virtual void Validate()
        {
        }

        static ValueOf()
        {
            ConstructorInfo ctor = typeof(TThis)
                .GetTypeInfo()
                .DeclaredConstructors
                .First();

            var argsExp = new Expression[0];
            NewExpression newExp = Expression.New(ctor, argsExp);
            LambdaExpression lambda = Expression.Lambda(typeof(Func<TThis>), newExp);

            Factory = (Func<TThis>)lambda.Compile();
        }

        public TValue Value { get; protected set; }

        public static TThis From(TValue item)
        {
            TThis x = Factory();
            x.Value = item;
            x.Validate();

            return x;
        }

        protected virtual bool Equals(ValueOf<TValue, TThis> other)
        {
            return EqualityComparer<TValue>.Default.Equals(Value, other.Value);
        }

        public override bool Equals(object obj)
        {
            if (obj is null)
                return false;

            if (ReferenceEquals(this, obj))
                return true;

            return obj.GetType() == GetType() && Equals((ValueOf<TValue, TThis>)obj);
        }

        public override int GetHashCode()
        {
            return EqualityComparer<TValue>.Default.GetHashCode(Value);
        }

        public static bool operator ==(ValueOf<TValue, TThis> a, ValueOf<TValue, TThis> b)
        {
            if (a is null && b is null)
                return true;

            if (a is null || b is null)
                return false;

            return a.Equals(b);
        }

        public static bool operator !=(ValueOf<TValue, TThis> a, ValueOf<TValue, TThis> b)
        {
            return !(a == b);
        }

        public override string ToString()
        {
            return Value.ToString();
        }
    }
}
در این روش، یک کلاس را به عنوان Value Object ایجاد کرده‌ایم. این کلاس، نوع اولیه‌ای را که با آن سر و کار داریم، در بر خواهد گرفت و منطق مربوط به مقایسه، همچنین عملگرهای == و != را هم از طریق Equals و GetHashCode، پیاده سازی کرده. برای مثال جهت کلاس ایمیل می‌توانیم به صورت زیر عمل کنیم:
public class EmailAddress : ValueOf<string, EmailAddress> { }
همچنین برای مقدار دهی این کلاس میتوانید به صورت زیر عمل کنید:
EmailAddress emailAddress = EmailAddress.From("foo@bar.com");
برای مثال‌های پیچیده‌تر مانند آدرس، که شامل آدرس، کد پستی و … می‌باشد، میتوانید با استفاده از امکان Tuple‌ها که از سی شارپ 7 به بعد معرفی شده، مانند مثال زیر عمل کنید:
public class Address : ValueOf<(string firstLine, string secondLine, Postcode postcode), Address> { }
و در نهایت برای نوشتن منطق مربوط به validation می‌توانید متد Validate را Override کنید و قاعده‌ی DRY را هم نقض نکنید.

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

در صورتیکه مشکلی در پیاده سازی داشتید، می‌توانید مشکل خود را زیر همین مطلب و یا بر روی gist آن کامنت کنید.
مطالب
اصول طراحی شیء گرا: OO Design Principles

قصد دارم مجموعه ای کامل از اصول طراحی شیء گرا، الگوهای طراحی و best practice ‌های مربوطه را ارائه دهم. از این رو ابتدا با اصول طراحی شروع می‌کنم. سپس در مقالات آتی به الگوهای مختلف خواهم پرداخت و تا جایی که معلومات اجازه دهد مشخص خواهم کرد که هر الگو متمرکز بر رعایت کدام یک از اصول است و اینکه آیا مناسب است از الگوی مورد نظر استفاده کنیم.

این مطالب نیز چکیده ای از آموزش‌های Lynda, Pluralsight , SkillFeed  و کتاب های Gang of four, Headfirst Design patterns  و ... میباشد


اصل اول: Encapsulate what varies

"آنچه را که تغییر می‌کند مشخص و جدا کن یا به عبارتی آنرا کپسوله کن"

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


اصل دوم: Program to an interface not implementation

" برنامه نویسی را متمرکز بر واسط کن، نه نحوه‌ی پیاده سازی "

برای این اصل تعابیر مختلفی ارائه شده است:

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

برای رعایت این اصل باید:

  • سعی بر تعریف واسط برای اکثر کلاس‌ها کنیم
  • اشیا را از نوع واسط تعریف کنیم، نه کلاس‌های پیاده ساز آن

در کد زیر نحوه‌ی تعریف واسط را برای کلاس و تعریف اشیاء از نوع واسط را میبینیم: 

    public interface IMyInterface
    {
        void DoWork();
    }

    public class MyImplementation1 : IMyInterface
    {
        public void DoWork()
        {
            var implementationName = this.GetType().ToString();
            Console.WriteLine("DoWork from " + implementationName);
        }
    }
    public class MyImplementation2 : IMyInterface
    {
        public void DoWork()
        {
            var implementationName = this.GetType().ToString();
            Console.WriteLine("DoWork from " + implementationName);
        }
    }

    public class Context
    {
        IMyInterface myInterface;

        public void Print() 
        {
            myInterface = new MyImplementation1();
            myInterface.DoWork();

            myInterface = new MyImplementation2();
            myInterface.DoWork();
        }
    }


اصل سوم: Favor composition over inheritance

"ترکیب را بر توارث ترجیح بده"

رابطه "دارد" (has a ) انعطاف بیشتری نسبت به ارث بری یا "از نوع ... هست" ( is a ) دارد. برای مثال فرض کنید کلاس Enemy رفتار Movable را دارد و این رفتار در طول بازی بر حسب موقعیتی تغییر میکند. اگر این رفتار را با توارث مدل کنیم، یعنی Enemy از نوع Movable باشد، آنگاه برای هر نوع رفتار Movable (هر نوع پیاده سازی) باید یک نوع Enemy داشته باشیم و تصور کنید بعضی از این پیاده سازی‌ها در کلاس Player یکسان باشد. آنگاه کد باید دوباره تکرار شود. حال تصور کنید این موقعیت را با ترکیب مدل کنیم. آنگاه کلاس Enemy یک شیء از جنس Movable خواهد داشت و در زمان نیاز میتواند نوع این رفتار را با نمونه گیری از کلاس‌های پیاده ساز آن، تغییر دهد. در واقع با اینکار اصل اول را رعایت کرده ایم و بخش ثابت را از بخش متغیر جدا نموده ایم.

در زیر مدلسازی با توارث را میبینیم: 

public interface Movable
    {
        void Move();
    }
    public class EnemyBase : Movable
    {
        // Enemy properties goes here
        protected int x, y;
        public virtual void Move()
        {
            x += 2;
            y += 2;
        }
    }
    public class EnemyWithMoveType2 : EnemyBase
    {
        // override the move method
        public override void Move()
        {
            x += 4;
            y += 4;
        }
    }
    public class EnemyWithMoveType3 : EnemyBase
    {
        // override the move method
        public override void Move()
        {
            x += 6;
            y += 6;
        }
    }
    public class PlayerBase : Movable
    {
        // Player properties goes here
        protected int x, y;
        public  virtual void Move()
        {
            // same code as EnemyBase move method
            x += 2;
            y += 2;
        }
    }
    public class PlayerWithMoveType2 : PlayerBase
    {
        // override the move method
        public override void Move()
        {
            // same code as EnemyWithMoveType2 move method
            x += 4;
            y += 4;
        }
    }
    public class PlayerWithMoveType3 : PlayerBase
    {
        // override the move method
        public override void Move()
        {
            // same code as EnemyWithMoveType3 move method
            x += 6;
            y += 6;
        }
    }  

در ادامه نیز مدلسازی با ترکیب را میبینیم: 

     public interface IMovable
    {
        void Move(ref int x, ref int y);
    }
    public class EnemyBase
    {
        // Enemy properties goes here
        protected int x, y;
        IMovable moveBehavior;
        public void SetMoveBehavior(IMovable _moveBehavior) { moveBehavior = _moveBehavior; }
        public void Move()
        {
            moveBehavior.Move(ref x,ref y);
        }
    }
    public class PlayerBase
    {
        // Player properties goes here
        protected int x, y;
        IMovable moveBehavior;
        public void SetMoveBehavior(IMovable _moveBehavior) { moveBehavior = _moveBehavior; }
        public void Move()
        {
            moveBehavior.Move(ref x, ref y);
        }
    }
    public class MoveBehavior1
    {
        public void Move(ref int x, ref int y)
        {
            x += 2;
            y += 2;
        }
    }
    public class MoveBehavior2 : IMovable
    {
        public void Move(ref int x, ref int y)
        {
            x += 4;
            y += 4;
        }
    }
    public class MoveBehavior3 : IMovable
    {
        public void Move(ref int x, ref int y)
        { 
            x += 6;
            y += 6;
        }
    }  

همانطور که میبینید، با فراخوانی تابع SetMoveBehavior میتوان رفتار را در زمان اجرا تغییر داد.

در مقاله‌ی بعدی به ادامه‌ی اصول خواهم پرداخت. از شنیدن نظرات و سوالات شما خرسند خواهم شد.

مطالب
طراحی گزارش در Stimulsoft Reports.Net – بخش 1

برای طراحی گزارش شما میتوانید به سه روش این کار را انجام دهید.

1- طراحی در برنامه طراح گزارش

2- طراحی از داخل ویژوال استودیو

3- طراحی گزارش در زمان اجرا

برای شروع شما میتوانید نسخه آزمایشی این گزارش‌ساز را دریافت کنید. تنها محدودیت این نسخه نمایش عبارت Demo در چاپ میباشد.

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

Header, Footer, Data, Page Header, Page Footer, Report Title, Report Summery

*به ابزارهای بالا Band گفته میشود.

Header , Footer :

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

Data :

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

Page Header , Page Footer :

این دو بند با توجه به نامشان جهت نمایش در بالا و پایین هر صفحه از گزارش میباشد. البته باز هم یادآور میشوم که با تغییر در خصوصیات‌شان میتوانید رفتار و نحوه نمایش آنها را تغییر دهید.

Report Title :

این بند فقط در ابتدای گزارش نمایش داده خواهد شد.

Report Summery :

این بند بلافاصله بعد از اتمام گزارش نمایش داده خواهد شد.

مثال :

برای شروع در Designer یک گزارش جدید از نوع Blank Report ایجاد نمایید. سپس در پنل Dictionary بر روی New Item کلیک کرده و گزینه XML Data را انتخاب نمایید. با توجه به محل نصب گزارش‌ساز وارد مسیر …\Bin\Data شده و فایلهای Demo.xsd و Demo.xml را برای قسمتهای مربوطه انتخاب نمایید. یک بار دیگر بر رو New Item کلیک کرده و گزینه New Data Source را انتخاب نمایید، از لیست ظاهر شده کانکشنی را که ایجاد کرده‌اید را انتخاب نمایید؛ نتیجه کار تا اینجا باید به صورت زبر باشد.

جدول Product را دراگ کرده و بر روی صفحه طراحی گزارش رها کنید. فرم Data ظاهر میشود این فرم را مطابق تصویر زیر تنظیم نمایید.

حال بر روی صفحه طراحی گزارش بندهای Header, Data, Footer مشاهده میشود؛ حال شما میتوانید با کلیک بر روی سربرگ Preview خروجی گزارش را ببینید.

توابع :

این گزارش‌ساز دارای توابع بسیاری است که اکثر نیازهای شما را برطرف می‌کند به عنوان مثال تابع تبدیل عدد به حروف به زبان فارسی. همچنین شما میتوانید توابع خاص خود را ساخته و به صورت رفرنس به گزارش اضافه نمایید.

در این بخش ما از توابع موجود در گزارش استفاده خواهیم کرد. برای شروع بر روی کامپوننت Text در بند Footer  زیر ستون UnitPrice دابل کلیک کرده تا فرم TextEditor ظاهر شود. سربرگ Summery را انتخاب نمایید. مطابق اطلاعات زیر بخشها را تنظیم نمایید.

Summery Function: Sum

Data Band: DataProducts

Data Column: Products.UnitPrice

حال بر روی سربرگ Preview کلیک نمایید تا خروجی گزارش را ببینید. جمع ستون UnitPrice فقط در صفحه آخر نمایش داده خواهد شد. اگر بخواهید جمع ستون در پایین هر صفحه نمایش داده شود ابتدا باید خصوصیت Print on All Pages بند Footer به True ست شود. سپس بر روی کامپوننت Text در بند Footer، دابل کلیک نمایید و در فرم TextEditor سربرگ Summery تیک Running Total را به حالت انتخاب شده در بیاورید، حال خروجی گزارش را ببینید، جمع در انتهای هر صفحه ظاهر میشود.

متغیرها :

در این گزارش ساز دو نوع متغیر وجود دارد؛ نرمال و سیستمی. نوع سیستمی شامل متغیرهایی میشود که کاربرد مشخصی در تهیه گزارش دارند، مثل شماره صفحه، شماره ردیف، عنوان گزارش و ...

برای مثال شما میتوانید متغیر سیستمی Line را برای روی صفحه طراحی دراگ کنید. دو کامپوننت Text بر روی صفحه ایجاد میشود. اولی با محتوای Line و دومی با محتوای {Line}. اولی را به بند Header و دومی را به بند Data منتقل کنید و سپس خروجی گزارش را مشاهده نمایید، حال گزارش شما دارای شماره ردیف است.

متغیرهای نرمال تقریبا همانند متغیرهایی هستند که همه روزه شما در برنامه‌های خود از آنها استفاده می‌کنید. با کلیک بر روی New Item گزینه New Variable را انتخاب نمایید و نوع متغیر را Decimal انتخاب نمایید، سپس متغیر ایجاد شده را دراگ کرده و بروی صفحه طراحی قرار دهید و مشابه متغیر Line عمل کرده و کامپوننتهای Text را در بندهای مناسب قرار دهید. سپس بند Data بر روی صفحه طراحی را انتخاب نمایید، در پنل Properties بر روی Eventes کلیک کرده سپس در رویداد Rendering کد زیر را وارد نمایید.

Variable1 += Products.UnitPrice

حال در خروجی گزارش میتوانید مقادیر محاسبه شده را ببینید. توجه داشته باشید که شما میتوانید در رویدادهای این گزارش‌ساز به زبان VB و C# برنامه نویسی کنید و محدود به یک خط کد نمی‌باشید.

شما میتوانید گزارش ساخته شده را به صورتهای مختلف ذخیره کنید از جمله کد C# و یا یک اپلیکیشن قابل اجرا. 

Report.mrt 

مطالب
یکپارچه کردن ELMAH با WCF RIA Services

پیشتر در مورد ELMAH مطلبی را منتشر کرده بودم و اگر برنامه نویس ASP.NET هستید و با ELMAH آشنایی ندارید،‌ جدا نیمی از عمر کاری شما بر فنا است!
هاست پیش فرض یک WCF RIA Service هم یک برنامه‌ی ASP.NET است. بنابراین کلیه‌ی خطاهای رخ داده در سمت سرور را باید بتوان به نحوی لاگ کرد تا بعدا با مطالعه‌ی آن‌ها اطلاعات ارزشمندی را از نقایص برنامه در عمل و پیش از گوشزد شدن آن‌ها توسط کاربران، دریافت، بررسی و رفع کرد.
کلیه خطاها را لاگ می‌کنم تا:
- بدانم معنای جمله‌ی "برنامه کار نمی‌کنه" چی هست.
- بدون روبرو شدن با کاربران یا حتی سؤال و جوابی از آن‌ها بدانم دقیقا مشکل از کجا ناشی شده.
- بدانم رفتارهای عمومی کاربران که منجر به بروز خطا می‌شوند کدام‌ها هستند.
- بدانم در کدامیک از قسمت‌های برنامه تعیین اعتبار ورودی کاربران یا انجام نشده یا ضعیف و ناکافی است.
- بدانم زمانیکه دوستی (!) قصد پایین آوردن برنامه را با تزریق SQL داشته، دقیقا چه چیزی را وارد کرده، در کجا و چه زمانی؟
- بتوانم Remote worker خوبی باشم.

ELMAH هم برای لاگ کردن خطاهای مدیریت نشده‌ی یک برنامه‌ی ASP.NET ایجاد شده است. بنابراین باید بتوان این دو (WCF RIA Services و ELMAH) را به نحوی با هم سازگار کرد. برای اینکار نیاز است تا یک مدیریت کننده‌ی خطای سفارشی را با پیاده سازی اینترفیس IErrorHandler تهیه کنیم (تا خطاهای مدیریت نشده‌ی حاصل را به سمت ELMAH هدایت کند) و سپس آن‌را به کمک یک ویژگی یا Attribute به DomainService خود جهت لاگ کردن خطاها اعمال نمائیم. روش تعریف این Attribute را در کدهای بعد ملاحظه خواهید نمود (در اینجا نیاز است تا دو ارجاع را به اسمبلی‌های Elmah.dll که دریافت کرده‌اید و اسمبلی استاندارد System.ServiceModel نیز به پروژه اضافه نمائید):

//add a reference to "Elmah.dll"
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.Web;

namespace ElmahWcf
{
public class HttpErrorHandler : IErrorHandler
{
#region IErrorHandler Members
public bool HandleError(Exception error)
{
return false;
}

public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error == null)
return;

if (HttpContext.Current == null) //In case we run outside of IIS
return;

Elmah.ErrorSignal.FromCurrentContext().Raise(error);
}
#endregion
}
}

//add a ref to "System.ServiceModel" assembly
using System;
using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace ElmahWcf
{
public class ServiceErrorBehaviorAttribute : Attribute, IServiceBehavior
{
Type errorHandlerType;
public ServiceErrorBehaviorAttribute(Type errorHandlerType)
{
this.errorHandlerType = errorHandlerType;
}

#region IServiceBehavior Members

public void AddBindingParameters(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints,
BindingParameterCollection bindingParameters)
{ }

public void ApplyDispatchBehavior(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandler;
errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher cd = cdb as ChannelDispatcher;
cd.ErrorHandlers.Add(errorHandler);
}
}

public void Validate(
ServiceDescription serviceDescription,
ServiceHostBase serviceHostBase)
{ }
#endregion
}
}
اکنون پس از تعریف ویژگی ServiceErrorBehavior، نوبت به اعمال آن می‌رسد. به فایل DomainService خود مراجعه کرده و یک سطر زیر را به آن اضافه نمائید:
    [ServiceErrorBehavior(typeof(HttpErrorHandler))] //Integrating with ELMAH
[EnableClientAccess()]
public partial class MyDomainService : LinqToEntitiesDomainService<myEntities>

در ادامه نحوه‌ی افزودن تعاریف متناظر با ELMAH به Web.Config برنامه ذکر شده است. این تعاریف برای IIS6 و 7 به بعد هم تکمیل گردیده است. خطاها هم به صورت فایل‌های XML در پوشه‌ای به نام Errors که به ریشه‌ی سایت اضافه خواهید نمود (یا هر پوشه‌ی دلخواه دیگری)، لاگ می‌شوند.
به نظر من این روش، از ذخیره سازی اطلاعات لاگ‌ها در دیتابیس بهتر است. چون اساسا زمانیکه خطایی رخ می‌دهد شاید مشکل اصلی همان ارتباط با دیتابیس باشد.
قسمت ارسال خطاها به صورت ایمیل نیز comment شده است که در صورت نیاز می‌توان آن‌را فعال نمود:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<sectionGroup name="elmah">
<section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah"/>
<section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
<section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
<section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah"/>
<section name="errorTweet" requirePermission="false" type="Elmah.ErrorTweetSectionHandler, Elmah"/>
</sectionGroup>
</configSections>

<elmah>
<security allowRemoteAccess="1" />
<errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/Errors" />
<!-- <errorMail
from="errors@site.net"
to="nasiri@site.net"
subject="prj-error"
async="true"
smtpPort="25"
smtpServer="mail.site.net"
noYsod="true" /> -->
</elmah>

<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
<add name="DomainServiceModule"
preCondition="managedHandler"
type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</modules>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<add name="Elmah" verb="POST,GET,HEAD" path="myelmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</handlers>
</system.webServer>
<system.web>
<globalization
requestEncoding="utf-8"
responseEncoding="utf-8"
/>
<authentication mode="Forms">
<!--one month ticket-->
<forms name=".403AuthV"
cookieless="UseCookies"
slidingExpiration="true"
protection="All"
path="/"
timeout="43200" />
</authentication>
<httpHandlers>
<add verb="POST,GET,HEAD" path="myelmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah"/>
<add name="DomainServiceModule"
type="System.ServiceModel.DomainServices.Hosting.DomainServiceHttpModule, System.ServiceModel.DomainServices.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
</httpModules>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</assemblies>
</compilation>
</system.web>
<connectionStrings>
</connectionStrings>
<system.serviceModel>
<serviceHostingEnvironment
aspNetCompatibilityEnabled="true"
multipleSiteBindingsEnabled="true" />
</system.serviceModel>
</configuration>
اکنون برای مثال به یکی از متدهای DomainService خود سطر زیر را اضافه کرده و برنامه را آزمایش کنید:
throw new Exception("This is an ELMAH test");

سپس به آدرس http://localhost/myelmah.axd مراجعه نموده و اطلاعات لاگ شده حاصل را بررسی کنید:


این روش با WCF Services های متداول هم کار می‌کند. فقط در این سرویس‌ها باید aspNetCompatibilityEnabled مطابق تگ‌های ذکر شده‌ی system.serviceModel فوق در web.config لحاظ شوند (این مورد به صورت پیش فرض در WCF RIA Services وجود دارد). همچنین ویژگی زیر نیز باید به سرویس شما اضافه گردد:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

منابع مورد استفاده:
Integrating ELMAH for a WCF Service
Making WCF and ELMAH play nice together
Getting ELMAH to work with WCF services



پ.ن.
اگر به خطاهای ASP.NET دقت کرده باشید که به yellow screen of death هم مشهور هستند (در مقابل صفحات آبی ویندوز!)، ابتدای آن خیلی بزرگ نوشته شده Server Error و سپس ادامه‌ی خطا. همین مورد دقیقا یادم هست که هر بار سبب بازخواست مدیران شبکه بجای برنامه نویس‌ها می‌شد! (احتمالا این هم یک نوع بدجنسی تیم ASP.NET برای گرفتن حال ادمین‌های شبکه است! و گرنه مثلا می‌توانستند همان ابتدا بنویسند program/application error بجای server error)

بازخوردهای پروژه‌ها
درخواست راهنمایی بیشتر
با سلام، خیلی ممنون که سورس این وب اپلیکیشن را در اختیار کاربران قرار داده اید؛
بنده با مطالعه (بخشی از) این مطالب: 
مسیر راه Entity framework code-first ، 
مسیر راه ASP.NET MVC ،
بررسی مفاهیم معکوس سازی وابستگی‌ها و ابزارهای مرتبط با آن ،
AutoMapper ،
چک لیست تهیه یک برنامه ASP.NET MVC ،
تا حد زیاد نحوه پیاده سازی back end پروژه را درک کردم (البته ابتدا پروژه فروشگاه اینترنتی شهر طلایی من که ساده‌تر است را بررسی کردم).
با این وجود بخش هایی از پروژه از نظر بنده همچنان پیچیده است!
- مدیریت کاربران، نقش‌ها و تعیین دسترسی ها؛
- نحوه لاگ کردن فعالیت ها؛
- ارسال و دریافت پیام؛
- نحوه پیاده‌سازی front end پروژه (به طور خاص)،
لطفا در صورت امکان در این موارد (به ویژه مورد آخر طراحی UI) اگر منبعی یا مسیر راهی وجود دارد، معرفی نمایید.
با تشکر،