مطالب
الگوی طراحی Null Object

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

الگوی طراحی Null Object این مشکل را حل می‌کند که جای پاس دادن شیء Null Reference بجای شیء ای که واقعا به آن وابستگی وجود دارد و باید هر بار قبل استفاده‌ی از آن بررسی کنیم که آیا آن شیء ای که داریم با آن کار می‌کنیم نال است یا خیر، کلاسی خاصی را بسازیم که یک وابستگی غیر کاربردی است. به این معنا که قرار نیست هیچ کاری را انجام دهد و عملا یک non-functional Dependency است. این کلاس یا یک اینترفیس خاصی را پیاده سازی می‌کند و یا اینکه از یک کلاس انتزاعی ارث بری خواهد کرد؛ ولی هیچ عملکرد خاصی را نخواهد داشت. به این معنا که متدها و پراپرتی‌های این کلاس کاری را انجام نداده و یک مقدار پیشفرض و یا یک مقدار خاصی را برگشت خواهند داد. این روش به ساده سازی کد کمک خواهد کرد، چون می‌توان بدون انجام پیش شرط‌هایی مانند بررسی نال بودن یا نبودن یک شیء وابسته، از آن استفاده کرد.

این الگوی طراحی معمولا همراه با دیگر الگوهای طراحی مورد استفاده قرار می‌گیرد. بهینه‌تر است که خود کلاس Null Object به صورت Singleton پیاده سازی شود. مزیت این کار در این است که چون شیء ساخته شده از این کلاس، نه کار خاصی را انجام می‌دهد و نه حالت خاصی را نگه می‌دارد، پس ساختن شیءای از آن عملا ضرورتی نداشته و هیچگونه ارزشی ندارد و فقط سرباری را بر روی نرم افزار قرار می‌دهد. پس سزاوار است فقط به یک شیء از این کلاس اکتفا کرد و هر بار همان شیء را برگشت داد. الگوی دیگری که غالبا از الگوی Null Object در آن استفاده می‌شود، الگوی Strategy است. زمانیکه یکی از استراتژی‌ها این باشد که کار خاصی را انجام نداد و یا استراتژی مورد نظر عملکردی نداشته باشد، از الگوی Null Object استفاده می‌کنیم. الگوی دیگری که از الگوی Null Object زیاد استفاده می‌کند، الگوی Factory است. برای مثال هنگامیکه بخواهیم بر طبق شرایط برنامه یک شیء Null Reference را بسازیم و برگردانیم، از الگوی Null Object استفاده خواهیم کرد.

فرض کنید می‌خواهیم ماژولی را توسعه دهیم که وظیفه‌ی آن گزارش دادن وضعیت وقوع رخدادها است و می‌خواهیم پیام‌های وضعیت، به روش‌های مختلفی مانند ارسال ایمیل و یا ثبت لاگ در سرورهای راه دور که برای لاگ گیری تعبیه شده‌اند، انجام گیرد و در بعضی از مواقع هم می‌خواهیم برای برخی از رخداد‌ها نیاز به گزارش نباشد. در این مواقع برای استراتژی سوم از الگوی طراحی Null Object استفاده می‌کنند.


پیاده سازی الگوی طراحی Null Object

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

Client : این کلاس دارای یک وابستگی به یک کلاس دیگر است که در بعضی مواقع نیازی به این وابستگی پیدا نمی‌کند و در صورتیکه به کارکرد اصلی وابستگی نیاز پیدا نکند، متدهای داخل کلاس Null Object را اجرا می‌کند.

DependencyBase : این قسمت کلاس پایه‌ای است که به صورت Abstract بوده و شامل همه وابستگی‌هایی است که ممکن است Client به آن وابسته باشد. همچنین این بخش، کلاس پایه‌ی کلاس Null Object هم است. شایان ذکر است که بجای استفاده از کلاس Abstract می‌توان از یک Interface هم استفاده کرد؛ چون این کلاس هیچ عملکرد مشترکی را برای زیر کلاس‌هایش پیاده سازی نمی‌کند.

Dependency : این کلاس یک عملکرد واقعی از یک وابستگی است که Client به آن وابسته است.

NullObject : این همان کلاس Null Object است که به عنوان یک وابستگی توسط Client مورد استفاده قرار می‌گیرد. این کلاس هیچ عملکرد مشخصی را ندارد ولی باید تمام اعضای کلاس پایه، یعنی DependencyBase را پیاده سازی کند.

مثال زیر کدهای اصلی پیاده سازی الگوی طراحی Null Object را نشان خواهد داد که با زبان سی شارپ نوشته شده‌است. کلاس Client، وابستگی‌های خود را از طریق سازنده دریافت خواهد کرد که به آن Constructor injection گفته می‌شود. همانطور که می‌بینید در کلاس NullObject، تنها متد Operation بازنویسی شده است و داخل آن هیچ عملکرد خاصی پیاده سازی نشده است؛ زیر تنها به وجود آن نیاز است و نه عملکرد داخلی آن.

public class Client
{
    DependencyBase _dependency;
 
    public void Client(DependencyBase dependency)
    {
        _dependency = dependency;
    }
 
    public void DoSomething()
    {
        _dependency.Operation();
    }
}
 
 
public abstract class DependencyBase
{
    public abstract void Operation();
}
 
 
public class Dependency : DependencyBase
{
    public override void Operation()
    {
        Console.WriteLine("Dependency.Operation() executed");
    }
}
 
 
public class NullObject : DependencyBase
{
    public override void Operation() { }
}


یک نمونه واقعی از الگوی طراحی Null Object

در این بخش قصد داریم مثالی از الگوی استراتژی را ارائه دهیم که در یکی از استراتژی‌هایش از کلاس Null Object استفاده خواهد کرد. در این مثال کلاسی وجود دارد به نام StatusMonitor که پس از انجام کارهایی، وضعیت انجام آن را اعلام می‌کند. ۳ نوع استراتژی برای اعلام وضعیت انجام کارها متصور است که بسته به موقعیت‌های مختلف، یکی از آنها انتخاب خواهد شد. استراتژی‌های اعلام وضعیت شامل ارسال ایمیل، ارسال وضعیت به یک وب سرویس و یا اصلا اعلام نکردن وضعیت هستند. زمانیکه قصد داریم هیچگونه وضعیتی اعلام نشود، از نمونه‌ای از کلاس Null Object استفاده خواهد شد که در این مثال کلاس NullStatusReporter این وابستگی را تامین می‌کند. همه کلاس‌های استراتژی که بیان شد تنها شامل یک متد هستند که از آن برای گزارش پیام وضعیت استفاده خواهیم کرد.

کلاس‌های EmailStatusReporter و WebServiceStstusReporter در صورتیکه بتوانند به درستی پیام‌ها را گزارش دهند، مقدار true را برگشت خواهند داد و در غیر اینصورت مقدار false برگشت داده می‌شود. اما کلاس Null Object هیچ کاری را انجام نمی‌دهد و چیزی را گزارش نمی‌دهد و تنها مقدار true را برگشت خواهد داد. اینکه این کلاس چه مقداری را برگشت دهد، قراردادی است که بین Client و Dependency انجام می‌گیرد. به این نکته هم توجه بفرمایید که کلاس NullStatusReporter به صورت Singleton پیاده سازی شده است.

public class StatusMonitor
{
    StatusReporterBase _reporter;
 
    public StatusMonitor(StatusReporterBase reporter)
    {
        _reporter = reporter;
    }
 
    public void CheckStatus()
    {
        // Do something to check status
        if (!_reporter.Report("Everything's OK"))
        {
            Console.WriteLine("Failed to report status.");
        }
    }
}
 
 
public abstract class StatusReporterBase
{
    public abstract bool Report(string message);
}
 
 
public class EmailStatusReporter : StatusReporterBase
{
    public override bool Report(string message)
    {
        try
        {
            Console.WriteLine("Emailed '{0}'.", message);
            return true;
        }
        catch
        {
            return true;
            throw;
        }
    }
}
 
 
public class WebServiceStatusReporter : StatusReporterBase
{
    public override bool Report(string message)
    {
        try
        {
            Console.WriteLine("Sent '{0}' to web service.", message);
            return true;
        }
        catch
        {
            return true;
            throw;
        }
    }
}
 
 
public class NullStatusReporter : StatusReporterBase
{
    private static NullStatusReporter _instance;
    private static object _lock = new object();
 
    private NullStatusReporter() { }
 
    public static NullStatusReporter GetReporter()
    {
        lock (_lock)
        {
            if (_instance == null) _instance = new NullStatusReporter();
        }
 
        return _instance;
    }
 
    public override bool Report(string message)
    {
        return true;
    }
}


تست کلاس Null Object

برای تست کلاس StatusMonitor باید یکی از انواع استرتژی‌ها را برایش تعیین و آن را به سازنده کلاس تزریق کرد و با آن استراتژی، کلاس را تست نمود. در کد زیر از استراتژی NullObject استفاده شده‌است. پس یک نمونه‌ی آن ساخته شده و از طریق سازنده به کلاس StatusMonitor فرستاده می‌شود. سپس متد CheckStatus فراخوانی می‌گردد. اما این متد کاری را انجام نمی‌دهد و تنها مقدار true  برگشت داده می‌شود. بررسی روش‌های دیگر را به خودتان واگذار می‌کنم.

StatusReporterBase reporter = NullStatusReporter.GetReporter();
StatusMonitor monitor = new StatusMonitor(reporter);
monitor.CheckStatus();


مطالب
شروع به کار با بوت استرپ 4
روش‌های مختلف دریافت و نصب بوت استرپ 4

اولین کاری را که باید جهت شروع به کار با بوت استرپ 4 انجام داد، نصب و افزودن آن به صفحه‌ی HTML جاری است. روش‌های زیادی برای انجام اینکار وجود دارند:
الف) استفاده از نگارش SASS آن
بوت استرپ 4 در اصل مبتنی بر SASS توسعه یافته‌است و فایل‌های آن با فرمت scss. ارائه می‌شوند. مزیت کار با این روش، امکان سفارشی سازی بوت استرپ 4 و یا مشارکت در پروژه‌ی آن است و بدیهی است پس از آن باید SASS را به CSS کامپایل و مورد استفاده قرار داد.

ب) استفاده از CDN و یا Content delivery network
- مزیت آن بالا رفتن سرعت سایت با کش شدن آن در شبکه و یا شبکه‌های توزیع محتوا است.
- اما این روش محدودیت و الزام کار آنلاین با فایل‌های بوت استرپ را نیز به همراه دارد.
برای کار با CDN‌های بوت استرپ، مطابق راهنمای آن، تنها کافی است مدخل فایل css آن‌را به head صفحه و مداخل فایل‌های js ذیل را پیش از بسته شدن تگ body قرار دهید:
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
در اینجا شاید نام دو فایل، برای شما تازگی داشته باشند:
- jquery-3.3.1.slim : در اینجا slim یک نگارش بسیار کوچک از jQuery می‌باشد که بوت استرپ 4 بر مبنای آن کار می‌کند. البته در یک پروژه‌ی واقعی احتمالا نیاز به نگارش کامل آن‌را خواهید داشت و یا اگر قصد حذف کردن جی‌کوئری را دارید، این نگارش، کم‌حجم‌ترین آن است.
- popper.min.js : برای نمونه Bootstrap dropdown برای کارکرد صحیح آن در نگارش 4، نیاز به این وابستگی جدید را دارد.

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

د) استفاده از ابزارهای مدیریت بسته‌ها
روشی را که ما در اینجا از آن استفاده خواهیم کرد، دریافت و نصب وابستگی‌های مورد نیاز جهت کار با بوت استرپ 4، توسط npm است. به همین جهت یک فایل جدید package.json را با محتوای ذیل ایجاد کنید:
{
  "name": "bootstrap.4",
  "version": "1.0.0",
  "description": "client side resources of the project",
  "scripts": {},
  "author": "VahidN",
  "license": "ISC",
  "dependencies": {
    "bootstrap": "^4.1.3",
    "components-font-awesome": "5.0.6",
    "jquery": "^3.3.1",
    "popper.js": "^1.14.4"
  }
}
سپس از طریق خط فرمان به این پوشه وارد شده و دستور npm install را جهت دریافت این وابستگی‌ها صادر کنید. یکی از مزیت‌های مهم این روش، آگاه شدن خودکار از به روز رسانی‌های وابستگی‌های فوق، توسط افزونه‌هایی مانند Version Lens است.
در اینجا font-awesome را نیز مشاهده می‌کنید؛ از این جهت که بوت استرپ 4 برخلاف نگارش 3 آن، به همراه گلیف آیکن‌های پیش‌فرض آن نیست.


ایجاد قالب ابتدایی شروع به کار با بوت استرپ 4

پس از دریافت وابستگی‌های مورد نیاز جهت شروع به کار با بوت استرپ 4 که هم اکنون باید در پوشه‌ی node_modules واقع در ریشه‌ی پوشه‌ی جاری موجود باشند، در ادامه حداقل قالبی را که برای کار با آن نیاز است، مرور می‌کنیم:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta http-equiv="x-ua-compatible" content="ie=edge">
    <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.min.css">
    <link rel="stylesheet" href="/node_modules/components-font-awesome/css/fa-solid.min.css">
    <link rel="stylesheet" href="/node_modules/components-font-awesome/css/fontawesome.min.css">
    <title>Bootstrap</title>
</head>
<body>
    <div class="container">

    </div>
    <script src="/node_modules/jquery/dist/jquery.min.js"></script>
    <script src="/node_modules/popper.js/dist/umd/popper.min.js"></script>
    <script src="/node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
</body>
</html>
- در ابتدا سازگاری با edge و الزام به استفاده‌ی از آخرین نگارش IE نصب شده مشخص شده‌است.
- سپس viewport استاندارد، جهت تعیین اینکه این صفحه با ابزارهای موبایل نیز سازگار است، تعریف شده‌است.
- در قسمت head، مدخل فایل bootstrap.min.css تعریف شده‌است. همچنین مداخل مورد نیاز جهت کار با font-awesome را نیز مشاهده می‌کنید.
- پیش از بسته شدن تگ body، تعاریف jQuery، کتابخانه‌ی popper و سپس bootstrap.min.js قید شده‌اند. کتابخانه‌ی popper از مسیر umd آن دریافت شده‌است تا همه جا کار کند.


نکته‌ی مهم!
در نگارش نهایی برنامه‌ی شما، مسیرهای فایل‌های شروع شده‌ی با /node_modules/ نباید وجود داشته باشند. این فایل‌ها را بهتر است توسط ابزارهای bundling & minification یکی و سپس به صفحه اضافه کنید.


غنی سازی ویرایشگر VSCode برای کار ساده‌تر با بوت استرپ

VSCode یک ویرایشگر حرفه‌ای چندسکویی است که برای ویندوز، مک و لینوکس تهیه شده‌است. این ویرایشگر را می‌توان توسط افزونه‌های زیر برای کار ساده‌تر با بوت استرپ غنی کرد:
Bootstrap 4, Font awesome 4, Font Awesome 5 Free & Pro snippets: ساده سازی تشکیل تگ‌های بوت استرپ
Path Autocomplete: کار وارد کردن مسیر فایل‌ها و تصاویر را ساده می‌کند.
HTML CSS Support: کار آن غنی سازی intellisense این ویرایشگر جهت کار با ویژگی‌ها و همچنین کلاس‌های CSS است.
IntelliSense for CSS class names in HTML: انتخاب کلاس‌های CSS بوت استرپ را ساده‌تر می‌کند.
Live Server: کار آن راه اندازی یک وب سرور آزمایشی و سپس امکان مشاهده‌ی آنی تغییرات در برنامه و فایل HTML جاری، در مرورگر می‌باشد.


برای کار با آن، در حالیکه صفحه‌ی HTML جاری در VSCode باز است، بر روی دکمه‌ی Go Live اضافه شده‌ی در status bar آن کلیک کنید. پس از آن، یک وب سرور آزمایشی را بر روی پورت 5500 آغاز کرده و صفحه‌ی جاری را در آدرس http://127.0.0.1:5500/index.html در مرورگر پیش‌فرض سیستم نمایش می‌دهد. اکنون فایل HTML خود را در VSCode ویرایش کنید. ملاحظه خواهید کرد که بلافاصله این تغییرات در مرورگر قابل مشاهده هستند.


نگارش‌های راست به چپ بوت استرپ 4

قرار است بوت استرپ 4 نگارش رسمی راست به چپ نیز داشته باشد. به همین منظور می‌توانید در اینجا رای خود را اظهار کنید.
همچنین پروژه‌های زیر نیز چنین قابلیتی را ارائه می‌دهند:
  1. MahdiMajidzadeh/bootstrap-v4-rtl
  2. DediData/Bootstrap-RTL 
  3. GhalamborM/bootstrap4-rtl



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: Bootstrap4_01.zip
مطالب
بررسی Bad code smell ها: درخت ارث بری موازی یا Parallel inheritance hierarchy
این کد بد بو در دسته «جلوگیری کنندگان از تغییر» قرار می‌گیرد. اگر زمان ایجاد یک کلاس فرزند برای کلاسی، مجبور به ایجاد یک کلاس فرزند متناظر آن برای کلاس دیگری باشید، با این کد بد بود مواجه هستید. 
معمولا زمانی این اتفاق می‌افتد که یک درخت ارث بری به درخت ارث بری دیگری وابسته باشد. به‌طوری که هر یک از کلاس‌های موجود در آن، با یک کلاس در درخت دیگر متناظر باشند و ارتباط داشته باشند. این امر ایجاد تغییرات در کد را با مشکل مواجه خواهد کرد. 
به طور مثال:
public abstract class InvoiceGenerator 
{ 
    public abstract dynamic Generate(); 
} 
public abstract class AmountCalculator 
{ 
    public abstract decimal Calculate(dynamic item); 
}
public class StandardInvoiceGenerator : InvoiceGenerator 
{ 
    public override dynamic Generate() 
    { 
        return new object(); 
    } 
} 
public class StandardInvoiceAmountCalculator : AmountCalculator 
{ 
    public override decimal Calculate(dynamic item) 
    { 
        return 1; 
    } 
} 
public class PrepaymentInvoiceGenerator : InvoiceGenerator 
{ 
    public override dynamic Generate() 
    { 
        return new object(); 
    } 
} 
public class PrepaymentInvoiceAmountCalculator : AmountCalculator 
{ 
    public override decimal Calculate(dynamic item) 
    { 
        return 1; 
    } 
}

در این مثال کلاسی به نام InvoiceGenerator وجود دارد که کلاس پدر تولید کنندگان فاکتور است و کلاس دیگری نیز وجود دارد به نام AmountCalculator که کلاس پدر محاسبه قیمت است. هر یک از سازندگان فاکتور، یک کلاس متناظر در درخت Caluclator‌ها دارند؛ برای محاسبه قیمت آیتم‌های آن نوع فاکتور. به طور خاص در این مثال فاکتور استاندارد و فاکتور پیش پرداخت نوشته شده است  (در این مثال فقط یک سطح از ارث بری پیاده سازی شده است و تمامی کلاس‌ها مستقیما از کلاس پدر به ارث برده می‌شوند. در صورت وجود چند سطح ارث بری، مشکل پیچیده‌تر نیز خواهد شد).
حال فرض کنید قصد افزودن یک کلاس را برای ایجاد «فاکتور گزارش مخارج» (فاکتوری برای ثبت مخارجی که کارکنان زمان انجام کار برای کارفرما متحمل می‌شوند) دارید.
در این حالت نیاز است که یک کلاس ExpenseReportInvoiceGenerator ساخته شود و متناظر با آن نیاز خواهد بود کلاس ExpenseReportAmountCalculator ساخته شود. 

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

دلایل بوجود آمدن چنین بویی به سه دسته تقسیم می‌شوند: 

  • تشخیص نادرست روابط بین کلاس‌ها  
  • استفاده نامناسب از الگوهای طراحی 
  • تشخیص نادرست مسئولیت‌های کلاس‌ها و طراحی نامناسب آن 


روش‌های اصلاح این کد بد بو  

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

دوم: تبدیل درخت ارث بری به یک درخت ارث بری ناقص
در این روش می‌توان interface هایی را برای AmountCalculator و InvoiceGenerator ساخت و کلاس‌هایی که این دو interface را پیاده سازی می‌کنند؛ مانند: 
public interface IInvoiceGenerator 
{ 
    dynamic Generate(); 
} 
public interface IAmountCalculator 
{ 
    decimal Calculate(dynamic item); 
} 
public class StandardInvoiceGenerator : IInvoiceGenerator, IAmountCalculator 
{ 
    public dynamic Generate() 
    { 
        return new object(); 
    } 
    public decimal Calculate(dynamic item) 
    { 
        return 1; 
    } 
} 
public class PrepaymentInvoiceGenerator : IInvoiceGenerator, IAmountCalculator 
{ 
    public dynamic Generate() 
    { 
        return new object(); 
    } 
    public decimal Calculate(dynamic item) 
    { 
        return 1; 
    } 
}
با این تغییر، بخشی از اصل single responsibility نادیده گرفته شده‌است. اما با وجود یک درخت ارث بری فیزیکی، درخت منطقی موجود دست نخورده باقی مانده و در صورت نیاز می‌توان دوباره اقدام به جداسازی مسئولیت‌ها کرد. این جداسازی بیشتر اوقات زمانی اتفاق می‌افتد که یکی از جنبه‌های مسئولیتی یک کلاس، در جای دیگری به صورت مستقل مورد استفاده قرار گیرد. 

سوم: از بین بردن درخت ارث بری موازی 
این روش تقریبا مشابه روش ذکر شده در بخش دوم است. تنها با این تفاوت که در این روش، رابطه منطقی ارث بری موازی نیز حذف می‌شود؛ مانند:
public abstract class InvoiceGenerator 
{ 
    public abstract dynamic Generate(); 
    public abstract decimal CalculateItemAmount(dynamic item); 
} 
public class StandardInvoiceGenerator : InvoiceGenerator 
{ 
    public override dynamic Generate() 
    { 
        throw new NotImplementedException(); 
    } 
    public override decimal CalculateItemAmount(dynamic item) 
    { 
        throw new NotImplementedException(); 
    } 
} 
public class PrepaymentInvoiceGenerator : InvoiceGenerator 
{ 
    public override dynamic Generate() 
    { 
        throw new NotImplementedException(); 
    } 
    public override decimal CalculateItemAmount(dynamic item) 
    { 
        throw new NotImplementedException(); 
    } 
}
در این روش دو درخت ارث بری از نظر منطقی و فیزیکی با یکدیگر ادغام شده‌اند. البته همان طور که در بخش یک گفته شد، بهترین گزینه برای مثال ذکر شده در این مطلب، دست نخوردن آن است. این روش زمانی مناسب است که موجودیت‌های ادغام شده از نظر ذاتی به قدری به یکدیگر شباهت داشته باشند که عملا بتوانیم آن‌ها را یک موجودیت مستقل در نظر بگیریم. 

جمع بندی 

روش‌های رفع بوی بد «درخت‌های ارث بری موازی» به زمینه ایجاد آنها وابستگی شدیدی دارد. به همین دلیل نمی‌توان راه حلی کلی را برای آن ارائه داد. سه روش ذکر شده در این مطلب در واقع سه نوع رفتاری هستند که می‌توان با چنین بوی بدی داشت. بسته به زمینه ایجاد این بوی بد و شرایط کد می‌توان هر یک از این روش‌ها را انتخاب نمود. نکاتی مانند سهولت نگهداری، تست نویسی آسان و خوانایی کد، فاکتورهایی هستند که می‌توانند در تصمیم گیری مد نظر قرار گیرند.
مطالب
مبانی TypeScript؛ تنظیمات کامپایلر
برای کامپایل کدهای TypeScript به جاوا اسکریپت، علاوه بر پارامترهای کامپایلر، از تنظیمات فایل مخصوصی به نام tsconfig.json نیز استفاده می‌شود که این موارد را در قسمت جاری بررسی خواهیم کرد.


نحوه‌ی اعمال تنظیمات کامپایلر TypeScript

روش‌های متفاوتی جهت اعمال تنظیمات کامپایلر TypeScript وجود دارند:
الف) ذکر پارامترها و سوئیچ‌های کامپایلر خط فرمان tsc به صورت مستقیم.
ب) بعضی از ادیتور‌ها و IDEها این پارامترها را به صورت گزینه‌ها و دیالوگ‌هایی ارائه می‌دهند.
ج) استفاده از یک Build task، همانند روشی که در تنظیمات VSCode در مطلب «چرا TypeScript» مشاهده کردید.
د) ذکر تنظیمات کامپایلر، در فایل مخصوصی به نام tsconfig.json.


گزینه‌های متداول کامپایلر TypeScript

گزینه‌های کامپایلر TypeScript نسبتا قابل توجه هستند و لیست کامل و به روز آن‌ها را در هندبوک تایپ‌اسکریپت می‌توانید مشاهده کنید. در اینجا تعدادی از مهم‌ترین‌ها را بررسی خواهیم کرد:
- سوئیچ module-- جهت مشخص سازی فرمت خروجی ماژول‌های TypeScript بکار می‌رود. در مطلب بررسی ماژول‌ها عنوان شد که TypeScript قادر است ماژول‌های تعریف شده را با سایر فرمت‌های متداول جاوا اسکریپت مانند common.js و amd نیز تولید کند. سوئیچ module-- جهت تنظیم این گزینه درنظر گرفته شده‌است. خلاصه‌ای این سوئیچ نیز m-- است. این سوئیچ یکی از مقادیر commonjs, amd, system, es2015 را می‌پذیرد. اگر es2015 را مشخص کردید، نیاز است target را نیز به ES6 تنظیم کنید.
- سوئیچ moduleResolution-- نحوه‌ی یافتن ارجاعات به ماژول‌ها را مشخص می‌کند. در اینجا روش‌های node.js و کلاسیک را می‌توان قید کرد.
- سوئیچ target-- برای تعیین نگارش خروجی جاوا اسکریپت تولیدی بکار می‌رود. حالت پیش فرض آن ES3 است و ES5 و ES6 را نیز پشتیبانی می‌کند.
- گزینه‌ی watch-- کامپایلر را در حالت watch نگه می‌دارد. در این حالت تغییرات آخرین تاریخ نوشته شدن در فایل‌های ts بررسی شده و در صورت یافتن تغییری، بلافاصله خروجی js آن‌ها تهیه می‌شود.
- سوئیچ outDir-- برای مشخص کردن پوشه‌ی فایل‌های تولیدی نهایی بکار می‌رود.
- گزینه‌ی noImplicitAny-- برای ممنوع کردن نوع‌های Any متغیرها به صورت پیش فرض است و در این حالت خطای کامپایلری را مشاهده خواهید کرد. استفاده از این گزینه به این معنا نیست که دیگر نمی‌توان از نوع Any استفاده کرد؛ بلکه به این معنا است که در صورت نیاز باید آن‌را به صورت صریح قید کنید.

یک مثال:
در VSCode و در پوشه‌ی vscode. آن، در تنظیمات فایل tasks.json، چنین گزینه‌هایی را می‌توان برای کامپایلر tsc، ذکر کرد:
"args": ["--target", "ES5",
            "--outDir", "js",
            "--module", "commonjs",
            "--sourceMap",
            "--watch",
             "app.ts"],
به این ترتیب خروجی جاوا اسکریپت آن با فرمت ES 5 بوده و فایل‌های نهایی آن در پوشه‌ی js، در ریشه‌ی پروژه نوشته خواهند شد. همچنین فرمت ماژول‌های خروجی آن نیز به commonjs تنظیم شده‌است. این کامپایلر sourceMapها را جهت امکان دیباگ بهتر کدها تولید کرده و در حالت watch قرار دارد.


بررسی کاربرد فایل tsconfig.json

فایل ویژه‌ی tsconfig.json در نگارش 1.5 تایپ‌اسکریپت معرفی گردید. هدف از این فایل، ساده کردن تعریف پارامترهای کامپایلر است؛ البته الزامی به استفاده‌ی از آن وجود ندارد.
این فایل مزایای ذیل را به همراه دارد:
الف) محل قرارگیری آن، ریشه‌ی پروژه‌ی TypeScript را مشخص می‌کند.
ب) تنظیمات ذکر شده‌ی در این فایل، به تمام فایل‌های موجود در پوشه و زیر پوشه‌های محل قرارگیری آن به صورت پیش فرض اعمال می‌شوند.
هنگامیکه در تنظیمات کامپایلر tsc، نام فایل یا فایل‌های ts ایی را ذکر نمی‌کنید، این کامپایلر در ابتدا به دنبال فایل tsconfig.json می‌گردد و بر این اساس فایل‌های ts را پردازش خواهد کرد. اگر مانند مثال فوق، در انتهای پارامترها، نام فایلی را ذکر کنید، از فایل tsconfig.json صرفنظر خواهد شد.
یک نکته: برای ذکر صریح محل فایل tsconfig از پارامتر project استفاده کنید:
 tsc --project ./lib
ج) امکان ذکر گزینه‌های کامپایلر را فراهم می‌کند.
در این حالت می‌توان کامپایلر tsc را بدون پارامتری اجرا کرد و این برنامه اطلاعات مورد نیاز خود را از فایل tsconfig.json دریافت خواهد کرد. باید دقت داشت، هر سوئیچی که در خط فرمان ذکر شود، پارامترهای معادل ذکر شده‌ی در فایل tsconfig.json را بازنویسی می‌کند. بنابراین در صورت وجود این فایل، می‌توان خاصیت args مثال قبل را به یک آرایه‌ی خالی تنظیم کرد.
د) امکان مشخص سازی الحاق و عدم الحاق فایل‌های ts را به همراه دارد.
 {
     "compilerOptions": {
                      "target": "es5",
                      "outDir": "js",
                      "module":"commonjs",
                      "sourceMap":true
     },
     "files": [
             "app.ts",
             "classes.ts"
     ]
}
نمونه‌ای از محتوای این فایل JSON را در مثال فوق مشاهده می‌کنید. در خاصیت compilerOptions آن، امکان تعریف پارامترهای کامپایلر وجود دارند؛ مانند تعیین نوع جاوا اسکریپت خروجی و پوشه‌ی نهایی آن. خاصیت آرایه‌ی files آن، برای ذکر لیست فایل‌هایی است که باید به کامپایلر ارسال شوند.
کار خاصیت files الحاق و include است. اگر می‌خواهید از پوشه‌ها و یا فایل‌هایی صرفنظر شود، از خاصیت exclude استفاده کنید:
{
      "compilerOptions": {
                     "target": "es5",
                     "outDir": "js"
      },
      "exclude": [
               "node_modules",
               "lib"
      ]
}
باید دقت داشت که در اینجا تنها یکی از خواص files و یا exclude را می‌توان ذکر کرد. اگر هر دو را با هم ذکر کنید، تنها از خاصیت files استفاده می‌شود.

یک نکته
در VSCode داخل فایل tsconfig.json با فشردن ctrl+space، به یک intellisense حاوی گزینه‌های تکمیل کننده‌ی آن خواهید رسید.


ساده سازی الحاق فایل‌های تعاریف نوع‌ها

در مطلب «مبانی TypeScript؛ تهیه فایل‌های تعاریف نوع‌ها» با فایل‌های ویژه‌ی d.ts. آشنا شدیم. استفاده‌ی از این فایل‌ها به همراه ذکر اجباری reference path مرتبط در ابتدای هر فایل ts است. این‌کار اضافی را با استفاده از فایل tsconfig.json می‌توان حذف کرد:
{
         "compilerOptions": {
                  "target": "es5",
                  "outDir": "js",
                  "module": "commonjs",
                  "sourceMap": true,
                  "watch": true
         },
         "files": [
                "app.ts",
                "typings/main.d.ts"
         ]
}
در اینجا با ذکر typings/main.d.ts در قسمت files، اطلاعات موجود در این فایل d.ts. به صورت سراسری به تمام فایل‌های ts موجود در پروژه‌ی جاری اعمال می‌شود و دیگر نیازی به ذکر صریح reference path آن نیست.
مطالب
آشنایی با CLR: قسمت نوزدهم
در فصل دوم کتاب تا به الان یاد گرفتیم چگونه ماژول‌ها را کامپایل کنیم و چگونه آن‌ها را در یک اسمبلی قرار دهیم. حال وقت آن فرا رسیده است که با بسته بندی کردن (Package) و انتشار آن (Deploy) به طوری که کاربران بتوانند برنامه را اجرا کنند آشنا شویم.

نصب برنامه از طریق فروشگاه ویندوز
در فروشگاه ویندوز Windows Store Apps قوانین سخت و شدیدی برای بسته بندی کردن اسمبلی‌ها وجود دارد. ویژوال استودیو تمام اسمبلی‌های مورد نیاز برنامه را در یک فایل با پسوند appx قرار داده و آن را به سمت فروشگاه آپلود می‌کند. هر کاربری که این فایل appx را نصب کند، همه‌ی اسمبلی‌هایی را که در دایرکتوری مربوطه قرار گرفته است، توسط CLR بار شده و آیکن برنامه هم در صفحه‌ی start ویندوز قرار می‌گیرد و اگر دیگر کاربران همان سیستم هم این فایل appx را نصب کنند، از آنجا که قبلا روی سیستم موجود هست، تنها آیکن برنامه به صفحه‌ی start اضافه می‌گردد و برای حذف هم تنها آیکن برنامه از روی این صفحه حذف می‌شود؛ مگر اینکه تنها کاربری باشد که این برنامه را نصب کرده‌است که در آن صورت کلا همه‌ی اسمبلی‌های آن از روی سیستم حذف می‌شود.
در صورتیکه کاربرهای مختلف نسخه‌های مختلفی از همان برنامه را روی سیستم نصب کنند، برای اسمبلی‌ها هر کدام یک دایرکتوری ایجاد شده و به ازای نسخه‌ی نصب شده آن کاربر، یکی از این دایرکتوری‌ها مورد استفاده قرار می‌گیرند. کاربران مختلف می‌توانند روی سیستم به طور همزمان از نسخه‌های مختلف برنامه استفاده کنند.

روش‌های پکیج گذاری
برای برنامه‌های دسکتاپ که ربطی به فروشگاه ندارند و بین ایرانیان طرفدار زیادی دارد، نیازی به استفاده از هیچ روش خاصی نیست و یک کپی معمولی هم کفایت می‌کند. همه‌ی فایل‌های مثل اسمبلی، باید در یک دایرکتوری قرار گرفته و به روش کپی کردن آن را انتقال داد. یا برای بسته بندی از یک فایل batch کمک گرفت و آن را روی سیستم نصب کرد و نیازی به هیچ تغییری در رجیستری نیست. برای حذف برنامه هم، حذف معمولی دایرکتوری مربوطه کفایت می‌کند.
البته گزینه‌های دیگری هم برای پکیج کردن این نوع برنامه‌ها وجود دارند:
یکی از روش‌های پکیج کردن فایل‌ها به صورت cab هست که عموما برای سناریوهای اینترنتی و فشرده سازی و کاهش زمان دانلود به کار می‌رود.
روش دوم استفاده از پکیج MSI است که توسط سرویس نصب مایکروسافت Microsoft Installer Service یا MSIExec.exe انجام می‌گیرد. فایل‌های MSI به اسمبلی‌ها اجازه می‌دهند که بر اساس زمان تقاضای CLR برای بارگیری اولیه نصب شوند. البته این ویژگی جدیدی نیست و برای فایل‌های exe یا dll مدیریت نشده هم به کار می‌رود.

استفاده از نصاب سازها
بهتر هست که برای انتشار برنامه از برنامه‌های نصاب سازی استفاده کنید که با واسطی جذاب‌تر به نصب پرداخته و امکاناتی از قبیل shotrcut‌ها، حذف و بازیابی و نصب و .. را هم به کاربر می‌دهند.
نصاب سازهای متفاوتی وجود دارند که در زیر به تعدادی از آنها اشاره می‌کنیم:
Install Shield (+ ) : این برنامه نسخه‌های متفاوتی را با قیمت‌های متفاوتی، عرضه می‌کند و در این زمینه، جزء بهترین‌ها نام برده می‌شود. حتی ویژگی‌های مخصوصی هم برای ویژوال استودیو دارد. شرکت سازنده، برنامه‌ی دیگری را هم اخیر تحت نام Install Anywhere عرضه کرده است که اجازه می‌دهد از روی یک برنامه برای پلتفرم‌های مختلف setup بسازد.

NSIS : این برنامه هم در زمینه‌ی ساخت setup محبوبیت زیادی دارد. این برنامه به صورت متن باز منتشر شده و رایگان است. امکانات این برنامه ساده است و برای راه اندازی سریع یک setup و اجرای راحت آن توسط کاربر، کاملا کاربردی است.

Tarma Installmate : این نرم افزار نسبت به InstallShield ساده‌تر و کم حجم‌تر است. حداقل برای برنامه‌های عادی امکانات مناسبی دارد.

DeployMaster : یک برنامه‌ی دیگر با امکانات حرفه‌ای جهت انشار برنامه‌های دسکتاپ، که از ویندوز 98 تا 8.1 را در حال حاضر پشتیبانی می‌کند.

QSetup Installation Suite : یک برنامه‌ی نصب حرفه‌ای که فایل نهایی آن می‌تواند به دو فرمت exe یا MSI باشد و قابلیت‌هایی چون پشتیبانی از زبان فارسی، ورود لایسنس، سریال نرم افزار و ... را نیز پشتیبانی می‌کند.

Inno Setup : این برنامه هم امکانات خوبی را برای ساخت یک نصاب ساز دارد و همچنین از زبان پاسکال جهت اسکریپت نویسی جهت توسعه امکانات بهره می‌برد.

Visual Patch : وب سایت پی سی دانلود این برنامه را اینگونه توضیح می‌دهد:
نرم افزار Visual Patch یک ابزار توسعه یافته‌ی نرم افزاری برای ساخت پچ و آپدیت برنامه‌ها می‌باشد. این سازنده پچ باینری، استفاده از فشرده سازی داده DeltaMAX برای سریع‌تر کردن توسعه‌ی نرم افزار، یکپارچگی با نصب نرم افزار و ابزار‌های مدیریت پچ از فروشندگانی نظیر Installshield, Lumension, Patchlink, Shavlik, Indigo Rose و ...، را به طور برجسته نمایان ساخته است.
با استفاده از این ابزار پچ کردن برنامه‌ها که برای توسعه دهندگان نرم افزار و برنامه نویسان طراحی شده است، توزیع نرم افزار و سیستم گسترش پچ بهبود می‌یابد. Visual Patch الگوریتم‌های فشرده سازی و state-of- the-art binary differencing را نمایان می‌سازد و این کمک می‌کند که شما به کوچکتر شدن و بهتر شدن پچ‌های نرم افزار اطمینان داشته باشید.  
و ...

انتشار توسط ویژوال استودیو
ویژوال استودیو هم امکانات خوبی برای انتشار در بخش Properties پروژه، برگه‌ی publish ارائه می‌کند و فایل MSI نتیجه را به سمت وب سرور، FTP Server یا روی دیسک ارسال میکند. یکی از خصوصیات خوب این روش این است که میتواند پیش نیازهایی مانند فریم ورک دات نت یا sql server Express را به سیستم اضافه کنید؛ در نهایت با مزیت آپدیت و نصب تک کلیکی، کاربر، برنامه را بر روی سیستم نصب کند.

اسمبلی‌های انتشاریافته اختصاصی
در روش‌هایی که ذکر کردیم، از آنجا که اسمبلی‌ها در همان شاخه یا دایرکتوری برنامه قرار گرفته‌اند و نمی‌توان آن‌ها را با برنامه‌های دیگر به اشتراک گذاشت (مگر اینکه برنامه دیگری را هم در همان دایرکتوری قرار داد) به این روش Privately Deployed Assemblies می‌گویند. این روش برگ برنده بزرگی برای برنامه نویسان، کاربران و مدیران سیستم‌ها محسوب می‌شود. زیرا که جابجایی آنها راحت بوده و CLR در همانجا اسمبلی‌ها را در حافظه بار کرده و اجرا می‌کند. در این نوع برنامه‌ها عملیات نصب/جابجایی/ حذف به راحتی صورتی میگرد و نیازی به تنظیمات خارجی مانند رجیستری ندارد. یکی از خصوصیات مهمی که دارد این هست که جداول متادیتا به اسمبلی اشاره می‌کنند که برنامه بر پایه آن ساخته شده و با آن تست شده است؛ نه با اسمبلی موجود دیگر در سیستم که شاید نام نوع مورد استفاده آن یا اسمبلی آن به طور تصادفی با آن یکی است.
در مقالات آتی در مورد اشتراک گذاری اسمبلی‌ها بین چند برنامه مفصل‌تر صحبت خواهیم کرد.

مطالب
تبادل داده ها بین لایه ها- قسمت اول

معماری لایه بندی شده، یک معماری بسیار همه گیر می‌باشد. به این خاطر که به راحتی SOC ، decoupling و قدرت درک کد را بسیار بالا می‌برد. امروزه کمتر برنامه نویس و فعال حوضه‌ی نرم افزاری است که با لایه‌های کلی و وظایف آنها آشنا نباشد ( UI layer آنچه که ما می‌بینیم، middle layer   برای مقاصد منطق کاری، data access layer برای هندل کردن دسترسی به داده‌ها). اما مسئله ای که بیشتر برنامه نویسان و توسعه دهندگان نرم افزار با استاندارد‌های آن آشنا نیستند، راه‌های تبادل داده‌ها مابین layer ‌ها می‌باشد. در این مقاله سعی داریم راه‌های تبادل داده‌ها را مابین لایه‌ها، تشریح کنیم. 

Layers  و Tiers با هم متفاوت هستند

Layer  با Tier متفاوت است. هنگامیکه در مورد مفهوم layer و Tier دچار شک شدید، دیاگرام ذیل می‌تواند به شما بسیار کمک کند. layer به مجزاسازی منطقی کد و Tier هم به مجزا سازی فیزیکی در ماشین‌های مختلف اطلاق می‌شود. توجه داشته باشید که این نکته یک شفاف سازی کلی در مورد یک مسئله مهم بود.


داده‌های وارد شونده( incoming ) و خارج شونده( outgoing )

ما باید تبادل داده‌ها را از دو جنبه مورد بررسی قرار دهیم؛ اول اینکه داده‌ها چگونه به سمت لایه Data Access می‌روند، دوم اینکه داده‌ها چگونه به لایه UI پاس می‌شوند، در ادامه شما دلیل این مجزا سازی را درک خواهید کرد. 

روش اول: Non-uniform

این روش اولین روش و احتمالا عمومی‌ترین روش می‌باشد. خوب، اجازه دهید از لایه‌ی UI به لایه DAL شروع کنیم. داده‌ها از لایه UI به Middle با استفاده از getter ‌ها و setter ‌ها ارسال خواهد شد. کد ذیل این مسئله را به روشنی نمایش می‌دهد.

Customer objCust = new Customer();
objCust.CustomerCode = "c001";
objCust.CustomerName = "Shivprasad";

بعد از آن، از لایه Middle  به لایه Data Access داده‌ها با استفاده از مجزاسازی به وسیله comma و آرایه‌ها و سایر روش‌های  non-uniform پاس داده می‌شوند. در کد ذیل به متد Add دقت کنید که چگونه فراخوانی به لایه Data Access را با استفاده از پارامتر‌های ورودی انجام می‌دهد. 

public class Customer
{
    private string _CustomerName = "";
    private string _CustomerCode = "";
    public string CustomerCode
    {
        get { return _CustomerCode; }
        set { _CustomerCode = value; }
    }
    public string CustomerName
    {
        get { return _CustomerName; }
        set { _CustomerName = value; }
    }
    public void Add()
    {
        CustomerDal obj = new CustomerDal();
        obj.Add(_CustomerName,_CustomerCode);
    }
}

کد ذیل، متد add در لایه Data Access را با استفاده از دو متد نمایش می‌دهد. 

public class CustomerDal
{
    public bool Add(string CustomerName,string CustomerCode)
    {
        // Insert data in to DB
    }
}

بنابراین اگر بخواهیم  به صورت خلاصه نحوه پاس دادن داده‌ها را در روش non-uniform  بیان کنیم، شکل ذیل به زیبایی این مسئله را نشان می‌دهد. 

· از لایه UI به لایه Middle  با استفاده از setter و getter

· از لایه Middle به لایه  data access با استفاده از comma ، input ، array 

حال نوبت این است بررسی کنیم که چگونه داده‌ها از DAL به UI   در روش non-uniform پاس خواهند شد. بنابراین اجازه دهید که اول از UI شروع کنیم. از لایه UI داده‌ها با استفاده از object ‌های لایه Middle  واکشی می‌شوند.   

Customer obj = new Customer();
List<Customer> oCustomers = obj.getCustomers();

از لایه Middle هم داده‌ها با استفاده از dataset ، datatable و xml پاس خواهند شد. مهمترین مسئله برای لایه middle ، loop بر روی dataset و تبدیل آن به strong type object  ها می‌باشد. برای مثال می‌توانید کد تابع getCustomers که بر روی dataset ، loop می‌زند و یک لیست از Customer ‌ها را آماده می‌کند در ذیل مشاهده کنید. این تبدیل باید انجام شود، به این دلیل که UI به کلاس‌های strongly typed دسترسی دارد. 

public class Customer
{
    private string _CustomerName = "";
    private string _CustomerCode = "";
    public string CustomerCode
    {
        get { return _CustomerCode; }
        set { _CustomerCode = value; }
    }
    public string CustomerName
    {
        get { return _CustomerName; }
        set { _CustomerName = value; }
    }
    public List<Customer> getCustomers()
    {
        CustomerDal obj = new CustomerDal();
        DataSet ds = obj.getCustomers();
        List<Customer> oCustomers = new List<Customer>();
        foreach (DataRow orow in ds.Tables[0].Rows)
        {
            // Fill the list
        }
        return oCustomers;
    }
}

با انجام این تبدیل به یکی از بزرگترین اهداف معماری لایه بندی شده می‌رسیم؛ یعنی اینکه « UI نمی‌تواند به طور مستقیم به کامپوننت‌های لایه Data Access مانند ADO.NET ، OLEDB و غیره دستیابی داشته باشد. با این روش اگر ما در ادامه متدولوژی Data Access را تغییر دهیم تاثیری بر روی لایه UI نمی‌گذارد.» آخرین مسئله اینکه کلاس CustomerDal یک Dataset  را با استفاده از ADO.NET بر می‌گرداند و Middle از آن استفاده می‌کند. 

public class CustomerDal
{
    public DataSet getCustomers()
    {
        // fetch customer records
        return new DataSet();
    }
}

حال اگر بخواهیم حرکت داده‌ها را به لایه UI، به صورت خلاصه بیان کنیم، شکل ذیل کامل این مسئله را نشان می‌دهد. 

· داده‌ها از لایه DAL به لایه  Middle با استفاده از Dataset ، Datareader ،  XML ارسال خواهند شد. 

· از لایه Middle به UI  از strongly typed classes استفاده می‌شود. 


مزایا و معایب روش non-uniform

یکی از مزایای non-uniform

· به راحتی قابل پیاده سازی می‌باشد، در مواردی که روش data access تغییر نمی‌کند این روش کارآیی لازم را دارد.

تعدادی از معایب  این روش

· به خاطر اینکه یک ساختار uniform نداریم، بنابراین نیاز داریم که همیشه در هنگام عبور از یک لایه به یک لایه‌ی دیگر از یک ساختار به یک ساختار دیگر تبدیل را انجام دهیم.

· برنامه نویسان از روش‌های خودشان برای پاس دیتا استفاده می‌کنند؛ بنابراین این مسئله خود باعث پیچیدگی می‌شود.

· اگر برای مثال شما بخواهید متدولوژی Data Access خود را تغییر دهید، تغییرات بر تمام لایه‌ها تاثیر می‌گذارد. 

مطالب
نحوه‌ی محاسبه‌ی هش کلمات عبور کاربران در ASP.NET Identity
روش‌های زیادی برای ذخیره سازی کلمات عبور وجود دارند که اغلب آن‌ها نیز نادرست هستند. برای نمونه شاید ذخیره سازی کلمات عبور، به صورت رمزنگاری شده، ایده‌ی خوبی به نظر برسد؛ اما با دسترسی به این کلمات عبور، امکان رمزگشایی آن‌ها، توسط مهاجم وجود داشته و همین مساله می‌تواند امنیت افرادی را که در چندین سایت، از یک کلمه‌ی عبور استفاده می‌کنند، به خطر اندازد.
در این حالت هش کردن کلمات عبور ایده‌ی بهتر است. هش‌ها روش‌هایی یک طرفه هستند که با داشتن نتیجه‌ی نهایی آن‌ها، نمی‌توان به اصل کلمه‌ی عبور مورد استفاده دسترسی پیدا کرد. برای بهبود امنیت هش‌های تولیدی، می‌توان از مفهومی به نام Salt نیز استفاده نمود. Salt در اصل یک رشته‌ی تصادفی است که پیش از هش شدن نهایی کلمه‌ی عبور، به آن اضافه شده و سپس حاصل این جمع، هش خواهد شد. اهمیت این مساله در بالا بردن زمان یافتن کلمه‌ی عبور اصلی از روی هش نهایی است (توسط روش‌هایی مانند brute force یا امتحان کردن بازه‌ی وسیعی از عبارات قابل تصور).
اما واقعیت این است که حتی استفاده از یک Salt نیز نمی‌تواند امنیت بازیابی کلمات عبور هش شده را تضمین کند. برای مثال نرم افزارهایی موجود هستند که با استفاده از پرداش موازی قادرند بیش از 60 میلیارد هش را در یک ثانیه آزمایش کنند و البته این کارآیی، برای کار با هش‌های متداولی مانند MD5 و SHA1 بهینه سازی شده‌است.


روش هش کردن کلمات عبور در ASP.NET Identity

ASP.NET Identity 2.x که در حال حاضر آخرین نگارش تکامل یافته‌ی روش‌های امنیتی توصیه شده‌ی توسط مایکروسافت، برای برنامه‌های وب است، از استانداردی به نام RFC 2898 و الگوریتم PKDBF2 برای هش کردن کلمات عبور استفاده می‌کند. مهم‌ترین مزیت این روش خاص، کندتر شدن الگوریتم آن با بالا رفتن تعداد سعی‌های ممکن است؛ برخلاف الگوریتم‌هایی مانند MD5 یا SHA1 که اساسا برای رسیدن به نتیجه، در کمترین زمان ممکن طراحی شده‌اند.
PBKDF2 یا password-based key derivation function جزئی از استاندارد RSA نیز هست (PKCS #5 version 2.0). در این الگوریتم، تعداد بار تکرار، یک Salt و یک کلمه‌ی عبور تصادفی جهت بالا بردن انتروپی (بی‌نظمی) کلمه‌ی عبور اصلی، به آن اضافه می‌شوند. از تعداد بار تکرار برای تکرار الگوریتم هش کردن اطلاعات، به تعداد باری که مشخص شده‌است، استفاده می‌گردد. همین تکرار است که سبب کندشدن محاسبه‌ی هش می‌گردد. عدد معمولی که برای این حالت توصیه شده‌است، 50 هزار است.
این استاندارد در دات نت توسط کلاس Rfc2898DeriveBytes پیاده سازی شده‌است که در ذیل مثالی را در مورد نحوه‌ی استفاده‌ی عمومی از آن، مشاهده می‌کنید:
using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Text;
 
namespace IdentityHash
{
    public static class PBKDF2
    {
        public static byte[] GenerateSalt()
        {
            using (var randomNumberGenerator = new RNGCryptoServiceProvider())
            {
                var randomNumber = new byte[32];
                randomNumberGenerator.GetBytes(randomNumber);
                return randomNumber;
            }
        }
 
        public static byte[] HashPassword(byte[] toBeHashed, byte[] salt, int numberOfRounds)
        {
            using (var rfc2898 = new Rfc2898DeriveBytes(toBeHashed, salt, numberOfRounds))
            {
                return rfc2898.GetBytes(32);
 
            }
        }
    }
 
    class Program
    {
        static void Main(string[] args)
        {
            var passwordToHash = "VeryComplexPassword";
            hashPassword(passwordToHash, 50000);
            Console.ReadLine();
        }
 
        private static void hashPassword(string passwordToHash, int numberOfRounds)
        {
            var sw = new Stopwatch();
            sw.Start();
            var hashedPassword = PBKDF2.HashPassword(
                                        Encoding.UTF8.GetBytes(passwordToHash),
                                        PBKDF2.GenerateSalt(),
                                        numberOfRounds);
            sw.Stop();
            Console.WriteLine();
            Console.WriteLine("Password to hash : {0}", passwordToHash);
            Console.WriteLine("Hashed Password : {0}", Convert.ToBase64String(hashedPassword));
            Console.WriteLine("Iterations <{0}> Elapsed Time : {1}ms", numberOfRounds, sw.ElapsedMilliseconds);
        }
    }
}
شیء Rfc2898DeriveBytes برای تشکیل، نیاز به کلمه‌ی عبوری که قرار است هش شود به صورت آرایه‌ای از بایت‌ها، یک Salt و یک عدد اتفاقی دارد. این Salt توسط شیء RNGCryptoServiceProvider ایجاد شده‌است و همچنین نیازی نیست تا به صورت مخفی نگه‌داری شود. آن‌را  می‌توان در فیلدی مجزا، در کنار کلمه‌ی عبور اصلی ذخیره سازی کرد. نتیجه‌ی نهایی، توسط متد rfc2898.GetBytes دریافت می‌گردد. پارامتر 32 آن به معنای 256 بیت بودن اندازه‌ی هش تولیدی است. 32 حداقل مقداری است که بهتر است انتخاب شود.
پیش فرض‌های پیاده سازی Rfc2898DeriveBytes استفاده از الگوریتم SHA1 با 1000 بار تکرار است؛ چیزی که دقیقا در ASP.NET Identity 2.x بکار رفته‌است.


تفاوت‌های الگوریتم‌های هش کردن اطلاعات در نگارش‌های مختلف ASP.NET Identity

اگر به سورس نگارش سوم ASP.NET Identity مراجعه کنیم، یک چنین کامنتی در ابتدای آن قابل مشاهده است:
 /* =======================
* HASHED PASSWORD FORMATS
* =======================
*
* Version 2:
* PBKDF2 with HMAC-SHA1, 128-bit salt, 256-bit subkey, 1000 iterations.
* (See also: SDL crypto guidelines v5.1, Part III)
* Format: { 0x00, salt, subkey }
*
* Version 3:
* PBKDF2 with HMAC-SHA256, 128-bit salt, 256-bit subkey, 10000 iterations.
* Format: { 0x01, prf (UInt32), iter count (UInt32), salt length (UInt32), salt, subkey }
* (All UInt32s are stored big-endian.)
*/
در نگارش دوم آن از الگوریتم PBKDF2 با هزار بار تکرار و در نگارش سوم با 10 هزار بار تکرار، استفاده شده‌است. در این بین، الگوریتم پیش فرض HMAC-SHA1 نگارش‌های 2 نیز به HMAC-SHA256 در نگارش 3، تغییر کرده‌است.
در یک چنین حالتی بانک اطلاعاتی ASP.NET Identity 2.x شما با نگارش بعدی سازگار نخواهد بود و تمام کلمات عبور آن باید مجددا ریست شده و مطابق فرمت جدید هش شوند. بنابراین امکان انتخاب الگوریتم هش کردن را نیز پیش بینی کرده‌اند.

در نگارش دوم ASP.NET Identity، متد هش کردن یک کلمه‌ی عبور، چنین شکلی را دارد:
public static string HashPassword(string password, int numberOfRounds = 1000)
{
    if (password == null)
        throw new ArgumentNullException("password");
 
    byte[] saltBytes;
    byte[] hashedPasswordBytes;
    using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, 16, numberOfRounds))
    {
        saltBytes = rfc2898DeriveBytes.Salt;
        hashedPasswordBytes = rfc2898DeriveBytes.GetBytes(32);
    }
    var outArray = new byte[49];
    Buffer.BlockCopy(saltBytes, 0, outArray, 1, 16);
    Buffer.BlockCopy(hashedPasswordBytes, 0, outArray, 17, 32);
    return Convert.ToBase64String(outArray);
}
تفاوت این روش با مثال ابتدای بحث، مشخص کردن طول salt در متد Rfc2898DeriveBytes است؛ بجای محاسبه‌ی اولیه‌ی آن. در این حالت متد Rfc2898DeriveBytes مقدار salt را به صورت خودکار محاسبه می‌کند. این salt بجای ذخیره شدن در یک فیلد جداگانه، به ابتدای مقدار هش شده اضافه گردیده و به صورت یک رشته‌ی base64 ذخیره می‌شود. در نگارش سوم، از کلاس ویژه‌ی RandomNumberGenerator برای محاسبه‌ی Salt استفاده شده‌است.
مطالب
OutputCache در ASP.NET MVC
مقدمه

OutputCaching باعث می‌شود خروجیِ یک اکشن متد در حافظه نگهداری شود. با اعمال این نوع کشینگ، ASP.NET در خواست‌های بعدی به این اکشن را تنها با بازگرداندن همان مقدار قبلی ِ نگهداری شده در کش، پاسخ می‌دهد. در حقیقت با OutputCaching از تکرار چند باره کد درون یک اکشن در فراخوانی‌های مختلف جلوگیری کرده‌ایم. کش کردن باعث می‌شود که کارایی و سرعت سایت افزایش یابد؛ اما باید دقت کنیم که چه موقع و چرا از کَش کردن استفاده میکنیم و چه موقع باید از این کار امتناع کرد. 


فواید کَش کردن

- انجام عملیات هزینه دار فقط یکبار صورت میگیرد. (هزینه از لحاظ فشار روی حافظه سرور و کاهش سرعت بالا آمدن سایت)

- بار روی سرور در زمان‌های پیک کاهش می‌یابد.

- سرعت بالا آمدن سایت بیشتر میشود. 


چه زمانی باید کَش کرد؟ 

- وقتی محتوای نمایشی برای همه کاربران یکسان است.

- وقتی محتوای نمایشی برای نمایش داده شدن، فشار زیادی روی سرور تحمیل میکند.

- وقتی محتوای نمایشی به شکل مکرر در طول روز باید نمایش داده شود.

- وقتی محتوای نمایشی به طور مکرر آپدیت نمی‌شود. (در مورد تعریف کیفیت "مکرر"، برنامه نویس بهترین تصمیم گیرنده است) 


طرح مساله 

فرض کنید صفحه اول سایت شما دارای بخش‌های زیر است :

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

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

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

فشار ترافیکی(ریکوئست‌های زیاد) و آپدیت‌های روزانه‌ی دیتابیس را، در دو کفه ترازو قرار دهید؛ چه کار باید کرد؟ این تصمیمی است که شما باید بگیرید. نگرانی خود را در زمینه آپدیت‌های روزانه و ساعتی کمتر کنید؛ در ادامه راهی را معرفی میکنیم که آپدیت‌های هر از گاهِ شما، در پاسخِ ریکوئست‌ها دیده شوند. کمی کفه‌ی کش کردن را سنگین کنید.

به هر حال، فعال کردن قابلیت کش کردن برای یک اکشن، بسیار ساده است، کافیست ویژگی ( attribute ) آن را بالای اکشن بنویسید :

[OutputCache(Duration = "60", Location = OutputCacheLocation.Server)]
        public ActionResult Index()
        {
            //کوئری یا کوئری‌های لازم برای استفاده در صفحه اصلی و تبدیل آن به یک ویو مدل جامع
        }
[OutputCache(CacheProfile = "FirstPageIndex",Location=OutputCacheLocation.Server)]
        public ActionResult Index()
        {
            //کوئری یا کوئری‌های لازم برای استفاده در صفحه اصلی و تبدیل آن به یک ویو مدل جامع
        }

دو روش فوق برای کش کردن خروجی Index  از لحاظ عملکرد یکسان است، به شرطی که در حالت دوم در وب کانفیگ و در بخش system.web آن ، یک پروفایل ایجاد کنیم کنیم :

    <caching>
      <outputCacheSettings>
        <outputCacheProfiles>
          <add name="FirstPageIndex" duration="60"/>
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>

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

برای تست عملیات کشینگ، کافیست یک BreakPoint  درون Index قرار دهید و برنامه را اجرا کنید. پس از اجرا، برنامه روی Break Point می‌ایستد و اگر F5 را بزنیم، سایت بالا می‌آید. بار دیگر صفحه را رفرش کنیم، اگر این "بار دیگر" در کمتر از 60 ثانیه پس از رفرش قبلی اتفاق افتاده باشد برنامه روی Break Point متوقف نخواهد شد، چون خروجی اکشن، در کش بر روی سرور ذخیره شده است و این یعنی ما فشار کمتری به سرور تحمیل کرده ایم، صفحه با سرعت بالاتری در دسترس خواهد بود.

ما از تکرار اجرای کد جلوگیری کرده ایم و عدم اجرای کد بهترین نوع بهینه سازی برای یک سایت است. [اسکات الن، پلورال سایت]


چطور زمان مناسب برای کش کردن یک اکشن را انتخاب کنیم؟

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

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

اگر اطلاعات موجود در دیتابیس را تغییر دهیم چه کار کنیم که کشینگ رفرش شود؟ 

فرض کنید در همان مثال ابتدای این مقاله، شما یک پست به دیتابیس اضافه کرده اید، اما چون مثلا duration مربوط به کشینگ را روی 86400 تعریف کرده اید تا 24 ساعت از زمان ریکوئست اولیه نگذرد، سایت آپدیت نخواهد شد و محتوا همان چیزهای قبلی باقی خواهند ماند. اما چاره چیست؟

کافیست در بخش ادمین، وقتی که یک پست ایجاد میکنید یا پستی را ویرایش میکند در اکشن‌های مرتبط با Create یا Edit یا Delete چنین کدی را پس از فرمان ذخیره تغییرات در دیتابیس، بنویسید: 

Response.RemoveOutputCacheItem(Url.Action("index", "home"));

واضح است که ما داریم کشینگ مرتبط با یک اکشن متد مشخص را پاک میکنیم. با اینکار در اولین ریکوئست پس از تغییرات اعمال شده در دیتابیس، ASP.NET MVC چون میبیند کَشی برای این اکشن وجود ندارد، متد را اجرا میکند و کوئری‌های درونش را خواهد دید و اولین ریکوئست پیش از کَش شدن را انجام خواهد داد. با اینکار کشینگ ریست شده است و پس از این ریکوئست و استخراج اطلاعات جدید، زمان کشینگ صفر شده و آغاز میشود.

میتوانید یک دکمه در بخش ادمین سایت طراحی کنید که هر موقع دلتان خواست کلیه کش‌ها را به روش فوق پاک کنید! تا اپلیکیشن منتظر ریکوئست‌های جدید بماند و کش‌ها دوباره ایجاد شوند. 


جمع بندی

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

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

<caching>
      <outputCacheSettings>
        <outputCacheProfiles>
          <add name="Long" duration="86400"/>
          <add name="Average" duration="43600"/>
          <add name="Short" duration="600"/>
        </outputCacheProfiles>
      </outputCacheSettings>
    </caching>

برای مطالعه جزئیات بیشتر در مورد OutputCaching مقالات زیر منابع مناسبی هستند.

[اینجا ] و [اینجا ]

مطالب
ارسال PingBack در ASP.NET
Pingback یکی از روش‌های اطلاع رسانی به سایت‌های دیگر در مورد لینک دادن به آن‌ها در سایت خود است. برای مثال من لینکی از یکی از مطالب شما را در متن جاری خودم قرار می‌دهم. سپس به وسیله‌ی ارسال یک ping، در مورد انجام اینکار به شما اطلاع رسانی می‌کنم. حاصل آن عموما قسمت معروف ping-backs سایت‌ها است. این مورد نیز یکی از روش‌های مؤثر SEO در گرفتن backlink است و تبلیغ محتوا.
کار کردن با پروتکل Ping-back آنچنان ساده نیست؛ از این جهت که تبادل ارتباطات آن با پروتکل XML-RPC انجام می‌شود. XML-RPC نیز توسط PHP کارها بیشتر مورد استفاده قرار می‌گیرد؛ بجای استفاده از پروتکل‌های استاندارد وب سرویس‌ها مانند Soap و امثال آن. پیاده سازی‌های ابتدایی Pingback نیز مرتبط است به Wordpress معروف که با PHP تهیه شده‌است. در ادامه نگاهی خواهیم داشت به جزئیات پیاده سازی ارسال ping-back توسط برنامه‌های ASP.NET.


یافتن آدرس وب سرویس سایت پذیرای Pingback

اولین قدم در پیاده سازی Pingback، یافتن آدرسی است که باید اطلاعات مورد نظر را به آن ارسال کرد. این آدرس عموما به دو طریق ارائه می‌شود:
الف) در هدری به نام x-pingback و یا pingback
ب) در قسمتی از کدهای HTML صفحه به شکل
 <link rel="pingback" href="pingback server">
برای مثال اگر به وبلاگ‌های MSDN دقت کنید، هدر x-pingback را می‌توانید در خروجی وب سرور آن‌ها مشاهده کنید:


همانطور که ملاحظه می‌کنید، نیاز است Response header را آنالیز کنیم.
        private Uri findPingbackServiceUri()
        {
            var request = (HttpWebRequest)WebRequest.Create(_targetUri);
            request.UserAgent = UserAgent;
            request.Timeout = Timeout;
            request.ReadWriteTimeout = Timeout;
            request.Method = WebRequestMethods.Http.Get;
            request.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
            using (var response = request.GetResponse() as HttpWebResponse)
            {
                if (response == null) return null;

                var url = extractPingbackServiceUriFormHeaders(response);
                if (url != null)
                    return url;

                if (!isResponseHtml(response))
                    return null;

                using (var reader = new StreamReader(response.GetResponseStream()))
                {
                    return extractPingbackServiceUriFormPage(reader.ReadToEnd());
                }
            }
        }

        private static Uri extractPingbackServiceUriFormHeaders(WebResponse response)
        {
            var pingUrl = response.Headers.AllKeys.FirstOrDefault(header =>
                                header.Equals("x-pingback", StringComparison.OrdinalIgnoreCase) ||
                                header.Equals("pingback", StringComparison.OrdinalIgnoreCase));

            return getValidAbsoluteUri(pingUrl);
        }

        private static Uri extractPingbackServiceUriFormPage(string content)
        {
            if (string.IsNullOrWhiteSpace(content)) return null;
            var regex = new Regex(@"(?s)<link\srel=""pingback""\shref=""(.+?)""", RegexOptions.IgnoreCase);
            var match = regex.Match(content);
            return (!match.Success || match.Groups.Count < 2) ? null : getValidAbsoluteUri(match.Groups[1].Value);
        }

        private static Uri getValidAbsoluteUri(string url)
        {
            Uri absoluteUri;
            return string.IsNullOrWhiteSpace(url) || !Uri.TryCreate(url, UriKind.Absolute, out absoluteUri) ? null : absoluteUri;
        }

        private static bool isResponseHtml(WebResponse response)
        {
            var contentTypeKey = response.Headers.AllKeys.FirstOrDefault(header =>
                                        header.Equals("content-type", StringComparison.OrdinalIgnoreCase));
            return !string.IsNullOrWhiteSpace(contentTypeKey) &&
                    response.Headers[contentTypeKey].StartsWith("text/html", StringComparison.OrdinalIgnoreCase);
        }
نحوه‌ی استخراج آدرس سرویس Pingback یک سایت را در کدهای فوق ملاحظه می‌کنید.
targetUri، آدرسی است از یک سایت دیگر که در سایت ما درج شده‌است. زمانیکه این صفحه را درخواست می‌کنیم، response.Headers.AllKeys حاصل می‌تواند حاوی کلید x-pingback باشد یا خیر. اگر بلی، همینجا کار پایان می‌یابد. فقط باید مطمئن شد که این آدرس مطلق است و نه نسبی. به همین جهت در متد getValidAbsoluteUri، بررسی بر روی UriKind.Absolute انجام شده‌است.
اگر هدر فاقد کلید x-pingback باشد، قسمت ب را باید بررسی کرد. یعنی نیاز است محتوای Html صفحه را برای یافتن link rel=pingback بررسی کنیم. همچنین باید دقت داشت که پیش از اینکار نیاز است حتما بررسی isResponseHtml صورت گیرد. برای مثال در سایت شما لینکی به یک فایل 2 گیگابایتی SQL Server درج شده‌است. در این حالت نباید ابتدا 2 گیگابایت فایل دریافت شود و سپس بررسی کنیم که آیا محتوای آن حاوی link rel=pingback است یا خیر. اگر محتوای ارسالی از نوع text/html بود، آنگاه کار دریافت محتوای لینک انجام خواهد شد.


ارسال Ping به آدرس سرویس Pingback

اکنون که آدرس سرویس pingback یک سایت را یافته‌ایم، کافی است ping ایی را به آن ارسال کنیم:
        public void Send()
        {
            var pingUrl = findPingbackServiceUri();
            if (pingUrl == null)
                throw new NotSupportedException(string.Format("{0} doesn't support pingback.", _targetUri.Host));

            sendPing(pingUrl);
        }

        private void sendPing(Uri pingUrl)
        {
            var request = (HttpWebRequest)WebRequest.Create(pingUrl);
            request.UserAgent = UserAgent;
            request.Timeout = Timeout;
            request.ReadWriteTimeout = Timeout;
            request.Method = WebRequestMethods.Http.Post;
            request.ContentType = "text/xml";
            request.ProtocolVersion = HttpVersion.Version11;
            makeXmlRpcRequest(request);
            using (var response = (HttpWebResponse)request.GetResponse())
            {
                response.Close();
            }
        }

        private void makeXmlRpcRequest(WebRequest request)
        {
            var stream = request.GetRequestStream();
            using (var writer = new XmlTextWriter(stream, Encoding.ASCII))
            {
                writer.WriteStartDocument(true);
                writer.WriteStartElement("methodCall");
                writer.WriteElementString("methodName", "pingback.ping");
                writer.WriteStartElement("params");

                writer.WriteStartElement("param");
                writer.WriteStartElement("value");
                writer.WriteElementString("string", Uri.EscapeUriString(_sourceUri.ToString()));
                writer.WriteEndElement();
                writer.WriteEndElement();

                writer.WriteStartElement("param");
                writer.WriteStartElement("value");
                writer.WriteElementString("string", Uri.EscapeUriString(_targetUri.ToString()));
                writer.WriteEndElement();
                writer.WriteEndElement();

                writer.WriteEndElement();
                writer.WriteEndElement();
            }
        }
اینبار HttpWebRequest تشکیل شده از نوع post است و نه get. همچنین مقداری را که باید ارسال کنیم نیاز است مطابق پروتکل XML-RPC باشد. برای کار با XML-RPC در دات نت یا می‌توان از کتابخانه‌ی Cook Computing's XML-RPC.Net استفاده کرد و یا مطابق کدهای فوق، دستورات آن‌را توسط یک XmlTextWriter کنار هم قرار داد و نهایتا در درخواست Post ارسالی درج کرد.
در اینجا sourceUri آدرس صفحه‌ای در سایت ما است که targetUri ایی (آدرسی از سایت دیگر) در آن درج شده‌است. در یک pinback، صرفا این دو آدرس به سرویس دریافت کننده‌ی pingback ارسال می‌شوند.
سپس سایت دریافت کننده‌ی ping، ابتدا sourceUri را دریافت می‌کند تا عنوان آن‌را استخراج کند و همچنین بررسی می‌کند که آیا targetUri، در آن درج شده‌است یا خیر (آیا spam است یا خیر)؟
تا اینجا اگر این مراحل را کنار هم قرار دهیم به کلاس Pingback ذیل خواهیم رسید:
Pingback.cs


نحوه‌ی استفاده از کلاس Pingback تهیه شده

کار ارسال Pingback عموما به این نحو است: هر زمانیکه مطلبی یا یکی از نظرات آن، ثبت یا ویرایش می‌شوند، نیاز است Pingbackهای آن ارسال شوند. بنابراین تنها کاری که باید انجام شود، استخراج لینک‌های خارجی یک صفحه و سپس فراخوانی متد Send کلاس فوق است.
یافتن لینک‌های یک محتوا را نیز می‌توان مانند متد extractPingbackServiceUriFormPage فوق، توسط یک Regex انجام داد و یا حتی با استفاده از کتابخانه‌ی معروف HTML Agility Pack:
var doc = new HtmlWeb().Load(url);
var linkTags = doc.DocumentNode.Descendants("link");
var linkedPages = doc.DocumentNode.Descendants("a")
                                  .Select(a => a.GetAttributeValue("href", null))
                                  .Where(u => !String.IsNullOrEmpty(u));
مطالب
ساختن code snippet ( قطعه کد ) در ویژوال استودیو 2010
ویژوال استودیو به توسعه دهندگان این امکان را می‌دهد تا کدهایی را که تکراری بوده و به  دفعات در متن برنامه مورد استفاده هستند به شکل یک قطعه کد آماده (در صورت نیاز با مقادیر پیش فرض ) ذخیره کنند ، سپس در مواقع نیاز بدون اینکه مجبور باشند آن را دوباره و دوباره بنویسند ، تنها با تایپ کردن نام قطعه کد ذخیره شده  و دو بار فشردن  کلید Tab ، کد  تعریف شده توسط ویژوال استودیو  در محل تعیین شده اضافه می‌گردد. به این  قطعه کدهای آماده code snippet گفته می‌شود .
خود ویژوال استودیو تعدادی code snippet آماده  دارد که آشنایی با آنها می‌تواند سرعت کدنویسی را افزایش دهد . برای دیدن لیست کامل و مدیریت آنها به مسیر

Tools -> Code Snippets Manager (Ctrl+K,Ctrl+B)
بروید .

در ویژوال استودیو 2010 دو نوع snippet وجود دارد :

 1- Expansion snippets : که در محل کرسر (Cursor) اضافه می‌شوند . مثل cw و enum که به ترتیب دستور writeLine و ساختار یک enum را ایجاد می‌کنند .

 2- SurroundsWith snippets : که  می‌توانند یک تکه کد انتخاب شده را در بر بگیرند مثل for و یا do که کد انتخاب شده را در یک حلقه for و do-while محصور می‌کنند  .

نکته ای که باید توجه داشت این است که یک snippet می‌تواند از هر دو نوع باشد . برای مثال  for و do و یا if ، در صورتی که  کدی انتخاب شده باشد آن را محصور می‌کنند و گرنه  ساختار خالی مرتبط را در محل cursor اضافه می‌کنند .

همانطور که در ابتدا هم ذکر شد ، علاوه بر snippet‌های آماده‌ی موجود ، توسعه دهنده می‌تواند قطعه کدهایی را خود ایجاد کرده و مورد استفاده قرار دهد .

در اینجا یک expansion snippet  خواهیم ساخت تا  کار اضافه کردن بلاک try-catch-finally  را برای ما انجام دهد .

- ابتدا یک فایل xml به پروژه اضافه می‌کنیم و آنرا TryCatchFinally.snippet می‌نامیم . توجه کنید که نام فایل باید به .snippet ختم شود .

- فایل را باز  و درون آن راست کلیک کرده و گزینه Insert snippet > Snippet را انتخاب می‌کنیم . با اینکار یک قالب پایه snippet ( که یک ساختار xml ) است به فایل اضافه می‌شود .  هر فایل snippet از دو بخش اصلی header و  snippet تشکیل شده که بخش header اطلاعاتی کلی درباره قطعه کد را نگهداری می‌کند و بخش snippet مربوط به تعریف محتوای قطعه کد است .

<codesnippet format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <header>
    <title>title</title>
    <author>author</author>
    <shortcut>shortcut</shortcut>
    <description>description</description>
    <snippettypes>
      <snippettype>SurroundsWith</snippettype>
      <snippettype>Expansion</snippettype>
    </snippettypes>
  </header>
  <snippet>
    <declarations>
      <literal>
        <id>name</id>
        <default>value</default>
      </literal>
    </declarations>
    <code language="XML">
      <!--[CDATA[<test-->
      <name>$name$</name>
      $selected$ $end$]]>
    </code>
  </snippet>
</codesnippet>
 

- قالب پیش فرض شامل هر دو نوع snippet است .


<codesnippet format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <header>
...
    <snippettypes>
      <snippettype>SurroundsWith</snippettype>
      <snippettype>Expansion</snippettype>
    </snippettypes>
  </header>
...
</codesnippet>

از آنجا که قصد داریم یک Expansion snippet بسازیم پس تگ SurroundsWith را حذف می‌کنیم . 


    <snippettypes>
      <snippettype>Expansion</snippettype>
    </snippettypes>

- در بخش header مقدار تگ Title را به “Try Catch Finally”و  مقدار تگ Shortcut را به “trycf” و Description را به “Adds a try-catch-finally block ” تغییر می‌دهیم . Title عنوان snippet است و وجود آن ضروری است . اضافه کردن shortcut  اختیاری است  و به عنوان یک متن میانبر برای اضافه کردن snippet استفاده می‌شود .


    <Header>
      <Title>Try Catch Finally</Title>
      <Author>mohsen.d</Author>
      <Shortcut>trycf</Shortcut>
      <Description>Adds a try-catch-finally block</Description>

- تگ Literal برای تعریف جایگزین برای  بخشی از کد  درون snippet  که احتمال دارد پس از اضافه شدن ، توسط  برنامه نویس و یا در صورت استفاده از  function توسط خود ویژوال استودیو تغییر کند استفاده می‌شود . در قطعه کد try-catch-finally ، ما می‌خواهیم به کاربر اجازه بدهیم که نوع استثنائی را که catch می‌شود تغییر دهد .
تگ id نامی برای بخش قابل ویرایش تعریف می‌کند ( که از آن در  ادامه در تعریف خود قطعه کد استفاده می‌کنیم ) . آنرا به “ExceptionName” تغییر می‌دهیم . تگ default هم مقدار پیش فرضی را  برای آن بخش مشخص می‌کند . ما می‌خواهیم تمام استثناها را Catch کنیم پس مقدار پیش فرض را برابر "Exception" قرار می‌دهیم .


.....
    <declarations>
      <literal>
        <id>ExceptionName</id>
        <default>Exception</default>
      </literal>
    </declarations>
...

- و در تگ Code ، خود قطعه کدی که ویژوال استودیو باید آن را اضافه کند ، تعریف می‌شود . مقدار مشخصه Language آن را به CSharp تغییر می‌دهیم و محتویات داخل آنرا به شکل زیر اضافه می‌کنیم . 


<code language="CSharp">
<!--[CDATA[
try
{
    $end$
}
catch($ExceptionName$)
{
  
}
finally
{
  
}
      ]]-->
</code>

به نحوه استفاده از ExceptionName که در قسمت Literal تعریف کردیم توجه کنید  . عبارت $end$ هم یک کلمه رزرو شده است که محل قرار گرفتن cursor را بعد از اضافه شدن قطعه کد مشخص می‌کند .

- در نهایت snippet ما به شکل زیر خواهد بود : 


<codesnippet format="1.0.0" xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
  <header>
    <title>Try Catch Finally</title>
    <author>mohsen.d</author>
    <shortcut>trycf</shortcut>
    <description>Adds a try-catch-finally block</description>
    <snippettypes>
      <snippettype>Expansion</snippettype>
    </snippettypes>
  </header>
  <snippet>
    <declarations>
      <literal>
        <id>ExceptionName</id>
        <default>Exception</default>
      </literal>
    </declarations>
    <code language="CSharp">
      <!--[CDATA[
try
{
    $end$
}
catch($ExceptionName$)
{
  
}
finally
{
  
}
      ]]-->
    </code>
  </snippet>
</codesnippet>

اضافه کردن snippet ساخته شده به visual studio


دو راه برای اضافه کردن snippet تعریف شده به ویژوال استودیو وجود دارد :
روش اول قرار دادن فایل .snippet در پوشه code snippets ویژوال استودیو است که مسیر پیش فرض آن 

C:\Users\<UserName>\Documents\Visual Studio 2010\Code Snippets\
است . این پوشه به ازای هر زبان دارای یک زیر پوشه است . این snippet را باید در پوشه C# قرار دهیم . همین که فایل را در پوشه مناسب قرار دهیم ویژوال استودیو بدون نیاز به restart شدن آن را خواهد شناخت .

گزینه دوم import کردن فایل .snippet به داخل ویژوال استودیو است . در ویژوال استودیو به مسیر

Tools -> Code Snippets Manager (Ctrl+K,Ctrl+B)
می‌رویم . در پنجره Code Snippets Manager ، بر روی کلید import کلیک و فایل موردنظر را یافته و انتخاب کرده و پوشه‌ی محل ذخیره شدن آن را تعیین می‌کنیم .

استفاده از snippet ساخته شده

برای استفاده از snippet می‌توانیم متن میانبر تعریف شده را تایپ کنیم و با دو بار فشردن کلید tab قطعه کد تعریف شده به محل کرسر اضافه می‌شود 



همینطور با فشردن کلیدهای Ctrl+K و Ctrl+X  به صورت پشت سر هم منوی “Insert Snippet” ظاهر می‌شود که از طریق آن می‌توانیم Snippet موردنظر را یافته  ( بدنبال Title تعریف شده برای snippet  در پوشه ای که آنرا ذخیره کرده اید بگردید ) و با انتخاب آن کد تعریف شده اضافه خواهد شد .



 برای آشنایی با روش‌های مختلف دسترسی به snippet‌ها اینجا را بررسی کنید .

ابزارها

دستکاری خود فایل xml چندان جالب و خالی از خطا نیست . روش‌های بهتری برای ساخت و ویرایش snippet‌ها وجود دارد . Snippet Editor ابزاری برای ویرایش و ساخت snippet هاست و Snippet Designer هم یک پلاگین برای ویژوال استودیوست که کار مشابهی را انجام می‌دهد . یکی از کارهای جالبی که با این ابزار می‌توانید انجام دهید انتخاب یک قطعه از کد ( مثل یک تابع ) و سپس ساختن یک snippet از روی آن است .

در این پروژه هم مجموعه snippet‌های موجود ویژوال استودیو 2010 برای زبان سی شارپ ، جهت سازگاری با stylecop ویرایش و refactor شده اند ( در کنار تعریف snippet‌های دیگر ).