مطالب
پیاده سازی CQRS توسط MediatR - قسمت اول
در مطالب قبلی (1 , 2) الگوی CQRS معرفی شد. همانطور که می‌بینید، پیاده سازی این الگو هرچند با فریمورک آماده‌ای همچون SimpleCQRS، دارای پیچیدگی زیادی است و باعث نوشتن حجم زیادی کد می‌شود.

فریمورک MediatR توسط توسعه دهنده کتابخانه‌ی محبوب AutoMapper ایجاد شده‌است. این فریمورک پیاده سازی کاملی از الگوی طراحی Mediator در NET. است که داخل خود، تمام پیچیدگی‌های پیاده سازی CQRS را Abstract کرده و با حداقل کد ممکن، می‌توانید به‌راحتی CQRS را داخل پروژه‌ی خود پیاده سازی کنید.

در این سری مطالب به بررسی کامل الگوی CQRS و مزایا و معایب استفاده از آن می‌پردازیم و سپس با استفاده از کتابخانه‌ی Mediatr، این الگو را داخل یک پروژه پیاده سازی می‌کنیم.

CQRS

در CQRS متد‌های برنامه به 2 بخش Read و Write تقسیم می‌شوند. بخش‌هایی که State کلی برنامه ( شامل Database, Cookie, Session, LocalStorage, Memory و ... ) را تغییر می‌دهند، Command و بخش‌هایی که صرفا جنبه خواندنی دارند و وضعیت سیستم را تغییر نمی‌دهند مثل خواندن و نشان دادن اطلاعات از دیتابیس، Query می‌نامند.

* نکته : Naming Convention مورد استفاده برای Command‌‌ها به صورت دستوری است و کار Command در نام آن مشخص است؛ مثال : RegisterUser, SendForgottenPasswordEmail, PlaceOrder

مزایا:
1- شما می‌توانید تکنولوژی‌های مورد استفاده‌ی در بخش‌های Command و Query برنامه‌ی خود را به‌راحتی از هم جدا سازید. به‌عنوان مثال Apache Cassandra در ذخیره سازی داده‌ها ( Write Side ) به عنوان یک دیتابیس قابل اعتنا شناخته میشود و از طرفی دیگر ElasticSearch بدلیل سرعت فوق العاده‌ی خود، برای خواندن داده‌ها استفاده میشود. در این روش، دیتابیس‌ها باید Sync باشند تا داده‌های به‌روز به کاربر نمایش داده شود که این موضوع چالش‌های خود همچون Eventual Consistency و Strong Consistency را دارد که در مقالات بعدی آن‌ها را بررسی خواهیم کرد.

2- در برنامه‌های معمول، اکثرا بخش Read Side، بیشتر از Write Side استفاده می‌شود و کاربران معمولا اطلاعات را دریافت و می‌بینند تا اینکه در آن تغییری ایجاد کنند؛ در این صورت شما می‌توانید بخش Read برنامه‌ی خود را Scale کرده و تعداد سیستم یا منابع بیشتری را به این قسمت از برنامه‌ی خود اختصاص دهید ( Horizontal Scaling, Vertical Scaling ). 

3- این جداسازی باعث تمرکز بیشتر شما بر روی قسمت‌های مختلف برنامه می‌شود؛ بخش‌هایی که وضعیت سیستم را تغییر می‌دهند از بخش‌هایی که صرفا داده‌هایی را خوانده و نمایش می‌دهند، بطور کامل جدا شده‌اند و به‌راحتی قابلیت تغییر هرکدام از این بخش‌ها را خواهید داشت.

معایب : معمولا از معایب این الگو، از پیچیدگی پیاده سازی آن یاد می‌شود که در این آموزش با استفاده از Mediatr سعی بر از بین بردن این پیچیدگی را داریم.

Events

Event‌ها رویدادهایی هستند که خبر انجام کاری را که قبلا داخل سیستم انجامش به پایان رسیده است، به Consumer‌های خود می‌دهند. بعنوان مثال می‌خواهیم بعد از ثبت نام موفق یک کاربر داخل سیستم، Notification و یا ایمیلی را به او ارسال کنیم. بعد از ثبت نام کاربر میتوانیم Event ای به نام UserRegistered را که شامل Username و Email کاربر در بدنه خود است، Raise کنیم.

Event‌ها می‌توانند چندین Consumer داشته باشند؛ بنابراین می‌توانیم یک EventHandler را برای UserRegistered بنویسیم که Email ارسال کند و EventHandler دیگری ایجاد کنیم که Notification ای را برای کاربر بفرستد.

* نکته : Naming Convention مورد استفاده برای Event‌ها به صورت گذشته‌است و خبر یک کار، که قبلا انجام شده است را می‌دهد؛ مثال : UserRegistered, OrderPlaced

Event Sourcing

Event Sourcing به معنای ذخیره‌ی تمام Event‌های رخ داده در برنامه داخل یک دیتابیس Append-Only است. در این نوع دیتابیس‌ها فقط میتوانیم Event‌های جدیدی به آن اضافه کنیم و قادر به ویرایش و حذف Event‌ها نیستیم؛ چون منطق Event، کارهایی است که در گذشته اتفاق افتاده‌اند و ما قادر به تغییر چیزی که در گذشته رخ داده‌است، نیستیم.

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

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

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

* در این سری آموزشی از دیتابیس  Event Store برای پیاده سازی Event Sourcing استفاده خواهیم کرد.

در مقاله‌ی بعدی، امکانات فریمورک MediatR را بررسی خواهیم کرد.
مطالب
فایل آپلود در AngularJs
در این بخش قصد داریم تا به چگونگی آپلود فایل در AngularJs بپردازیم. پلاگین‌های فراوانی جهت آپلود فایل وجود دارند، از جمله ng-file-Upload ، angular-file-upload و jquery-file-upload. البته نمونه‌ی ذکر شده‌ی آخر، یکی از پلاگین‌های معروف JQuery است که با AngularJs هم سازگار شده است.
ما قصد داریم تا در این مجموعه از ng-file-upload که توسط Danial Farid  توسعه داده شده است، استفاده نماییم. این پلاگین شامل یک دایرکتیو کم حجم برای مدیریت آپلود فایل سمت کلاینت است و با تمامی تکنولوژی‌های سمت سرور سازگار می‌باشد. صفحه‌ی دمو پلاگین، شامل برخی از امکانات ذکر شده می‌باشد که قرار است به توسعه و بحث پیرامون آن بپردازیم. این مجموعه دارای اسناد معتبری است که می‌توانید از آن برای توسعه‌ی هر گونه آپلود فایل استفاده نمایید. نسخه‌ی فعلی، نسخه‌ی منتشر شده‌ی 3.0 است. نسخه‌های قبلی این پلاگین نیز در سایت GitHub موجود است.

ویژگی ها

در بخش زیر ویژگی‌های این افزونه که در اسناد آن نیز موجود است، شرح داده شده است:
  • امکان ایجاد progress bar و همچنین قابلیت لغو آپلود.
  • Drag & Drop
  • امکان paste کردن از Clipboard و قابلیت drag & drop از browser
  • امکان resize کردن تصویر قبل از آپلود شدن
  • امکان تغییر دادن orientation تصویر پیش از ارسال
  • قابلیت resume و pause/play در آپلود
  • امکان فیلتر کردن type، size و طول و عرض تصاویر
  • قابلیت تبدیل به Flash shim در صورت عدم پشتیبانی html5 توسط مرورگر

نصب و پیاده سازی

برای استفاده‌ی از پلاگین file upload لازم است که دو مجموعه‌ی زیر را توسط Nuget یا Bower دریافت نمایید. پلاگین اول مربوط به مرورگرهایی است که از html5 پشتیبانی نمی‌کنند.
bower install ng-file-upload-shim --save
bower install ng-file-upload --save
پس از اینکه دو کامپوننت فوق را نصب نمودید، دو فایل اسکریپت دریافت شده را به فایل main html اضافه نمایید. پس آن شروع به نوشتن یک مثال ساده می‌کنیم.
این مثال در JSFIDDLE قرار داده شده است، به این صورت که در این مثال یک فایل آپلود ساده در قالب یک فرم به سرور ارسال می‌گردد. امکانات خیلی ساده نظیر نمایش فایل، پیش از آپلود نیز در این قسمت قرار داده شده است.
به عنوان اولین گام به کد زیر توجه کنید:
<input type="file" ngf-select ng-model="picFile" name="file" accept="image/*" ngf-max-size="2MB" required>
تکه کد فوق مربوط به تگ آپلود فایل است که به توضیح آن خواهیم پرداخت. ngf-select Attribute دایرکتیوی برای تبدیل فایل آپلود، به ng-file-upload است. شما به طور کلی به دو صورت قادر هستید که این کار را انجام دهید. مورد اول ngf-select بود و مورد بعدی ngf-drop است. ngf-drop به گونه‌ای دیگر نوشته می‌شود و کد زیر یک مورد استفاده‌ی از ngf-drop می‌باشد.
<div ngf-drop ng-model="picFile" ngf-pattern="image/*">
     ...
</div>
همانطور که ملاحظه می‌کنید در این نوع فراخوانی، یک تگ div به یک file select مبدل شده که در مورد آن صحبت خواهیم نمود. همانطور که مشاهده می‌کنید، شما قادرید تا با استفاده از Attribute ایی با نام accept، فایل‌های ارسالی را از لحاظ محتوایی فیلتر کنید. در کد فوق image/* این مفهوم را می‌رساند که شما قادرید فایل‌های تصویری و با هر فرمتی را به سرور ارسال نمایید. اما اگر آن را به صورت image/jpg بنویسیم این عملیات تنها به تصاویر با فرمت jpeg منتهی می‌شود. مورد بعدی محدودیت حجمی تصویر است. شما با ngf-max-size و مقداری که درون آن قرار می‌دهید، قادرید تا محدودیت حجمی را بر روی فایل‌های ارسالی به سرور اعمال نمایید. اگر با نوشتار form validationها در Angular آشنا باشید، به سادگی قادرید تا خطاهایی را که کاربر ممکن است در حین آپلود انجام دهد، به او گوشزد کنید. به کد زیر توجه کنید:
<i ng-show="myForm.file.$error.required">*required</i><br>
<i ng-show="myForm.file.$error.maxSize">
File too large: max is 2M
</i>
همانطور که مشاهده می‌کنید syntax نمایش خطاها دقیقا مانند نمایش خطاها در validation فرم‌های انگیولاری است.
و اما اینکه چه کارهایی نیاز است تا در سمت Controller انجام دهیم. اول اینکه لازم است شما dependency ngFileUpload زیر را به app.module اضافه نمایید. حال اینکه پس از inject کردن این وابستگی، تنها کافیست که وابستگی Upload را به کنترلر تزریق نمایید. این وابستگی وظیفه‌ی ارسال فایل به سرور را بر عهده دارد. به مثال زیر توجه نمایید:
app.controller('MyCtrl', ['$scope', 'Upload', '$timeout', function ($scope, Upload, $timeout) {
    // the method uploadPic called from Angular View
$scope.uploadPic = function(file) {
    file.upload = Upload.upload({
      url: 'https://angular-file-upload-cors-srv.appspot.com/upload',
      data: {file: file, otherData: $scope.otherData},
    });

    file.upload.then(function (response) {
// waiting for response
        $timeout(function () {
file.result = response.data;
        });
    }, function (response) {
//error
        if (response.status > 0)
$scope.errorMsg = response.status + ': ' + response.data;
    }, function (evt) {
//progress
        file.progress = Math.min(100, parseInt(100.0 * evt.loaded / evt.total));
console.log(file.progress);
    });
    }
}]);
تمام عملیات در قالب یک فراخواننده‌ی ng-click از سوی view آغاز شده‌است. با این رخداد، متد uploadPic که همراه آن فایل attach شده نیز به عنوان پارامتر ارسال می‌گردد، فرخوانی می‌شود و کلیه‌ی کارها طی عملیات post، توسط وابستگی Upload برای سرور ارسال می‌گردد. همانند http$ درون بلاک then سه متد Success ، Error و progress پیاده‌سازی شده است. البته اگر به فرایند مقداردهی Url و data توجه کنید می‌بینید که فایل‌ها نیز درون data قرار گرفته‌اند. شما برای ارسال فایل‌ها می‌توانید از وابستگی upload$ نیز استفاده کنید که مثالی هم در این زمینه در mono-software.com پیاده‌سازی شده است که از ng-file-upload و در سمت سرور، از ASP.NET Web API استفاده می‌کند. در این مثال چندین فایل به صورت غیر همزمان برای سرور ارسال می‌گردند.
توحه داشته باشید که شما برای uploadهای ساده، قادر هستید تا دایرکتیو خاص خود را نوشته و از آن استفاده نمایید. در msdn نمونه‌ای از این کار، یعنی نوشتن دایرکتیو فایل آپلود، به طور کامل پیاده‌سازی شده است. اما مزیت ng-file-upload این است که این پلاگین، با بسیاری از پلاگین‌های 3rd party دیگر مانند ng-image-crop به خوبی همگام شده‌است. مثالی از آن را می‌توانید در اینجا مشاهده نمایید.
در این مقاله، هدف کلی، چگونگی فایل آپلود در AngularJs و پس از آن معرفی و بررسی ویژگی‌ها و همچنین تشریح عملکرد ng-file-upload بود. این افزونه قابلیت‌های فراوان دیگری نیز دارد که بنابر نیاز می‌توانید در کد‌های خود از آن‌ها استفاده نمایید.
مطالب
آشنایی با Refactoring - قسمت 6

در ادامه بحث «حذف کدهای تکراری»، روش Refactoring دیگری به نام "Extract Superclass" وجود دارد که البته در بین برنامه نویس‌های دات نت به نام Base class بیشتر مشهور است تا Superclass. هدف آن هم انتقال کدهای تکراری بین چند کلاس، به یک کلاس پایه و سپس ارث بری از آن می‌باشد.

یک مثال:
در WPF و Silverlight جهت مطلع سازی رابط کاربری از تغییرات حاصل شده در مقادیر داده‌ها، نیاز است کلاس مورد نظر، اینترفیس INotifyPropertyChanged را پیاده سازی کند:

using System.ComponentModel;

namespace Refactoring.Day6.ExtractSuperclass.Before
{
public class User : INotifyPropertyChanged
{
string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
raisePropertyChanged("Name");
}
}

public event PropertyChangedEventHandler PropertyChanged;
void raisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}


و نکته‌ی مهم این است که اگر 100 کلاس هم داشته باشید، باید این کدهای تکراری اجباری مرتبط با raisePropertyChanged را در آن‌ها قرار دهید. به همین جهت مرسوم است برای کاهش حجم کدهای تکرای، قسمت‌های تکراری کد فوق را در یک کلاس پایه قرار می‌دهند:

using System.ComponentModel;

namespace Refactoring.Day6.ExtractSuperclass.After
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler == null) return;
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

و سپس از آن ارث بری می‌کنند:

namespace Refactoring.Day6.ExtractSuperclass.After
{
public class User : ViewModelBase
{
string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value) return;
_name = value;
RaisePropertyChanged("Name");
}
}
}
}


به این ترتیب این کلاس پایه در ده‌ها و صدها کلاس قابل استفاده خواهد بود، بدون اینکه مجبور شویم مرتبا یک سری کد تکراری «اجباری» را copy/paste کنیم.

مثالی دیگر:
اگر با ORM های Code first کار کنید، نیاز است تا ابتدا طراحی کار توسط کلاس‌های ساده دات نتی انجام شود؛ که اصطلاحا به آن‌ها POCO یا Plain old CLR objects یا Plain old .NET Classes هم گفته می‌شود. در بین این کلاس‌ها، متداول است که یک سری از خصوصیات، تکراری و مشترک باشد؛ مثلا تمام کلاس‌ها تاریخ ثبت رکورد را هم داشته باشند به همراه نام کاربر و مشخصاتی از این دست. اینجا هم برای حذف کدهای تکراری، یک Base class طراحی می‌شود: (+)

نظرات مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 13 - معرفی View Components
نکته تکمیلی :
ایجاد منوهای چند سطحی با استفاده از ViewComponent تا N سطح
کلاس Entity :
public class Navbar
    {
        public int Id { get; set; }
        public string Title { get; set; }

        public int? ParentId { get; set; }
        public virtual Navbar Parent { get; set; }
        public bool IsActive { get; set; }
        public bool HasChiled { get; set; }
        public bool IsMegaMenu { get; set; }
        public PageGroup PageGroup { get; set; }
        public string Url { get; set; }
        public bool OpenNewPage { get; set; }

        public virtual ICollection<Navbar> Children { get; set; }
    }
کلاس ViewComponent :
    public class TopNavbar : ViewComponent
    {
        private readonly DbSet<Navbar> _navbars;
        private readonly AppDbContext _dbContext;

        public TopNavbar(AppDbContext dbContext)
        {
            _dbContext = dbContext;
            _navbars = _dbContext.Set<Navbar>();
        }
        public async Task<IViewComponentResult> InvokeAsync()
        {
            var navbars = await _navbars.Include(p=>p.Parent).Include(x=>x.Children).OrderBy(x=>x.ParentId).ToListAsync();
            return View(viewName: "~/Views/Shared/Components/NavbarViewComponent/_Menu.cshtml", navbars);
        }
    }
فراخوانی viewcomponent در Layout.cshtml
 <ul class="menu">
                <li>
                    <a href="Index_demo6.html"><i class="menu_icon_wrapper fal fa-home-lg-alt"></i>صفحه اصلی</a>
                </li>
                @await Component.InvokeAsync("TopNavbar");                
 </ul>
Menu.cshtml_ :
@using TR.Context.Entities
@using Microsoft.AspNetCore.Html
@model IEnumerable<TR.Context.Entities.Navbar>


@foreach (var menu in Model.Where(x => x.Parent == null))
{

    <li class="@(menu.HasChiled ? "has_sub narrow" : "")">
        <a href="#">@menu.Title</a>
        @if (menu.HasChiled)
        {
            <div class="second">
                <div class="inner">
                    <ul>
                        @foreach (var menuChild in menu.Children)
                        {
                        <partial name="~/Views/Shared/Components/NavbarViewComponent/_SubMenu.cshtml" model="menuChild" />
                        }
                    </ul>
                </div>
            </div>
        }
    </li>
}
پارشیال ویو SubMenu.cshtml_
@model TR_.Context.Entities.Navbar

<li class="@(Model.HasChiled ? "sub":"")">
    <a href="#">
        @if (Model.Children.Any())
        {<i class="q_menu_arrow fal fa-angle-left"></i>}
        @Model.Title
    </a>
    @if (Model.Children.Any())
    {
        <ul>
            @foreach (var menuChild in Model.Children)
            {
                <partial name="~/Views/Shared/Components/NavbarViewComponent/_SubMenu.cshtml" model="menuChild" />
            }
        </ul>
    }
</li>
با این خروجی :

مطالب
آشنایی با WPF قسمت پنجم : DataContext بخش اول
یکی از مهمترین قسمت‌های برنامه، کار با داده‌های بانک اطلاعاتی (یا در کل منابع اطلاعاتی) است. اینکه چگونه با آن‌ها ارتباط برقرار کنیم و آن‌ها را در یک قالب کاربر پسند به کاربران برنامه نشان دهیم. افزودن شیء DataContext و مفاهیمی چون DataBinding باعث ارتباط سریع‌تر و راحت‌تری با منبع داده‌ها شده است. همچنین این قابلیت وجود دارد که هر گونه به روز آوری در اطلاعات دریافت شده، شما را با خبر سازد تا بتوانید طبق آن چه که می‌خواهید اطلاعات نمایشی را به روز کنید. در این مقاله به نحوه‌ی ارتباط بین منبع داده با DataContext و سپس کنترل‌هایی را چون Grid و ListBox و ... در رابطه با این منابع داده بررسی می‌کنیم.

در مورد بررسی ارتباط با داده‌ها در WPF باید سه مورد را بشناسیم:

  • DataContext: این شیء اتصالش را به منبع داده‌ها برقرار کرده و هر موقع داده‌ای را نیاز داریم، از طریق این شیء تامین می‌شود.
  • DataBinding: یک واسطه بین DataContext و هر آن چیزی است که قرار است از داده‌ها تغذیه کند. در تعریفی رسمی‌تر می‌گوییم: روشی ساده و قدرتمند بوده و واسطی است بین مدل تجاری و رابط کاربری. هر زمانی که داده‌ای تغییر کند، ما را آگاه می‌سازد که می‌تواند یک ارتباط یک طرفه یا دو طرفه باشد.
  • DataTemplate: نحوه‌ی فرمت بندی و نمایش داده‌ها را تعیین می‌کند.
ابتدا کار را با یک مثال ساده آغاز می‌کنیم. قصد داریم فرمی را که در قسمت قبلی ساختیم، با استفاده از یک منبع داده پر کنیم:
ابتدا قبل از هر چیزی کلاس فرم قبلی را پیاده سازی می‌کنیم. در این پیاده سازی از یک enum برای انتخاب زمینه‌های کاری هم کمک گرفته ایم و هچنین با یک متد ایستا، منبع داده‌ی تک رکوردی را جهت تست برنامه آماده کرده‌ایم:
   public enum FieldOfWork
    {
        Actor=0,
        Director=1,
        Producer=2
    }
    public class Person
    {
        public string Name { get; set; }

        public bool Gender { get; set; }

        public string ImageName { get; set; }

        public string Country { get; set; }

        public DateTime Date { get; set; }

        public IList<FieldOfWork> FieldOfWork { get; set; }
        public static Person GetPerson()
        {
            return new Person()
            {
                Name = "Leo",
                Gender = true,
                ImageName ="man.jpg",
                Country = "Italy",
                Date = DateTime.Now
            };
        }
    }

حالا لازم است که این منبع داده را در اختیار DataContext بگذاریم. وارد بخش کد نویسی شده و در سازنده‌ی پنجره کد زیر را می‌نویسیم:
 DataContext = Person.GetPerson();
public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = Person.GetPerson();
        }  
    }
با این کار، ارتباط شما با منبع داده آغاز می‌شود و طبق درخواست‌هایی که از DataBinding به آن می‌رسد، اطلاعات را تحویل DataBinding می‌دهد. برای نمایش داده‌ها در کنترل‌ها و استفاده از DataBinding، به سراغ خصوصیات وابسته می‌رویم. در حال حاضر فعلا برنامه را با دو کنترل عکس و نام که رشته‌ای هستند آغاز می‌کنیم؛ چون بقیه‌ی کنترل‌ها کمی متفاوت هستند.
همانطور که می‌دانید متن کنترل TextBox توسط خصوصیت Text پر می‌شود و برای همین در این خصوصیت می‌نویسیم:
Text="{Binding Name}"
علامت {} را باز کرده و در ابتدا نام Binding را می‌آوریم. سپس بعد از یک فاصله، نام پراپرتی کلاسی را که حاوی اطلاعات مدنظر است، می‌نویسیم و بدین صورت اتصال برقرار می‌شود. برای کنترل عکس هم وضعیت به همین صورت است:
Source="{Binding ImageName}"
حال برنامه را اجرا کرده و دو کنترل textbox و Image را بررسی می‌کنیم:


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

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

کار را از یک کلاس آغاز می‌کنیم. از اینترفیس INotifyPropertyChanged ارث بری کرده و در آن یک رویداد و یک متد را تعریف می‌کنیم و کمی در هم در تعریف Property‌ها دست می‌بریم. فعلا اینکار را فقط برای پراپرتی Name انجام می‌دهیم:
 private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged("Name");
            }
        }
        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
در کد بالا یک رویداد از نوع PropertyChangedEventHandler تعریف می‌کنیم که وظیفه‌ی به روزآوری را به عهده دارد؛ ولی صدا زدن این رویداد بر عهده‌ی ماست و خود به خود صدا زده نمی‌شود. پس نیاز است متدی را فراهم کرده تا بدانیم که چه خصوصیتی تغییر یافته‌است و از آن طریق رویداد را فراخوانی کنیم و به رویداد بگوییم که کدام پراپرتی تغییر کرده است. این متد را OnpropertyChanged می‌نامیم که آرگومان ورودی آن نام خصوصیتی است که تغییر یافته است و پس از ارزیابی از صحت آن، رویداد را Invoke می‌کنیم.
در بخش Setter آن خصوصیت هم باید این متد را صدا زده و نام خصوصیت را به آن پاس بدهیم تا موقعی که مدل تغییر پیدا کرد، بگوید که خصوصیت Name بوده است که تغییر کرده است.
برای اینکه بدانیم کد واقعا کار می‌کند و تستی بر آن زده باشیم، فعلا دکمه‌ی Save را به Change تغییر می‌دهیم و کد داخل پنجره را بدین صورت تغییر می‌دهیم:
  public partial class MainWindow : Window
    {
        private Person person;
        public MainWindow()
        {
            InitializeComponent();
            person = Person.GetPerson();
            DataContext = person;

        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            person.Name = "Leonardo Decaperio";
        }  
    }

متغیر کلاسی را از حالت محلی Local به عمومی Global تغییر دادم که از طریق دکمه‌ی منبع داده در دسترس باشد. حال در رویداد دکمه نام بازیگر را تغییر می‌دهم. برنامه را اجرا کنید و بر روی دکمه کلیک کنید. باید بعد از یک لحظه‌ی کوتاه، نام بازیگر از Leo به Leonardo Decaperio تغییر کند.
این کد واقعا کدی مفید جهت به روزرسانی است ولی مشکلی دارد که نام پراپرتی باید به صورت String به آن پاس شود که در یک برنامه بزرگ این مورد یک مشکل خواهد شد و اگر نام خصوصیت تغییر کند باید نام داخل آن هم تغییر کند؛ پس کد را به شکل دیگری بازنویسی می‌کنیم:
 private string _name;
        public string Name
        {
            get { return _name; }
            set
            {
                _name = value;
                OnPropertyChanged();
            }
        } 

  private void OnPropertyChanged([CallerMemberName] string property="")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }

در متد OnPropertyChanged در کنار پارامتر اول، ویژگی attribute به نام CallerMemberName را که در فضای نام system.runtime.compilerservice قرار دارد استفاده می‌کنیم (دات نت 4.5). این ویژگی، نام پراپرتی یا متدی که متد OnpropertyChnaged را صدا زده است، به دست می‌آورد. پارامتر اول را هم اختیاری می‌کنیم که سیستم بر ورود پارامتر اجباری نداشته باشد و نهایتا در هر پراپرتی تنها لازم است همانند بالا، خط زیر ذکر شود:
OnPropertyChanged();
اگر الان یک تست از آن بگیرید، می‌بینید که بدون مشکل کار می‌کند. حالا همین متد را در setter تمام پراپرتی‌هایی که دوست دارید از تغییر آن‌ها آگاه شوید قرار دهید.
کد این قسمت
در قسمت‌های آینده به بررسی تبدیل مقادیر و framework element و کنترل‌ها می‌پردازیم.
مطالب
ASP.NET MVC #17

فیلترهای امنیتی ASP.NET MVC

ASP.NET MVC به همراه تعدادی فیلتر امنیتی توکار است که در این قسمت به بررسی آ‌ن‌ها خواهیم پرداخت.


بررسی اعتبار درخواست (Request Validation) در ASP.NET MVC

ASP.NET MVC امکان ارسال اطلاعاتی را که دارای تگ‌های HTML باشند، نمی‌دهد. این قابلیت به صورت پیش فرض فعال است و جلوی ارسال انواع و اقسام اطلاعاتی که ممکن است سبب بروز حملات XSS Cross site scripting attacks شود را می‌گیرد. نمونه‌ای از خطای نمایش داده:

A potentially dangerous Request.Form value was detected from the client (Html="<a>"). 

بنابراین تصمیم گرفته شده صحیح است؛ اما ممکن است در قسمتی از سایت نیاز باشد تا کاربران از یک ویرایشگر متنی پیشرفته استفاده کنند. خروجی این نوع ویرایشگرها هم HTML است. در این حالت می‌توان صرفا برای متدی خاص امکانات Request Validation را به کمک ویژگی ValidateInput غیرفعال کرد:

[HttpPost]
[ValidateInput(false)]
public ActionResult CreateBlogPost(BlogPost post)

از ASP.NET MVC 3.0 به بعد راه حل بهتری به کمک ویژگی AllowHtml معرفی شده است. غیرفعال کردن ValidateInput ‌ایی که معرفی شد، بر روی تمام خواص شیء BlogPost اعمال می‌شود. اما اگر فقط بخواهیم که مثلا خاصیت Text آن از مکانیزم بررسی اعتبار درخواست خارج شود، بهتر است دیگر از ویژگی ValidateInput استفاده نشده و به نحو زیر عمل گردد:

using System;
using System.Web.Mvc;

namespace MvcApplication14.Models
{
public class BlogPost
{
public int Id { set; get; }
public DateTime AddDate { set; get; }
public string Title { set; get; }

[AllowHtml]
public string Text { set; get; }
}
}

در اینجا فقط خاصیت Text مجاز به دریافت محتوای HTML ایی خواهد بود. اما خاصیت Title چنین مجوزی را ندارد. همچنین دیگر نیازی به استفاده از ویژگی ValidateInput غیرفعال شده نیز نخواهد بود.
به علاوه همانطور که در قسمت‌های قبل نیز ذکر شد، خروجی Razor به صورت پیش فرض Html encoded است مگر اینکه صریحا آن‌را تبدیل به HTML کنیم (مثلا استفاده از متد Html.Raw). به عبارتی خروجی Razor در حالت پیش فرض در مقابل حملات XSS مقاوم است مگر اینکه آگاهانه بخواهیم آن‌را غیرفعال کنیم.

مطلب تکمیلی
مقابله با XSS ؛ یکبار برای همیشه!



فیلتر RequireHttps

به کمک ویژگی یا فیلتر RequireHttps، تمام درخواست‌های رسیده به یک متد خاص باید از طریق HTTPS انجام شوند و حتی اگر شخصی سعی به استفاده از پروتکل HTTP معمولی کند، به صورت خودکار به HTTPS هدایت خواهد شد:

[RequireHttps]
public ActionResult LogOn()
{
}


فیلتر ValidateAntiForgeryToken

نوع دیگری از حملات که باید در برنامه‌های وب به آن‌ها دقت داشت به نام CSRF یا Cross site request forgery معروف هستند.
برای مثال فرض کنید کاربری قبل از اینکه بتواند در سایت شما کار خاصی را انجام دهد، نیاز به اعتبار سنجی داشته باشد. پس از لاگین شخص و ایجاد کوکی و سشن معتبر، همین شخص به سایت دیگری مراجعه می‌کند که در آن مهاجمی بر اساس وضعیت جاری اعتبار سنجی او مثلا لینک حذف کاربری یا افزودن اطلاعات جدیدی را به برنامه ارائه می‌دهد. چون سشن شخص و کوکی مرتبط به سایت اول هنوز معتبر هستند و شخص سایت را نبسته است، «احتمال» اجرا شدن درخواست مهاجم بالا است (خصوصا اگر از مرورگرهای قدیمی استفاده کند).
بنابراین نیاز است بررسی شود آیا درخواست رسیده واقعا از طریق فرم‌های برنامه ما صادر شده است یا اینکه شخصی از طریق سایت دیگری اقدام به جعل درخواست‌ها کرده است.
برای مقابله با این نوع خطاها ابتدا باید داخل فرم‌های برنامه از متد Html.AntiForgeryToken استفاده کرد. کار این متد ایجاد یک فیلد مخفی با مقداری منحصربفرد بر اساس اطلاعات سشن جاری کاربر است، به علاوه ارسال یک کوکی خودکار تا بتوان از تطابق اطلاعات اطمینان حاصل کرد:

@using (Html.BeginForm()) {
@Html.AntiForgeryToken()

در مرحله بعد باید فیلتر ValidateAntiForgeryToken جهت بررسی مقدار token دریافتی به متد ثبت اطلاعات اضافه شود:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateBlogPost(BlogPost post)

در اینجا مقدار دریافتی از فیلد مخفی فرم :

<input name="__RequestVerificationToken" type="hidden" value="C0iPfy/3T....=" />

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

[ValidateAntiForgeryToken(Salt="1234")]

@Html.AntiForgeryToken(salt:"1234")

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

مطلب مشابه
Anti CSRF module for ASP.NET



فیلتر سفارشی بررسی Referrer

یکی دیگر از روش‌های مقابله با CSRF، بررسی اطلاعات هدر درخواست ارسالی است. اگر اطلاعات Referrer آن با دومین جاری تطابق نداشت، به معنای مشکل دار بودن درخواست رسیده است. فیلتر سفارشی زیر می‌تواند نمونه‌ای باشد جهت نمایش نحوه بررسی UrlReferrer درخواست رسیده:

using System.Web.Mvc;

namespace MvcApplication14.CustomFilter
{
public class CheckReferrerAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext != null)
{
if (filterContext.HttpContext.Request.UrlReferrer == null)
throw new System.Web.HttpException("Invalid submission");

if (filterContext.HttpContext.Request.UrlReferrer.Host != "mysite.com")
throw new System.Web.HttpException("This form wasn't submitted from this site!");
}

base.OnAuthorization(filterContext);
}
}
}

و برای استفاده از آن:
[HttpPost]
[CheckReferrer]
[ValidateAntiForgeryToken]
public ActionResult DeleteTask(int id)


نکته‌ای امنیتی در مورد آپلود فایل‌ها در ASP.NET

هر جایی که کاربر بتواند فایلی را به سرور شما آپلود کند، مشکلات امنیتی هم از همانجا شروع خواهند شد. مثلا در متد Upload قسمت 11 این سری، منعی در آپلود انواع فایل‌ها نیست و کاربر می‌تواند انواع و اقسام شل‌ها را جهت تحت کنترل گرفتن سایت و سرور آپلود و اجرا کند. راه حل چیست؟
از همان روش امنیتی مورد استفاده توسط تیم ASP.NET MVC استفاده می‌کنیم. فایل web.config قرار گرفته در پوشه Views را باز کنید (نه فایل وب کانفیگ ریشه اصلی سایت‌را). چنین تنظیمی را می‌توان مشاهده کرد:
برای IIS6 :

<system.web>
<httpHandlers>
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
</httpHandlers>
</system.web>
برای IIS7 :
<system.webServer>
<handlers>
<remove name="BlockViewHandler"/>
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
</handlers>
</system.webServer>


تنظیم فوق، موتور اجرایی ASP.NET را در این پوشه خاص از کار می‌اندازد. به عبارتی اگر شخصی مسیر یک فایل aspx یا cshtml یا هر فایل قرار گرفته در پوشه Views را مستقیما در مرورگر خود وارد کند، با پیغام HttpNotFound مواجه خواهد شد.
این روش هم با ASP.NET Web forms سازگار است و هم با ASP.NET MVC؛ چون مرتبط است به موتور اجرایی ASP.NET که هر دوی این فریم ورک‌ها برفراز آن معنا پیدا می‌کنند.
بنابراین در پوشه فایل‌های آپلودی به سرور خود یک web.config را با محتوای فوق ایجاد کنید (و فقط باید مواظب باشید که این فایل حین آپلود فایل‌های جدید، overwrite نشود. مهم!). به این ترتیب این مسیر دیگر از طریق مرورگر قابل دسترسی نخواهد بود (با هر محتوایی). سپس برای ارائه فایل‌های آپلودی به کاربران از روش زیر استفاده کنید:

public ActionResult Download()
{
return File(Server.MapPath("~/Myfiles/test.txt"), "text/plain");
}

مزیت مهم روش ذکر شده این است که کاربران مجاز به آپلود هر نوع فایلی خواهند بود و نیازی نیست لیست سیاه تهیه کنید که مثلا فایل‌هایی با پسوند‌های خاص آپلود نشوند (که در این بین ممکن است لیست سیاه شما کامل نباشد ...).




علاوه بر تمام فیلترهای امنیتی که تاکنون بررسی شدند،‌ فیلتر دیگری نیز به نام Authorize وجود دارد که در قسمت‌های بعدی بررسی خواهد شد.
مطالب
فراخوانی متدهای Controllerها در Viewهای ASP.NET MVC

شاید در ابتدا فراخوانی متدی از یک کنترلر در یک View کار سختی به نظر برسد، ولی در واقع با استفاده از مفاهیم Lambda expressions و Delegate‌ها این کار بسیار راحت خواهد بود. 

برای این کار میتوانیم متد مورد نظر را به صورت یک delegate تعریف کرده و به view ارسال کنیم. فرض کنیم متدی داریم برای برگرداندن مجموع 2 عدد به صورت string: 

public string Sum(int a,int b)
{
    return (a + b).ToString();
}

حال برای اینکه بتوانیم این متد را بصورت یک delegate به view ارسال کنیم لازم است تا یک delegate را بصورت public و در خارج از تعریف کلاسها و درون یک namespace مشخصی تعریف کنیم. در اینجا برای راحتی در همان MvcTest.Controllers namespace  یک delegate را بصورت زیر (MvcTest  نام پروژه است) تعریف می‌کنیم: 

public delegate string SumOf2Number(int a, int b);

حال میتوانیم بصورت زیر این متد را از طریق ViewBag به View ارسال کنیم: 

SumOf2Number sum2numbers = Sum;
ViewBag.SumFunc3 = sum2numbers;

در روش دوم، می‌توانیم متد مورد نظر را بصورت Func  به View ارسال کنیم. این کار را میتوانیم به دو صورت انجام دهیم، که هر دو را در تکه کد زیر خواهید دید: 

ViewBag.SumFunc =(Func<int,int,string>) Sum;//way 1
ViewBag.SumFunc2 = (Func<int, int, string>)((int a, int b) => { return (a + b).ToString(); });//way 2
همانطور که متوجه شدید، در روش اول تنها کاری که کردیم متد Sum را از طریق TypeCasting به یک Func تبدیل کردیم و در روش دوم هم یک Lambda expression را بصورت مستقیم به Func تبدیل کرده و استفاده کردیم.

میتوانیم یک Lambda expression را به یک متغیر delegate نیز ربط دهیم؛ به این صورت:

SumOf2Number sum2numbers2 = (int a, int b) => { return (a + b).ToString(); };

در نهایت کد بخش کنترلر کلاً به اینصورت خواهد بود: 

namespace MvcTest.Controllers
{
    public  delegate string SumOf2Number(int a, int b);
    public class HomeController : Controller
    {        
        public ActionResult Index()
        {            
            SumOf2Number sum2numbers = Sum;
            SumOf2Number sum2numbers2 = (int a, int b) => { return (a + b).ToString(); };
 
            ViewBag.SumFunc =(Func<int,int,string>) Sum;
            ViewBag.SumFunc2 = (Func<int, int, string>)((int a, int b) => { return (a + b).ToString(); });
            ViewBag.SumFunc3 = sum2numbers;
            ViewBag.SumFunc4 = sum2numbers2;
            return View();
        }
        public string Sum(int a,int b)
        {
            return (a + b).ToString();
        }
        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";
 
            return View();
        }
 
        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";
 
            return View();
        }
    }
}

و در Index View خواهیم داشت: (البته اصولاً استفاده از controller namespace در سمت view کار درستی نیست، منتها اینجا فقط یک مثال کاربردی ساده است)  

@using MvcTest.Controllers;
@{
    ViewBag.Title = "Home Page";
}
   
<h1>
    @ViewBag.SumFunc(7,8)
</h1>
<h1>
    @ViewBag.SumFunc2(9, 10)
</h1>
<h1>
    @ViewBag.SumFunc3(5, 1)
</h1>
<h1>
    @ViewBag.SumFunc4(2, 3)
</h1>
نظرات مطالب
استفاده از Full text search توسط Entity Framework
کد‌ها را مطابق مطلب بالا زدم. چون fulltext را در چند تا سایت محدود دیدم که کد زده بودند. به خاطر همین اصلا منطق برنامه اش را متوجه نشدم و کد‌ها را کپی کردم. بعد توی تریس کردن این مشکل اومد. چون هیچ چیزی نمی‌دونستم از دوستان خواهش کردم که اگر به مشکل من برخورد کردند لطفا جواب من را بدهند. 
مطالب
اصول و قراردادهای نام‌گذاری در دات‌نت
نامگذاری (Naming) اشیا یک برنامه شاید در نگاه اول دارای اهمیت بالایی نباشه، اما تجربه نشون داده که در پروژه‌های بزرگ که با کمک چندین مجموعه به انجام میرسه نامگذاری صحیح و اصولی که از یکسری قواعد کلی و مناسب پیروی میکنه میتونه به پیشبرد اهداف و مدیریت راحتتر برنامه کمک بسیاری بکنه.
بیشتر موارد اشاره شده در این مطلب از کتاب جامع و مفید Framework Design Guidelines اقتباس شده که خوندن این کتاب مفید رو به خوانندگان توصیه میکنم.
برای کمک به نوشتن اصولی و راحتتر سورسهای برنامه‌ها در ویژوال استودیو نرم افزارهای متعددی وجود داره که با توجه به تجربه شخصی خودم نرم افزار Resharper  محصول شرکت Jetbrains یکی از بهترین هاست که در مورد خاص مورد بحث در این مطلب نیز بسیار خوب عمل میکنه.
برخی از موارد موجود در مطلب جاری نیز از قراردادهای پیشفرض موجود در نرم افزار Resharper نسخه 6.0 برگرفته شده است و قسمتی نیز از تجربه شخصی خودم و سایر دوستان و همکاران بوده است.
اصل این مطلب حدود یکسال پیش تهیه شده و اگر نقایصی وجود داره لطفا اشاره کنین.

اصول و قراردادهای نام‌گذاری در دات‌نت
انواع نام‌گذاری
نام‌گذاری اشیا در حالت کلی را می‌توان به سه روش زیر انجام داد:
1. Pascal Casing: در این روش حرف اول هر کلمه در نام شی به صورت بزرگ نوشته می‌شود.
FirstName
2. camel Casing: حرف اول در اولین کلمه نام هر شی به صورت کوچک و حرف اول بقیه کلمات به صورت بزرگ نوشته می‌شود.
firstName
3. Hungarian: در این روش برای هر نوع شی موجود یک پیشوند درنظر گرفته می‌شود تا از روی نام شی بتوان به نوع آن پی برد. در ادامه و پس از این پیشوندها سایر کلمات بر اساس روش Pascal Casing نوشته می‌شوند.
strFirstName
lblFirstName
نکته: استفاده از این روش به جز در نام‌گذاری کنترل‌های UI منسوخ شده است.

قراردادهای کلی
1. نباید نام اشیا تنها در بزرگ یا کوچک بودن حروف با هم فرق داشته باشند. به عنوان مثال نباید دو کلاس با نام‌های MyClass و myClass داشته باشیم. هرچند برخی از زبان‌ها case-sensitive هستند اما برخی دیگر نیز چنین قابلیتی ندارند (مثل VB.NET). بنابراین اگر بخواهیم کلاس‌های تولیدی ما در تمام محیط‌ها و زبان‌های برنامه نویسی قابل اجرا باشند باید از این قرارداد پیروی کنیم.
2. تا آنجا که امکان دارد باید از به‌کار بردن مخفف کلمات در نام‌گذاری اشیا دوری کنیم. مثلا به جای استفاده از GetChr باید از GetCharacter استفاده کرد.
البته در برخی موارد که مخفف واژه موردنظر کاربرد گسترده ای دارد می‌توان از عبارت مخفف نیز استفاده کرد. مثل UI به جای UserInterface و یا IO به جای InputOutput.
 
آ. اصول نام‌گذاری فضای نام (namespace)
1. اساس نام‌گذاری فضای نام باید از قاعده زیر پیروی کند:
<Company>.<Technology|Produt|Project>[.<Feature>][.<SubNamespace>]
( < > : اجباری        [ ] : اختیاری )
2. برای نام‌گذاری فضای نام باید از روش Pascal Casing استفاده شود.
3. در هنگام تعریف فضاهای نام به وابستگی آنها توجه داشته باشید. به عنوان مثال اشیای درون یک فضای نام پدر نباید به اشیای درون فضای نام یکی از فرزندانش وابسته باشد. مثلا در فضای نام System نباید اشیایی وجود داشته باشند که به اشیای درون فضای نام System.UI وابسته باشند.
4. سعی کنید از نام‌ها به صورت جمع برای عناوین فضای نام استفاده کنید. مثلا به جای استفاده از Kara.CSS.HQ.Manager.Entity از Kara.CSS.HQ.Manager.Entities استفاده کنید. البته برای مورادی که از عناوین مخفف و یا برندهای خاص استفاده کرده‌اید از این قرارداد پیروی نکنید. مثلا نباید از عنوانی شبیه به Kara.CSS.Manager.IOs استفاده کنید.
5. از عنوانی پایدار و مستقل از نسخه محصول برای بخش دوم عنوان فضای نام استفاده کنید. بدین معنی که این عناوین با گذر زمان و تغییر و تحولات در محتوای محصول و یا تولیدکننده نباید تغییر کنند. مثال:
Microsoft.Reporting.WebForms
Kara.Support.Manager.Enums
Kara.CSS.HQ.WebUI.Configuration
6. از عناوین یکسان برای فضای نام و اشیای درون آن استفاده نکنید. مثلا نباید کلاسی با عنوان Manager در فضای نام Kara.CSS.Manager وجود داشته باشد.
7. از عناوین یکسان برای اشیای درون فضاهای نام یک برنامه استفاده نکنید. مثلا نباید دو کلاس با نام Package در فضاهای نام Kara.CSS.Manger و Kara.CSS.Manger.Entities داشته باشید.

ب. اصول نام‌گذاری کلاس‌ها و Structها
1. عنوان کلاس باید اسم یا موصوف باشد.
2. در نام‌گذاری کلاس‌ها باید از روش Pascal Casing استفاده شود.
3. نباید از عناوین مخففی که رایج نیستند استفاده کرد.
4. از پیشوندهای زائد مثل C یا Cls نباید استفاده شود.
5. نباید از کاراکترهایی به غیر از حروف (و یا در برخی موارد خیلی خاص، شماره نسخه) در نام‌گذاری کلاس‌ها استفاده شود.
مثال:
درست:
PackageManager , PacakgeConfigGenerator
Circle , Utility , Package
نادرست:
CreateConfig , classdata
CManager , ClsPackage , Config_Creator , Config1389
6. در نام‌گذاری اشیای Generic از استفاده از عناوینی چون Element, Node, Log و یا Message پرهیز کنید. این کار موجب به‌وجودآمدن کانفلیکت در عناوین اشیا می‌شود. در این موارد بهتر است از عناوینی چون FormElement, XmlNode, EventLog و SoapMessage استفاده شود. درواقع بهتر است نام اشیای جنریک، برای موارد موردنیاز، کاملا اختصاصی و درعین حال مشخص‌کننده محتوا و کاربرد باشند.
7. از عناوینی مشابه عناوین کتابخانه‌های پایه دات‌نت برای اشیای خود استفاده نکنید. مثلا نباید کلاسی با عنوان Console, Parameter, Action و یا Data را توسعه دهید. هم‌چنین از کابرد عناوینی مشابه اشیای موجود در فضاهای نام غیرپایه دات‌نت و یا غیردات‌نتی اما مورداستفاده در پروژه خود که محتوا و کاربردی مختص همان فضای نام دارند پرهیز کنید. مثلا نباید کلاسی با عنوان Page در صورت استفاده از فضای نام System.Web.UI تولید کنید.
8. سعی کنید در مواردی که مناسب به‌نظر می‌رسد از عنوان کلاس پایه در انتهای نام کلاس‌های مشتق‌شده استفاده کنید. مثل FileStream که از کلاس Stream مشتق شده است. البته این قاعده در تمام موارد نتیجه مطلوب ندارد. مثلا کلاس Button که از کلاس Control مشتق شده است. بنابراین در به‌کاربردن این مورد بهتر است تمام جوانب و قواعد را درنظر بگیرید.

پ. اصول نام‌گذاری مجموعه‌ها (Collections)
یک مجموعه در واقع یک نوع کلاس خاص است که حاوی مجموعه‌ای از داده‌هاست و از همان قوانین کلاس‌ها برای نام‌گذاری آن‌ها استفاده می‌شود.
1. بهتر است در انتهای عنوان مجموعه از کلمه Collection استفاده شود. مثال:
CenterCollection , PackageCollection
2. البته درصورتی‌که کلاس مورد نظر ما رابط IDictionary را پیاده‌سازی کرده باشد بهتر است از پسوند Dictionary استفاده شود.

ت. اصول نام‌گذاری Delegateها
1. عنوان یک delegate باید اسم یا موصوف باشد.
2. در نام‌گذاری delegateها باید از روش Pascal Casing استفاده شود.
3. نباید از عناوین مخففی که رایج نیستند استفاده کرد.
4. از پیشوندهای زائد مثل D یا del نباید استفاده شود.
5. نباید از کاراکترهایی به غیر از حروف در نام‌گذاری delegateها استفاده شود.
6. نباید در انتهای نام یک delegate از عبارت Delegate استفاده شود.
7. بهتر است که درصورت امکان در انتهای نام یک delegate که برای Event Handler استفاده نمی‌شود از عبارت Callback استفاده شود. البته تنها درصورتی‌که معنی و مفهوم مناسب را داشته باشد.
مثال:
نحوه تعریف یک delegate
public delegate void Logger (string log);
public delegate void LoggingCallback (object sender, string reason);

ث. اصول نام‌گذاری رویدادها (Events)
1. عنوان یک رویداد باید فعل یا مصدر باشد.
2. در نام‌گذاری کلاس‌ها باید از روش Pascal Casing استفاده شود.
3. نباید از عناوین مخففی که رایج نیستند استفاده کرد.
4. از پیشوندهای زائد نباید استفاده شود.
5. نباید از کاراکترهایی به غیر از حروف در نام‌گذاری رویدادها استفاده شود.
6. بهتر است از پسوند EventHandler در عنوان هندلر رویداد استفاده شود.
7. از به کاربردن عباراتی چون AfterXXX و یا BeforeXXX برای نمایش رویدادهای قبل و یا بعد از رخداد خاصی خودداری شود. به جای آن باید از اسم مصدر (شکل ingدار فعل) برای نام‌گذاری رویداد قبل از رخداد و هم‌چنین شکل گذشته فعل برای نام‌گذاری رویداد بعد از رخداد خاص استفاده کرد.
مثلا اگر کلاسی دارای رویداد Open باشد باید از عنوان Openning برای رویداد قبل از Open و از عنوان Opened برای رویداد بعد از Open استفاده کرد.
8. نباید از پیشوند On برای نام‌گذاری رویداد استفاده شود.
9. همیشه پارامتر e و sender را برای آرگومانهای رویداد پیاده سازی کنید. sender که از نوع object است نمایش دهنده شیی است که رویداد مربوطه را به وجود آورده است و e درواقع آرگومانهای رویداد مربوطه است.
10. عنوان کلاس‌آرگومان‌های رویداد باید دارای پسوند EventArgs باشد.
مثال:
عنوان کلاس‌آرگومان:
AddEventArgs , EditEventArgs , DeleteEventArgs
عنوان رویداد:
Adding , Add , Added
تعریف یک EventHandler:
public delegate void <EventName>EventHandler  (object sender, <EventName>EventArgs e);
نکته: نیاز به تولید یک EventHandler مختص توسعه یک برنامه به‌ندرت ایجاد می‌شود. در اکثر موارد می‌توان با استفاده از کلاس جنریک <EventHandler<TEventArgs تمام احتیاجات خود را برطرف کرد.
تعریف یک رویداد:
public event EventHandler <AddEventArgs> Adding;

ج. اصول نام‌گذاری Attributeها
1. عنوان یک attribute باید اسم یا موصوف باشد.
2. در نام‌گذاری attributeها باید از روش Pascal Casing استفاده شود.
3. نباید از عناوین مخففی که رایج نیستند استفاده کرد.
4. از پیشوندهای زائد مثل A یا atr نباید استفاده شود.
5. نباید از کاراکترهایی به غیر از حروف در نام‌گذاری attributeها استفاده شود.
6. بهتر است در انتهای نام یک attribute از عبارت Attribute استفاده شود.
مثال:
DisplayNameAttribute , MessageTypeAttribute

چ. اصول نام‌گذاری Interfaceها
1. در ابتدای عنوان interface باید از حرف I استفاده شود.
2. نام باید اسم، موصوف یا صفتی باشد که interface را توصیف می‌کند.
به عنوان مثال:
IComponent  (اسم)
IConnectionProvider (موصوف)
ICloneable (صفت)
3. از روش Pascal Casing استفاده شود.
4. خودداری از بکاربردن عبارات مخفف غیررایج.
5. باید تنها از کاراکترهای حرفی در نام interface استفاده شود.
 
ح. اصول نام‌گذاری Enumerationها
1. استفاده از روش Pascal Casing
2. خودداری از کاربرد عبارات مخفف غیررایج
3. تنها از کاراکترهای حرفی در نام Enumretionها استفاده شود.
4. نباید از پسوند یا پیشوند Enum یا Flag استفاده شود.
5. اعضای یک Enum نیز باید با روش Pascal Casing نام‌گذاری شوند.
مثال:
public enum FileMode {
    Append,
    Read, …
}
6. درصورتی‌که enum موردنظر از نوع flag نیست باید عنوان آن مفرد باشد. 
7. درصورتی‌که enum موردنظر برای کاربرد flag طراحی شده باشد نام آن باید جمع باشد. مثال:
[Flag]
public enum KeyModifiers {
    Alt = 1, 
    Control = 2,
    Shift = 4
}
8. از به‌کاربردن پسوند و یا پیشوندهای اضافه در نام‌گذاری اعضای یک enum نیز پرهیز کنید. مثلا نام‌گذاری زیر نادرست است:
public enum OperationState {
    DoneState, 
    FaultState,
    RollbackState
}

خ. اصول نام‌گذاری متدها
1. نام متد باید فعل یا ترکیبی از فعل و اسم یا موصوف باشد.
2. باید از روش Pascal Casing استفاده شود.
3. خودداری از بکاربردن عبارات مخفف غیررایج و یا استفاده زیاد از اختصار
4. تنها از کاراکترهای حرفی برای نام متد استفاده شود.
مثال:
AddDays , Save , DeleteRow , BindData , Close , Open

د. اصول نام‌گذاری Propertyها
1. نام باید اسم، صفت یا موصوف باشد.
2. باید از روش Pascal Casing استفاده شود.
3. خودداری از بکاربردن عبارات مخفف غیررایج.
4. تنها از کاراکترهای حرفی برای نام‌گذاری پراپرتی استفاده شود.
مثال:
Radius , ReportType , DataSource , Mode , CurrentCenterId
5. از عبارت Get در ابتدای هیچ Propertyای استفاده نکنید.
6. نام خاصیت‌هایی که یک مجموعه برمی‌گرداند باید به صورت جمع باشد. عنوان این Propertyها نباید به‌صورت مفرد به همراه پسوند Collection یا List باشد. مثال:
public CenterCollection Centers { get; set; }
7. خواص Boolean را با عناوینی مثبت پیاده‌سازی کنید. مثلا به‌جای استفاده از CantRead از CanRead استفاده کنید. بهتر است این Propertyها پیشوندهایی چون Is, Can یا Has داشته باشند، البته تنها درصورتی‌که استفاده از چنین پیشوندهایی ارزش افزوده داشته و مفهوم آن را بهتر برساند. مثلا عنوان CanSeek مفهوم روشن‌تری نسبت به Seekable دارد. اما استفاده از Created خیلی بهتر از IsCreated است یا Enabled کاربرد به مراتب راحت‌تری از IsEnabled دارد.
برای تشخیص بهتر این موارد بهتر است از روش ifسنجی استفاده شود. به عنوان مثال
if (list.Contains(item))
if (regularExpression.Matches(text))
if (stream.CanSeek)
if (context.Created)
if (form.Enabled)
مفهوم درست‌تری نسبت به موارد زیر دارند:
if (list.IsContains(item))
if (regularExpression.Match(text))
if (stream.Seekable)
if (context.IsCreated)
if (form.IsEnabled)
8. بهتر است در موارد مناسب عنوان Property با نام نوعش برابر باشد. مثلا
public Color Color { get; set; }

ذ. اصول نام‌گذاری پارامترها
پارامتر درحالت کلی به آرگومان وروی تعریف شده برای یک متد گفته می‌شود.
1. حتما از یک نام توصیفی استفاده شود. از نام‌گذاری پارامترها براساس نوعشان به‌شدت پرهیز کنید.
2. از روش camel Casing استفاده شود.
3. تنها از کاراکترهای حرفی برای نام‌گذاری پارامترها استفاده شود.
مثال:
firstName , e , id , packageId , centerName , name
4. نکاتی برای نام‌گذاری پارامترهای Operator Oveloading:
- برای operatorهای دو پارامتری (binary operators) از عناوین left و right برای پارامترهای آن استفاده کنید:
public static MyType operator +(MyType left, MyType right)
public static bool operator ==(MyType left, MyType right)
- برای operatorهای تک‌پارامتری (unary operators) اگر برای پارامتر مورد استفاده هیچ عنوان توصیفی مناسبی پیدا نکردید حتما از عبارت value استفاده کنید:
public static MyType operator ++(MyType value)
- درصورتی‌که استفاده از عناوین توصیفی دارای ارزش افزوده بوده و خوانایی کد را بهتر می‌کند حتما از این نوع عناوین استفاده کنید:
public static MyType operator /(MyType dividend, MyType divisor)
- نباید از عبارات مخفف یا عناوینی با اندیس‌های عددی استفاده کنید:
public static MyType operator -(MyType d1, MyType d2) // incorrect!

ر. اصول نام‌گذاری متغیر (Variable)ها
- نام متغیر باید اسم، صفت یا موصوف باشد.
نام‌گذاری متغیرها باید با توجه به نوع آن انجام شود.
1. متغیرهای عمومی (public) و protected و Constantها
- استفاده از روش Pascal Casing
- تنها از کاراکترهای حرفی برای نام متغیر عمومی استفاده شود.
مثال:
Area , DataBinder , PublicCacheName
2. متغیرهای private (در سطح کلاس یا همان field)
- نام این نوع متغیر باید با یک "_" شروع شود.
- از روش camel Casing استفاده شود.
مثال:
_centersList
_firstName
_currentCenter
3. متغیرهای محلی در سطح متد
- باید از روش camel Casing استفاده شود.
- تنها از کاراکترهای حرفی استفاده شود.
مثال:
parameterType , packageOperationTypeId

ز. اصول نام‌گذاری کنترل‌های UI
1. نام باید اسم یا موصوف باشد.
2. استفاده از روش Hungarian !
3. عبارت مخفف معرفی‌کننده کنترل، باید به اندازه کافی برای تشخیص نوع آن مناسب باشد.
مثال:
lblName (Label)
txtHeader (TextBox)
btnSave (Button)

ژ. اصول نام‌گذاری Exceptionها
تمام موارد مربوط به نام‌گذاری کلاس‌ها باید در این مورد رعایت شود. 
1. باید از پسوند Exception در انتهای عنوان استفاده شود.
مثال:
ArgumentNullException , InvalidOperaionException

س. نام‌گذاری اسمبلی‌ها و DLLها
1. عناوینی که برای نام‌گذاری اسمبلی‌ها استفاده می‌شوند، باید نمایش‌دهنده محتوای کلی آن باشند. مثل:
System.Data
2. از روش زیر برای نام‌گذاری اسمبلی‌ها استفاده شود:
<Company>.<Component>.dll
<Company>.<Project|Product|Technology>.<Component>.dll
مثل:
Microsoft.CSharp.dll , Kara.CSS.Manager.dll

ش. نام‌گذاری پارامترهای نوع (Generic (type parameter
1. از حرف T برای پارامترهای تک‌حرفی استفاده کنید. مثل:
public int IComparer<T> {…}
public delegate bool Predicate<T> (T item)
2. تمامی پارامترهای جنریک را با عناوینی توصیفی و مناسب که مفهوم و کاربرد آنرا برساند نام‌گذاری کنید، مگر آنکه یافتن چنین عباراتی ارزش افزوده‌ای در روشن‌تر کردن کد نداشته باشد. مثال:
public int ISessionChannel<TSession> {…}
public delegate TOutput Converter<TInput, TOutput> (TInput from)
public class Nullable<T> {…}
public class List<T> {…}
3. در ابتدای عناوین توصیفی حتما از حرف T استفاده کنید.
4. بهتر است تا به‌صورتی روشن نوع قید قرار داده شده بر روی پارامتری خاص را در نام آن پارامتر نمایش دهید. مثلا اگر قید ISession را برای پارامتری قرار دادید بهتر است نام آن پارامتر را TSession درنظر بگیرید.
 
ص. نام‌گذاری کلید Resourceها
به دلیل شباهت ساختاری که میان کلیدهای resource و propertyها وجود دارد قواعد نام‌گذاری propertyها در اینجا نیز معتبر هستند.
1. از روش نام‌گذاری Pascal Casing برای کلیدهای resource استفاده کنید.
2. از عناوین توصیفی برای این کلیدها استفاده کنید. سعی کنید تا حد امکان به هیچ وجه از عناوین کوتاه و یا مخففی که مفهوم را به‌صورت ناکامل می‌رساند استفاده نکنید. درواقع سعی کنید که خوانایی بیشتر کد را فدای فضای بیشتر نکنید.
3. از کلیدواژه‌های CLR و یا زبان مورداستفاده برای برنامه‌نویسی در نام‌گذاری این کلیدها استفاده نکنید.
4. تنها از حروف و اعداد و _ در نام‌گذاری این کلیدها استفاده کنید.
5. سعی کنید از عناوین توصیفی همانند زیر برای پیام‌های مناسب خطاها جهت نمایش به کاربر برای کلیدهای مربوطه استفاده کنید. درواقع نام کلید باید ترکیبی از نام نوع خطا و یک آی‌دی مشخص‌کننده پیغام مربوطه باشد:
ArgumentExceptionIllegalCharacters
ArgumentExceptionInvalidName
ArgumentExceptionFileNotFound
 
نکاتی درمورد کلمات مرکب
کلمات مرکب به کلماتی گفته می‌شود که در آن از بیش از یک کلمه با مفهوم مستقل استفاده شده باشد. مثل Callback یا FileName. 
باید توجه داشت که با تمام کلمات موجود در یک کلمه مرکب نباید همانند یک کلمه مستقل رفتار کرد و حرف اول آن را در روش‌های نام‌گذاری موجود به‌صورت بزرگ نوشت. کلمات مرکبی وجود دارند که به آن‌ها closed-form گفته می‌شود. این کلمات مرکب با اینکه از 2 یا چند کلمه دارای مفهوم مستقل تشکیل شده‌اند اما به‌خودی‌خود دارای مفهوم جداگانه و مستقلی هستند. برای تشخیص این کلمات می‌توان به فرهنگ لغت مراجعه کرد (و یا به‌سادگی از نرم‌افزار Microsoft Word استفاده کرد) و دریافت که آیا کلمه مرکب موردنظر آیا مفهوم مستقلی برای خود دارد، یعنی درواقع آیا عبارتی closed-form است. با کلمات مرکب از نوع closed-form همانند یک کلمه ساده برخورد می‌شود! 
در جدول زیر مثال‌هایی از عبارات رایج و نحوه درست و نادرست استفاده از هریک نشان داده شده است.

Pascal Casing

camel Casing

Wrong

Callback

callback

CallBack

BitFlag

bitFlag

Bitflag / bitflag

Canceled

canceled

Cancelled

DoNot

doNot

Donot / Don’t

Email

email

EMail

Endpoint

endpoint

EndPoint / endPoint

FileName

fileName

Filename / filename

Gridline

gridline

GridLine / gridLine

Hashtable

hashtable

HashTable / hashTable

Id

id

ID

Indexes

indexes

Indices

LogOff

logOff

Logoff / LogOut !

LogOn

logOn

Logon / LogIn !

SignOut

signOut

Signout / SignOff

SignIn

signIn

Signin / SignOn

Metadata

metadata

MetaData / metaData

Multipanel

multipanel

MultiPanel / multiPanel

Multiview

multiview

MultiView / multiView

Namespace

namespace

NameSpace / nameSpace

Ok

ok

OK

Pi

pi

PI

Placeholder

placeholder

PlaceHolder / placeHolder

UserName

username

Username / username

WhiteSpace

whiteSpace

Whitespace / whitespace

Writable

writable

Writeable / writeable

همان‌طور که در جدول بالا مشاهد می‌شود در استفاده از قوانین عبارات مخفف دو مورد استثنا وجود دارد که عبارتند از Id و Ok. این کلمات باید همان‌طور که در اینجا نشان داده شده‌اند استفاده شوند.
 
نکاتی درباره عبارات مخفف
1. عبارات مخفف (Acronym) با خلاصه‌سازی کلمات (Abbreviation) فرق دارند. یک عبارت مخفف شامل حروف اول یک عبارت طولانی یا معروف است، درصورتی‌که خلاصه‌سازی یک عبارت یا کلمه از حذف بخشی از آن به‌دست می‌آید. تا آنجاکه امکان دارد از خلاصه‌سازی عبارات نباید استفاده شود. هم‌چنین استفاده از عبارات مخفف غیررایج توصیه نمی‌شود.
مثال: IO و UI
2. براساس تعریف، یک مخفف حداقل باید 2 حرف داشته باشد. نحوه برخورد با مخفف‌های دارای بیشتر از 2 حرف با مخفف‌های دارای 2 حرف باهم متفاوت است. با مخفف‌های دارای 2 حرف همانند کلمه‌ای یک حرفی! برخورد می‌شود، درصورتی‌که با سایر مخفف‌ها همانند یک کلمه کامل چندحرفی برخورد می‌شود. به‌عنوان مثال IOStream برای روش PascalCasing و ioStream برای روش camelCasing استفاده می‌شود. هم‌چنین از HtmlBody و htmlBody به‌ترتیب برای روش‌های Pascal و camel استفاده می‌شود.
3. هیچ‌کدام از حروف یک عبارت مخفف در ابتدای یک واژه نام‌گذاری‌شده به روش camelCasing به‌صورت بزرگ نوشته نمی‌شود!
نکته: موارد زیادی را می‌توان یافت که در ابتدا به‌نظر می‌رسد برای پیاده‌سازی آنها باید قوانین فوق را نقض کرد. این موارد شامل استفاده از کتابخانه‌های سایر پلتفرم‌ها (مثل MFC, HTML و غیره)، جلوگیری از مشکلات جغرافیایی! (مثلا در مورد نام کشورها یا سایر موقعیت‌های جغرافیایی)، احترام به نام افراد درگذشته، و مواردی از این دست می‌شود. اما در بیشتر قریب به اتفاق این موارد هم می‌توان بدون تقض قوانین فوق اقدام به نام‌گذاری اشیا کرد، بدون اینکه با تغییر عبارت اصلی (که موجب تطابق با این قوانین می‌شود) لطمه‌ای به مفهوم آن بزند. البته تنها موردی که به‌نظر می‌رسد می‌تواند قوانین فوق را نقض کند نام‌های تجاری هستند. البته استفاده از نام‌های تجاری توصیه نمی‌شود، چون این نام‌ها سریع‌تر از محتوای کتابخانه‌های برنامه‌نویسان تغییر می‌کنند!
نکته: برای درک بهتر قانون "عدم استفاده از عبارات مخففی که رایج نیستند" مثالی از زبان توسعه دهندگان دات‌نت‌فریمورک ذکر می‌شود. در کلاس Color متد زیر با Overloadهای مختلف در دسترس است:
public class Color {
    …
    public static Color FromArgb(…)
    { … }
}
همان‌طور که مشاهده می‌شود برای رعایت قوانین فوق به‌جای استفاده از ARGB از عبارت Argb استفاده شده است. اما این نحوه استفاده موجب شده تا این سوال به‌ظاهر خنده‌دار اما درست پیش‌آید:
"چطور می‌شود در این کلاس رنگی را از ARGB تبدل کرد؟ هرچه که من میبینم فقط تبدیل از طریق (Argb) آرگومان b است!"
حال در این نقطه به‌نظر می‌رسد که در اینجا باید قوانین فوق را نقض کرد و از عنوان FromARGB که مفهوم درست را می‌رساند استفاده کرد. اما با کمی دقت متوجه می‌شویم که این قوانین در ابتدا نیز با پیاده‌سازی نشان داده شده در قطعه کد بالا نقض شده‌اند! همه می‌دانیم که عبارت RGB مخفف معروفی برای عبارت Red Green Blue است. اما استفاده از ARGB برای افزودن کلمه Alpha به ابتدای عبارت مذکور چندان رایج نیست. پس استفاده از مخفف Argb از همان ابتدا اشتباه به‌نظر می‌رسد. بنابراین راه‌حل بهتر می‌تواند استفاده از عنوان FromAlphaRgb باشد که هم قوانین فوق را نقض نکرده و هم مفهوم را بهتر می‌رساند.
 
دیگر نکات
1. قراردادهای اشاره شده در این سند حاصل کار شبانه روزی تعداد بسیاری از برنامه نویسان در سرتاسر جهان در پروژه‌های بزرگ بوده است. این اصول کلی تنها برای توسعه آسان‌تر و سریع‌تر پروژه‌های بزرگ تعیین شده‌اند و همان‌طور که روشن است تنها ازطریق تجربه دست‌یافتنی هستند. بنابراین چه بهتر است که در این راه از تجارب بزرگان این عرصه بیشترین بهره برده شود.
2. هم‌چنین توجه داشته باشید که بیشتر قراردادهای اشاره شده در این سند از راهنمای نام‌گذاری تیم توسعه BCL دات‌نت‌فریمورک گرفته شده است. این تیم طبق اعتراف خودشان زمان بسیار زیادی را برای نام‌گذاری اشیا صرف کرده‌اند و توصیه کرده‌اند که دیگران نیز برای نام‌گذاری، زمان مناسب و کافی را در توسعه پروژه‌ها درنظر بگیرند.
 
مطالب
با HttpHandler بیشتر آشنا شوید
در  مقاله قبل توضیح دادیم که وظیفه httphandler رندر و پردازش خروجی یک درخواست هست؛ حالا در این مقاله قصد داریم که مفهوم httphandler را بیشتر بررسی کنیم.

HttpHandler
برای تهیه‌ی یک httphandler، باید کلاسی را بر اساس اینترفیس IHttpHandler پیاده سازی کنیم و بعدا آن را در web.config برنامه معرفی کنیم. برای پیاده سازی این اینترفیس، به یک متد به اسم ProcessRequest با یک پارامتر از نوع HttpContext و یک پراپرتی به اسم IsReusable نیاز داریم که مقدار برگشتی این پراپرتی را false بگذارید؛ بعدا خواهم گفت چرا اینکار را می‌کنیم. نحوه‌ی پیاده‌سازی یک httphandler به شکل زیر است:
public class MyHttpHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {        
    }

    public bool IsReusable
    {
        get { return false; }
    }
}
با استفاده از شیء context می‌توان به دو شیء httpresponse و httprequest دسترسی داشت. تکه کد زیر مثالی است در مورد نحوه‌ی تغییر در محتوای سایت:
public class MyHttpHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        HttpResponse response = context.Response;
        HttpRequest request = context.Request;

        response.Write("Every Page has a some text like this");
    }

    public bool IsReusable
    {
        get { return false; }
    }
}
بگذارید همین کد ساده را در وب کانفیگ معرفی کنیم:
<system.web>
  <httpHandlers>
      <add verb="*" path="*.aspx" type="MyHttpHandler"/>
  </httpHandlers>
</system.web>
اگر نسخه IIS شما همانند نسخه‌ی من باشد، نباید هیچ تغییری مشاهده کنید؛ زیرا کد بالا فقط در مورد نسخه‌ی IIS6 صدق می‌کند و برای نسخه‌های IIS 7 به بعد باید به شیوه زیر عمل کنید:
<configuration>
  <system.web>
    <httpHandlers>

  <add name="myhttphandler" verb="*" path="*.aspx" type="MyHttpHandler"/>

    </httpHandlers>
  </system.web>
</configuration>
خروجی نهایی باید تنها این متن باشد: Every Page has a some text like this 
گزینه Type که نام کلاس می‌باشد و اگر کلاس داخل یک فضای نام قرار گرفته باشد، باید اینطور نوشت : namespace.ClassName  
گزینه verb شامل مقادیری چون Get,Post,Head,Putو Delete می‌باشد و httphandler را فقط برای این نوع درخواست‌ها اجرا می‌کند و در صورتیکه بخواهید چندتا از آن‌ها را استفاده کنید، با , از هم جدا می‌شوند. مثلا Get,post و درصورتیکه همه‌ی گزینه‌ها را بخواهید علامت * را میتوان استفاده کرد. 
 گزینه‌ی path این امکان را به شما می‌دهد که مسیر و نوع فایل‌هایی را که قصد دارید روی آن‌ها فقط اجرا شود، مشخص کنید و ما در قطعه کد بالا گفته‌ایم که تنها روی فایل‌هایی با پسوند aspx اجرا شود و چون مسیری هم ذکر نکردیم برای همه‌ی مسیرها قابل اجراست. یکی از مزیت‌های دادن پسوند این است که می‌توانید پسوندهای اختصاصی داشته باشید. مثلا پسوند RSS برای فیدهای وب سایتتان. بسیاری از برنامه نویسان به جای استفاده از صفحات aspx از ashx استفاده می‌کنند که به مراتب سبک‌تر از aspx هست و شامل بخش ui نمی‌شود و نتیجه خروجی آن بر اساس کدی که می‌نویسید مشخص می‌شود که میتواند صفحه متنی یا عکس یا xml یا ... باشد. در اینجا در مورد ساخت صفحات ashx توضیح داده شده است. 

  IHttpHandlerFactory
کار این اینترفیس پیاده سازی یک کلاس است که خروجی آن یک کلاس از نوع IHttpHandler هست. اگر دقت کنید در مثال‌های قبلی ما برای معرفی یک هندلر در وب کانفیگ یک سری path را به آن میدادیم و برای نمونه aspx.* را معرفی می‌کردیم؛ یعنی این هندلر را بر روی همه‌ی فایل‌های aspx اجرا کن و اگر دو یا چند هندلر در وب کانفیگ معرفی کنیم و برای همه مسیر aspx را قرار بدهیم، یعنی همه این هندلرها باید روی صفحات aspx اجرا گردند ولی در httphandlerfactory، ما چند هندلر داریم و میخواهیم فقط یکی از آن‌ها بر روی صفحات aspx انجام بگیرد، پس ما یک هندلرفکتوری را برای صفحات aspx معرفی می‌کنیم و در حین اجرا تصمیم می‌گیریم که کدام هندلر را ارسال کنیم.
اجازه بدهید نوشتن این نوع کلاس را آغاز کنیم،ابتدا دو هندلر به نام‌های httphandler1 و httphandler2 می‌نویسیم :
public class MyHttpHandler1 :IHttpHandler
{
   
    public void ProcessRequest(HttpContext context)
    {
        HttpResponse response = context.Response;
        response.Write("this is httphandler1");
    }

    public bool IsReusable
    {
        get { return false; }
    }
}

public class MyHttpHandler2 : IHttpHandler
{

    public void ProcessRequest(HttpContext context)
    {
        HttpResponse response = context.Response;
        response.Write("this is httphandler2");
    }

    public bool IsReusable
    {
        get { return false; }
    }
}
سپس کلاس MyFactory را بر اساس اینترفیس IHttpFactory پیاده سازی می‌کنیم و باید دو متد برای آن صدا بزنیم؛ یکی که هندلر انتخابی را بر میگرداند و دیگری هم برای رها کردن یا آزادسازی یک هندلر هست که در این مقاله کاری با آن نداریم. عموما GC دات نت در این زمینه کارآیی خوبی دارد. در قسمت هندلرهای غیرهمزمان به طور مختصر خواهیم گفت که GC چطور آن‌ها را مدیریت می‌کند. کد زیر نمونه کلاسی است که توسط IHttpFactory پیاده سازی شده است:
public class MyFactory : IHttpHandlerFactory
{
    public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTrasnlated)
    {
        
    }

    public void ReleaseHandler(IHttpHandler handler)
    {
        
    }
}
در متد GetHandler چهار آرگومان وجود دارند که به ترتیب برای موارد زیر به کار می‌روند:
 Context یک شی از کلاس httpcontext که دسترسی ما را برای اشیاء سروری چون response,request,session و... فراهم می‌کند.
 RequestType  مشخص می‌کند که درخواست صفحه به چه صورتی است. این گزینه برای مواردی است که verb بیش از یک مورد را حمایت می‌کند. برای مثال دوست دارید یک هندلر را برای درخواست‌های Get ارسال کنید و هندلر دیگر را برای درخواست‌های نوع Post
 URL مسیر مجازی virtual Path صفحه صدا زده شده 
 PathTranslated مسیر فیزیکی صفحه درخواست کننده را ارسال می‌کند. 
متد GetHandler را بدین شکل می‌نویسیم و میخواهیم همه صفحات aspx هندلر شماره یک را انتخاب کنند و صفحات aspx که نامشان با t شروع می‌شوند، هندلر  شماره دو را انتخاب کند:
 public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTrasnlated)
    {
        string handlername = "MyHttpHandler1";
        if(url.Substring(url.LastIndexOf("/")+1).StartsWith("t"))
        {
            handlername = "MyHttpHandler2";
        }

        try
        {
            return (IHttpHandler) Activator.CreateInstance(Type.GetType(handlername));
        }
        catch (Exception e) 
        {
            throw new HttpException("Error: " + handlername, e);
        }
    }

    public void ReleaseHandler(IHttpHandler handler)
    {
        
    }
}
شی Activator که برای ساخت اشیاء با انتخاب بهترین constructor موجود بر اساس یک نوع Type مشخص به کار می‌رود و خروجی Object را می‌گرداند؛ با یک تبدیل ساده، خروجی به قالب اصلی خود باز میگردد. برای مطالعه بیشتر در مورد این کلاس به اینجا و اینجا مراجعه کنید.
نحوه‌ی تعریف factory در وب کانفیگ مانند قبل است و فقط باید در Type به جای نام هندلر نام فکتوری را نوشت. برنامه را اجرا کنید تا نتیجه آن را ببینیم:
تصویر زیر نتیجه صدا زده شدن فایل default.aspx است:

تصویر زیر نتیجه صدا زده شدن فایل Tours_List.aspx است:

AsyncHttpHandlers
برای اینکه کار این اینترفیس را درک کنید بهتر هست اینجا را مطالعه کنید. در اینجا به خوبی تفاوت متدهای همزمان و غیرهمزمان توضیح داده شده است.
متن زیر خلاصه‌ترین و بهترین توضیح برای این پرسش است، چرا غیرهمزمان؟
در اعمالی که disk I/O و یا network I/O دارند، پردازش موازی و اعمال async به شدت مقیاس پذیری سیستم را بالا می‌برند. به این ترتیب worker thread جاری (که تعداد آن‌ها محدود است)، سریعتر آزاد شده و به worker pool بازگشت داده می‌شود تا بتواند به یک درخواست دیگر رسیده سرویس دهد. در این حالت می‌توان با منابع کمتری، درخواست‌های بیشتری را پردازش کرد. 
موقعی که اینترفیس IHttpAsyncHandler را ارث بری کنید (این اینترفیس نیز از IHttpHandler ارث بری کرده است و دو متد اضافه‌تر دارد)، باید دو متد دیگر را نیز پیاده سازی کنید:
 public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback callback, object obj)
    {
        
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        
    }
پراپرتی ISResuable هم موقعی که true برگشت بدهد، باعث می‌شود pooling فعال شده و این هندلر در حافظه باقی بماند و تمامی درخواست‌ها از طریق همین یک نمونه اجرا شوند.
به زبان ساده‌تر، این پراپرتی می‌گوید اگر چندین درخواست از طرف کلاینت‌ها برسد، توسط یک نمونه یا instance از هندلر پردازش خواهند شد؛ چون به طور پیش فرض موقعی که تمام درخواست‌های از pipeline بگذرند، هندلر‌ها توسط httpapplication در یک لیست بازیافت قرار گرفته و همه‌ی آن‌ها با null مقداردهی می‌شوند تا از حافظه پاک شوند ولی اگر این پراپرتی true برگرداند، هندلر مربوطه نال نشده و برای پاسخگویی به درخواست‌های بعدی در حافظه خواهد ماند.
مهمترین مزیت این گزینه، این می‌باشد که کاآیی سیستم را بالا می‌برد و اشیا کمتری به GC پاس می‌شوند. ولی یک عیب هم دارد که این تردهایی که ایجاد می‌کند، امنیت کمتری دارند و باید توسط برنامه نویس این امنیت بالاتر رود. این پراپرتی را در مواقعی که با هندلرهای همزمان کار می‌کنید برابر با false بگذارید چون این گزینه بیشتر بر روی هندلرهای غیرهمزمان اثر دارد و هم اینکه بعضی‌ها توصیه می‌کنند که false بگذارید چون GC مدیریت خوبی در مورد هندلرها دارد و هم این که ارزش یافتن باگ در کد را ندارد.
بر میگردیم سراغ کد نویسی هندلر غیر همزمان. در آخرین قطعه کد نوشته شده، ما دو متد دیگر را پیاده سازی کردیم که یکی از آن‌ها BeginProcessRequest  است و خروجی آن کلاسی است که از اینترفیس IAsyncResult  ارث بری کرده است. پس یک کلاس با ارث بری از این اینترفیس می‌نویسیم و در این کلاس نیاز است که 4 پراپرتی را پیاده سازی کنیم که این کلاس به شکل زیر در خواهد آمد:
public class AsynchOperation : IAsyncResult
{
    private bool _completed;
    private Object _state;
    private AsyncCallback _callback;
    private HttpContext _context;

    bool IAsyncResult.IsCompleted { get { return _completed; } }
    WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } }
    Object IAsyncResult.AsyncState { get { return _state; } }
    bool IAsyncResult.CompletedSynchronously { get { return false; } }
}
متدهای private اجباری نیستند؛ ولی برای ذخیره مقادیر get و set نیاز است. همانطور که از اسامی آن‌ها پیداست مشخص است که برای چه کاری ساخته شده اند.
خب اجازه بدهید یک تابع سازنده به آن برای مقداردهی اولیه این متغیرهای خصوصی داشته باشیم:
   public AsynchOperation(AsyncCallback callback, HttpContext context, Object state)
    {
        _callback = callback;
        _context = context;
        _state = state;
        _completed = false;
    }
همانطور که می‌بینید موارد موجود در متد BeginProcessRequest را تحویل می‌گیریم تا اطلاعات درخواستی مربوطه را داشته باشیم و مقدار _Completed را هم برابر با false قرار می‌دهیم. سپس نوبت این می‌رسد که ما درخواست را در صف pool قرار دهیم. برای همین تکه کد زیر را اضافه می‌کنیم: 
   public void StartAsyncWork()
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(StartAsyncTask),null);
    }
با اضافه شدن درخواست به صف، هر موقع درخواست‌های قبلی تمام شوند و callback خودشان را ارسال کنند، نوبت درخواست‌های جدیدتر هم میرسد. StartAsyncTask هم متدی است که وظیفه‌ی اصلی پردازش درخواست را به دوش دارد و موقعی که نوبت درخواست برسد، کدهای این متد اجرا می‌گردد که ما در اینجا مانند مثال اول روی صفحه چیزی نوشتیم:
 private void StartAsyncTask(Object workItemState)
    {

        _context.Response.Write("<p>Completion IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");

        _context.Response.Write("Hello World from Async Handler!");
        _completed = true;
        _callback(this);
    }
دو خط اول اطلاعات را چاپ کرده و در خط سوم متغیر _completed را true کرده و در آخر این درخواست را فراخوانی مجدد می‌کنیم تا بگوییم که کار این درخواست پایان یافته‌است؛ پس این درخواست را از صف بیرون بکش و درخواست بعدی را اجرا کن.
نهایتا کل این کلاس را در متد BeginProcessRequest  صدا بزنید:
context.Response.Write("<p>Begin IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");
        AsynchOperation asynch = new AsynchOperation(callback, context, obj);
        asynch.StartAsyncWork();
        return asynch;
کل کد مربوطه : (توجه:کدها از داخل سایت msdn برداشته شده است و اکثر کدهای موجود در نت هم به همین قالب می‌نویسند)
public class MyHttpHandler : IHttpAsyncHandler
{
    public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback callback, object obj)
    {
        context.Response.Write("<p>Begin IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");
        AsynchOperation asynch = new AsynchOperation(callback, context, obj);
        asynch.StartAsyncWork();
        return asynch;
    }

    public void EndProcessRequest(IAsyncResult result)
    {
        
    }
    public void ProcessRequest(HttpContext context)
    {
       throw new InvalidOperationException(); 

    }

    public bool IsReusable
    {
        get { return false; }
    }
}

public class AsynchOperation : IAsyncResult
{
    private bool _completed;
    private Object _state;
    private AsyncCallback _callback;
    private HttpContext _context;

    bool IAsyncResult.IsCompleted { get { return _completed; } }
    WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } }
    Object IAsyncResult.AsyncState { get { return _state; } }
    bool IAsyncResult.CompletedSynchronously { get { return false; } }

    public AsynchOperation(AsyncCallback callback, HttpContext context, Object state)
    {
        _callback = callback;
        _context = context;
        _state = state;
        _completed = false;
    }


    public void StartAsyncWork()
    {
        
        ThreadPool.QueueUserWorkItem(new WaitCallback(StartAsyncTask),null);

    }
    private void StartAsyncTask(Object workItemState)
    {

        _context.Response.Write("<p>Completion IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "</p>\r\n");

        _context.Response.Write("Hello World from Async Handler!");
        _completed = true;
        _callback(this);
    }

آشنایی با فایل ASHX
در مطالب بالاتر به فایل‌های Ashx اشاره کردیم. این فایل به نام Generic Web Handler شناخته می‌شوند و می‌توانید با Add New Item این نوع فایل‌ها را اضافه کنید. این فایل شامل هیچ UI ایی نمی‌باشد و فقط شامل بخش کد می‌باشد. برای همین نسبت به aspx سبک‌تر بوده و شامل یک directive به اسم  WebHandler@ است.
مایکروسافت در MSDN نوشته است که httphandler‌ها در واقع فرآیندهایی هستند (به این فرایندها بیشتر End Point می‌گویند) که در پاسخ به درخواست‌های رسیده شده توسط asp.net application اجرا می‌شوند و بیشترین درخواست هایی هم که می‌رسد از نوع صفحات Aspx می‌باشد و موقعی که کاربری درخواست صفحه‌ی aspx می‌کند هندلرهای مربوط به page اجرا می‌شوند.
در متن بالا به خوبی روشن هست که ashx به دلیل نداشتن UI، تعداد کمتری از handlerها را در مسیر Pipeline قرار می‌دهند و اجرای آن‌ها سریعتر است. غیر از این دو هندلر aspx و ashx، هندلر توکار دیگری چون asmx که مختص وب سرویس هست و axd مربوط به اعمال trace نیز وجود دارند.

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