مطالب
خلاصه‌ای از LINQ to XML

در این مقاله مروری سریع و کاربردی خواهیم داشت بر توانایی‌های مقدماتی LINQ to XML .

فایل Employee.XML را با محتویات زیر در نظر بگیرید:

<Employees>
<Employee>
<Name>Vahid</Name>
<Phone>11111111</Phone>
<Department>IT</Department>
<Age>52</Age>
</Employee>
<Employee>
<Name>Farid</Name>
<Phone>124578963</Phone>
<Department>Civil</Department>
<Age>35</Age>
</Employee>
<Employee>
<Name>Mehdi</Name>
<Phone>1245788754</Phone>
<Department>HR</Department>
<Age>30</Age>
</Employee>
</Employees>

1- چگونه یک فایل XML را جهت استفاده توسط LINQ بارگذاری کنیم؟

قبل از شروع، اسمبلی System.Xml.Linq باید به ارجاعات برنامه اضافه شود. سپس:

using System.Xml.Linq;

XDocument xDoc = XDocument.Load("Employee.xml");

2- اگر محتویات XML دریافتی به صورت رشته بود (مثلا از یک دیتابیس دریافت شد)، اکنون چگونه باید آن‌را بارگذاری کرد؟

این‌کار را با استفاده از یک StringReader به صورت زیر می‌توان انجام داد:

// loading XML from string
StringReader sr = new StringReader(stringXML);
XDocument xDoc = XDocument.Load(sr);

3- چگونه یک کوئری ساده شامل تمامی رکوردهای Employee مجموعه Employees را تهیه کنیم؟

using System.Collections;

IEnumerable<XElement> empList = from e in xDoc.Root.Elements("Employee") select e;
توسط کوئری فوق، تمامی رکوردهای کارکنان در یک Collection در اختیار ما خواهند بود. نکته‌ی مهم عبارت LINQ فوق، xDoc.Root.Elements("Employee") می‌باشد. به این صورت از xDoc بارگذاری شده، ابتدا Root و یا همان محتوای فایل XML را جهت بررسی انتخاب کرده و سپس گره‌های مرتبط با کارکنان را انتخاب می‌کنیم.
اکنون که مجموعه کارکنان توسط متغیر empList در اختیار ما است، دسترسی به محتویات آن به سادگی زیر خواهد بود:

foreach (XElement employee in empList)
{
foreach (XElement e in employee.Elements())
{
Console.WriteLine(e.Name + " = " + e.Value);
}
}
در این‌جا حلقه خارجی اطلاعات کلی تمامی کارکنان را باز می‌گرداند و حلقه داخلی اطلاعات یک گره دریافت شده را نمایش می‌دهد.

4- کوئری بنویسید که اطلاعات تمامی کارکنان بخش HR را باز گرداند.

IEnumerable<XElement> hrList = from e in xDoc.Root.Elements("Employee")
where e.Element("Department").Value == "HR"
select e;

همانطور که ملاحظه می‌کنید همانند عبارات SQL ، در تمامی عناصر متعلق به کارکنان، عناصری که دپارتمان آن‌ها مساوی HR است بازگشت داده می‌شود.

5- کوئری بنویسید که لیست تمامی کارکنان بالای 30 سال را ارائه دهد.

IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
select e;

چون حاصل e.Element("Age").Value یک رشته است، برای اعمال فیلترهای عددی باید این رشته‌ها تبدیل به عدد شوند. به همین جهت از int.Parse استفاده شده است.

6- کوئری بنویسید که لیست تمامی کارکنان بالای 30 سال را مرتب شده بر اساس نام باز گرداند.

IEnumerable<XElement> tList = from e in xDoc.Root.Elements("Employee")
where int.Parse(e.Element("Age").Value) > 30
orderby e.Element("Name").Value
select e;
در اینجا همانند عبارات SQL از orderby جهت مرتب سازی بر اساس عناصر نام استفاده شده است.

7- تبدیل نتیجه‌ی یک کوئری LINQ به لیستی از اشیاء

مفهومی به سی شارپ 3 اضافه شده است به نام anonymous types . برای مثال:



توسط این قابلیت می‌توان یک شیء را بدون نیاز به تعریف ابتدایی آن ایجاد کرد و حتی از intelliSense موجود در IDE نیز بهره مند شد. این نوع‌های ناشناس توسط واژه‌های کلیدی new و var تولید می‌شوند. کامپایلر به صورت خودکار برای هر anonymous type یک کلاس ایجاد می‌کند.
دقیقا از همین توانایی در LINQ نیز می‌توان استفاده نمود:

var empList = from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
در این‌جا حاصل کوئری، تبدیل به لیستی از اشیاءanonymous می‌شود. اکنون برای نمایش آن‌ها نیز می‌توان از واژه کلیدی var استفاده نمود که از هر لحاظ نسبت به روش اعمال foreach بر روی Xelement ها که در مثال 3 مشاهده کردیم خواناتر است:

foreach (var employee in empList)
{
Console.WriteLine("Name = " + employee.Name);
Console.WriteLine("Dep = " + employee.Department);
Console.WriteLine("Phone = " + employee.Phone);
Console.WriteLine("Age = " + employee.Age);
}
و البته بدیهی است که می‌توان از anonymous types استفاده نکرد و دقیقا تعریف شیء را پیش از انتخاب آن نیز مشخص نمود. برای مثال:

public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}
در این حالت، قسمت select new عبارت LINQ ما به select new Employee تغییر خواهد کرد.
برای مثال اگر بخواهیم لیست دریافتی را به صورت یک لیست جنریک بازگشت دهیم خواهیم داشت:

public class Employee
{
public string Name { get; set; }
public string Phone { get; set; }
public string Department { get; set; }
public int Age { get; set; }
}

List<Employee> Get()
{
XDocument xDoc = XDocument.Load("Employee.xml");
var items =
from e in xDoc.Root.Elements("Employee")
orderby e.Element("Name").Value
select new Employee
{
Name = e.Element("Name").Value,
Phone = e.Element("Phone").Value,
Department = e.Element("Department").Value,
Age = int.Parse(e.Element("Age").Value)
};
return items.ToList();
}

مطالب
Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت
در حین کار با برنامه‌های وب، چشم‌پوشی از جاوا اسکریپت عملا ممکن نیست؛ هرچند با Blazor، امکان انجام کارهایی را یافته‌ایم که پیشتر با MVC و یا Razor pages میسر نبودند، اما هیچگاه به تنهایی نمی‌تواند جایگزین کامل جاوا اسکریپت، در تولید برنامه‌های وب باشد. بنابراین ضروری است که نحوه‌ی یکپارچگی جاوا اسکریپت را با برنامه‌های مبتنی بر Blazor، بررسی کنیم.


ایجاد کامپوننت جدید BlazorJS

برای بررسی نحوه‌ی تعامل جاوا اسکریپت و Blazor، در ابتدا کامپوننت جدید Pages\LearnBlazor\BlazorJS.razor را ایجاد کرده:
@page "/BlazorJS"

<h3>BlazorJS</h3>

@code
{
}
و همچنین مدخل منوی آن‌را نیز بر اساس مسیریابی ابتدای فایل این کامپوننت، به فایل Shared\NavMenu.razor اضافه می‌کنیم:
<li class="nav-item px-3">
    <NavLink class="nav-link" href="BlazorJS">
        <span class="oi oi-list-rich" aria-hidden="true"></span> BlazorJS
    </NavLink>
</li>


روش فراخوانی کدهای جاوا اسکریپتی از طریق کدهای سی‌شارپ Blazor

فرض کنید می‌خواهیم در حین کلیک بر روی دکمه‌ای مانند دکمه‌ی حذف، ابتدا تائیدیه‌ای را توسط تابع confirm جاوا اسکریپتی، از کاربر اخذ کنیم. روش انجام چنین کاری در برنامه‌های مبتنی بر Blazor به صورت زیر است:
@page "/BlazorJS"

@inject IJSRuntime JsRuntime

<h3>BlazorJS</h3>

<div class="row">
    <button class="btn btn-secondary" @onclick="TestConfirmBox">Test Confirm Button</button>
</div>
<div class="row">
    @if (ConfirmResult)
    {
        <p>Confirmation has been made!</p>
    }
    else
    {
        <p>Confirmation Pending!</p>
    }
</div>

@code {
    string ConfirmMessage = "Are you sure you want to click?";
    bool ConfirmResult;

    async Task TestConfirmBox()
    {
        ConfirmResult = await JsRuntime.InvokeAsync<bool>("confirm", ConfirmMessage);
    }
}
توضیحات:
- در اینجا می‌خواهیم تابع استاندارد confirm جاوا اسکریپتی را از طریق کدهای سی‌شارپ، با کلیک بر روی دکمه‌ی Test Confirm Button، فراخوانی کنیم. به همین جهت onclick@ این دکمه، به متد TestConfirmBox کدهای UI سی‌شارپ این کامپوننت، متصل شده‌است.
- برای دسترسی به توابع جاوا اسکریپتی، نیاز است سرویس توکار IJSRuntime را به کدهای کامپوننت تزریق کنیم که روش انجام آن‌را توسط دایرکتیو inject@ مشاهده می‌کنید. برای دسترسی به این سرویس توکار، نیاز به تنظیمات ابتدایی خاصی نیست و اینکار پیشتر انجام شده‌است.
- سرویس JsRuntime تزریق شده، دو متد مهم InvokeVoidAsync و InvokeAsync را جهت فراخوانی توابع جاوا اسکریپتی به همراه دارد. اگر تابعی، خروجی غیر void داشته باشد، باید از متد InvokeAsync استفاده کرد. برای مثال خروجی تابع استاندارد confirm، از نوع boolean است. بنابراین نوع این خروجی را به صورت یک آرگومان جنریک متد InvokeAsync مشخص کرده‌ایم.
- اولین پارامتر متد InvokeAsync، نام رشته‌ای تابع جاوا اسکریپتی است که قرار است صدا زده شود. پارامترهای اختیاری بعدی که به صورت params object?[]? args تعریف شده‌اند، لیست نامحدود آرگومان‌های ورودی این متد هستند.
- فیلد ConfirmMessage، پیامی را جهت اخذ تائید، تعریف می‌کند که به عنوان پارامتر متد confirm، توسط JsRuntime.InvokeAsync فراخوانی خواهد شد.
- فیلد ConfirmResult، نتیجه‌ی فراخوانی متد confirm جاوا اسکریپتی را به همراه دارد.
- در اینجا روش عکس العمل نشان دادن به خروجی دریافتی از متد جاوااسکریپتی را نیز مشاهده می‌کنید. پس از پایان متد TestConfirmBox که یک متد رویدادگران است، همانطور که در مطلب بررسی «چرخه‌ی حیات کامپوننت‌ها» نیز بررسی کردیم، متد StateHasChanged، در پشت صحنه فراخوانی می‌شود که سبب رندر مجدد UI خواهد شد. بنابراین در حین رندر مجدد UI، بر اساس مقدار جدید ConfirmResult دریافت شده‌ی از کاربر، با تشکیل یک if/else@، می‌توان به نتیجه‌ی تائید یا عدم تائید کاربر، واکنش نشان داد. با این توضیحات در اولین بار نمایش کامپوننت جاری چون مقدار ConfirmResult مساوی false است، پیام زیر را مشاهده می‌کنیم:


اما در ادامه با کلیک بر روی دکمه و تائید پیام ظاهر شده، عبارت زیر ظاهر می‌شود:



روش افزودن یک کتابخانه‌ی خارجی جاوا اسکریپتی به پروژه‌های Blazor

فرض کنید می‌خواهیم پیام‌های برنامه را توسط کتابخانه‌ی معروف جاوا اسکریپتی Toastr نمایش دهیم؛ با این دمو.
مرحله‌ی اول کار با این کتابخانه، دریافت فایل‌های CSS و JS آن است. برای این منظور قصد داریم از برنامه‌ی مدیریت بسته‌های LibMan استفاده کنیم:
dotnet tool install -g Microsoft.Web.LibraryManager.Cli
libman init
libman install bootstrap --provider unpkg --destination wwwroot/lib/bootstrap
libman install jquery --provider unpkg --destination wwwroot/lib/jquery
libman install toastr --provider unpkg --destination wwwroot/lib/toastr
بنابراین خط فرمان را در ریشه‌ی پروژه گشوده و پنج دستور فوق را اجرا می‌کنیم. دستور اول، ابزار خط فرمان LibMan را نصب می‌کند. دستور دوم، یک فایل libman.json خالی را در این پوشه ایجاد می‌کند و سه دستور بعدی، جی‌کوئری، بوت استرپ و toastr را دریافت و در پوشه‌ی wwwroot/lib قرار می‌دهند. Toastr برای اجرا، نیاز به jQuery نیز دارد.
البته تعاریف مداخل آن‌ها به فایل libman.json نیز اضافه می‌شوند. مزیت آن، اجرای دستور libman restore برای بازیابی و نصب مجدد تمام بسته‌های ذکر شده‌ی در فایل libman.json است.

پس از دریافت بسته‌های سمت کلاینت آن، مداخل مرتبط را به فایل Pages\_Host.cshtml برنامه‌ی Blazor Server اضافه خواهیم کرد (و یا در فایل wwwroot/index.html برنامه‌های Blazor WASM).
<head>
    <base href="~/" />
    <link rel="stylesheet" href="lib/toastr/build/toastr.min.css" />

</head>
<body>
 
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/toastr/build/toastr.min.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>
مدخل فایل css آن‌را در قسمت head و فایل js آن‌را پیش از بسته شدن تگ body تعریف می‌کنیم. در اینجا نیازی به ذکر پوشه‌ی آغازین wwwroot نیست؛ چون base href تعریف شده، به این پوشه اشاره می‌کند.

یک نکته: می‌توان فایل csproj برنامه را به صورت زیر تغییر داد تا کار اجرای دستور libman restore را قبل از build، به صورت خودکار انجام دهد:
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>

  <Target Name="DebugEnsureLibManEnv" BeforeTargets="BeforeBuild" Condition=" '$(Configuration)' == 'Debug' ">
    <!-- Ensure libman is installed -->
    <Exec Command="libman --version" ContinueOnError="true">
      <Output TaskParameter="ExitCode" PropertyName="ErrorCode" />
    </Exec>
    <Error Condition="'$(ErrorCode)' != '0'" Text="libman is required to build and run this project. To continue, please run `dotnet tool install -g Microsoft.Web.LibraryManager.Cli`, and then restart your command prompt or IDE." />
    <Message Importance="high" Text="Restoring dependencies using 'libman'. This may take several minutes..." />
    <Exec WorkingDirectory="$(MSBuildProjectDirectory)" Command="libman restore" />
  </Target>
</Project>


روش فراخوانی یک کتابخانه‌ی خارجی جاوا اسکریپتی در پروژه‌های Blazor

پس از افزودن فایل‌های سمت کلاینت toastr و تعریف مداخل آن در فایل Pages\_Host.cshtml برنامه‌ی Blazor Server جاری، اکنون می‌خواهیم از این کتابخانه استفاده کنیم. یک روش کار با این نوع کتابخانه‌های عمومی و سراسری به صورت زیر است:
- ابتدا فایل خالی جدید wwwroot\js\common.js را ایجاد می‌کنیم.
- سپس تابع عمومی و سراسری ShowToastr را بر اساس امکانات کتابخانه‌ی toastr و مستندات آن، به صورت زیر ایجاد می‌کنیم:
window.ShowToastr = (type, message) => {
  // Toastr don't work with Bootstrap 4.2
  toastr.options.toastClass = "toastr"; // https://github.com/CodeSeven/toastr/issues/599

  if (type === "success") {
    toastr.success(message, "Operation Successful", { timeOut: 20000 });
  }
  if (type === "error") {
    toastr.error(message, "Operation Failed", { timeOut: 20000 });
  }
};
چون تابع ShowToastr به شیء window انتساب داده شده‌است، در سراسر برنامه‌ی جاری قابل دسترسی است.
سطر اول آن هم برای رفع عدم تداخل با بوت استرپ 4x اضافه شده‌است. بوت استرپ 4x به همراه کلاس‌های CSS مشابهی است که نیاز است با تنظیم toastClass به مقداری دیگر، این تداخل را برطرف کرد.

- در ادامه مدخل تعریف فایل wwwroot\js\common.js را به انتهای تگ body فایل Pages\_Host.cshtml اضافه می‌کنیم:
    <script src="lib/jquery/dist/jquery.min.js"></script>
    <script src="lib/toastr/build/toastr.min.js"></script>
    <script src="js/common.js"></script>
    <script src="_framework/blazor.server.js"></script>
</body>

در آخر برای آزمایش آن به کامپوننت Pages\LearnBlazor\BlazorJS.razor مراجعه کرده و تابع سراسری ShowToastr را دقیقا مانند روشی که در مورد تابع confirm بکار بردیم، توسط سرویس JsRuntime، فراخوانی می‌کنیم:
@page "/BlazorJS"

@inject IJSRuntime JsRuntime


<div class="row">
    <button class="btn btn-success" @onclick="@(()=>TestSuccess("Success Message"))">Test Toastr Success</button>
    <button class="btn btn-danger" @onclick="@(()=>TestFailure("Error Message"))">Test Toastr Failure</button>
</div>

@code {
    async Task TestSuccess(string message)
    {
        await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message);
    }

    async Task TestFailure(string message)
    {
        await JsRuntime.InvokeVoidAsync("ShowToastr", "error", message);
    }
}
در اینجا دو دکمه، جهت فراخوانی متد ShowToastr با پارامترهای مختلفی تعریف شده‌اند. چون تابع ShowToastr خروجی ندارد، به همین جهت اینبار از متد InvokeVoidAsync استفاده کرده‌ایم. پارامتر اول آن، نام متد ShowToastr است. پارامتر‌های دوم و سوم آن با آرگومان‌های (type, message) تعریف شده‌ی تابع ShowToastr تطابق دارند. به علاوه در این مثال، روش ارسال پارامترها را نیز در onlick@ توسط arrow functions مشاهده می‌کنید.



کاهش کدهای تکراری فراخوانی متدهای جاوا اسکریپتی با تعریف متدهای الحاقی

می‌توان جهت کاهش تکرار کدهای استفاده از تابع ShowToastr، متدهای الحاقی زیر را برای سرویس IJSRuntime تهیه کرد:
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace BlazorServerSample.Utils
{
    public static class JSRuntimeExtensions
    {
        public static ValueTask ToastrSuccess(this IJSRuntime JSRuntime, string message)
        {
            return JSRuntime.InvokeVoidAsync("ShowToastr", "success", message);
        }

        public static ValueTask ToastrError(this IJSRuntime JSRuntime, string message)
        {
            return JSRuntime.InvokeVoidAsync("ShowToastr", "error", message);
        }
    }
}
و سپس فضای نام آن‌را به فایل Imports.razor_ معرفی نمود تا در تمام کامپوننت‌های برنامه قابل استفاده شوند.
@using BlazorServerSample.Utils
به این ترتیب به فراخوانی‌های ساده شده‌ی زیر خواهیم رسید:
async Task TestSuccess(string message)
{
   //await JsRuntime.InvokeVoidAsync("ShowToastr", "success", message);
   await JsRuntime.ToastrSuccess(message);
}


فراخوانی یک متد عمومی واقع در کامپوننت فرزند از طریق کامپوننت والد

فرض کنید در کامپوننت فرزند Pages\LearnBlazor\LearnBlazor‍Components\ChildComponent.razor که در قسمت‌های قبل آن‌را تکمیل کردیم، متد عمومی زیر تعریف شده‌است:
@inject IJSRuntime JsRuntime


@code {
    // ...

    public async Task TestSuccess(string message)
    {
        await JsRuntime.ToastrSuccess(message);
    }
}
اکنون اگر بخواهیم این متد عمومی را از طریق کامپوننت والد یا دربرگیرنده‌ی آن فراخوانی کنیم، نیاز است از مفهوم جدیدی به نام ref استفاده کرد. برای این منظور به کامپوننت Pages\LearnBlazor\ParentComponent.razor مراجعه کرده و تغییرات زیر را اعمال می‌کنیم:
@page "/ParentComponent"

<ChildComponent
    OnClickBtnMethod="ShowMessage"
    @ref="ChildComp"
    Title="This title is passed as a parameter from the Parent Component">
    <ChildContent>
        A `Render Fragment` from the parent!
    </ChildContent>
    <DangerChildContent>
        A danger content from the parent!
    </DangerChildContent>
</ChildComponent>

<div class="row">
    <button class="btn btn-success" @onclick="@(()=>ChildComp.TestSuccess("Done!"))">Show Alert</button>
</div>


@code {
    ChildComponent ChildComp;
    // ...
}
با استفاده از ref@ که به فیلد ChildComp انتساب داده شده‌است، می‌توان ارجاعی از کامپوننت فرزند را (وهله‌ای از کلاس مرتبط با آن‌را) در کامپوننت جاری بدست آورد و سپس از آن جهت فراخوانی متدهای عمومی کامپوننت فرزند استفاده کرد.


کدهای کامل این مطلب را از اینجا می‌توانید دریافت کنید: Blazor-5x-Part-11.zip
نظرات مطالب
استفاده از Flashback Table در Oracle
برای خالی کردن این فضا از سه حالت دستوری زیر می‌شه استفاده کرد:
Table
 برای آزاد کردن فضای مربوط اشیاء حذف شده برای کاربر جاری: 
PURGE TABLE Table_Name;
  برای آزاد کردن فضای مربوط  کل اشیاء حذف شده برای کاربر جاری: 
 PURGE RECYCLEBIN;
DBA_RecycleBin
برای آزاد کردن فضای مربوط به کل اشیاء حذف شده :
PURGE DBA_RECYCLEBIN;
از این حالت  فقط زمانی که بادسترسی SYSDBA  وارد سیستم شده باشیم می‌توانیم استفده نماییم.

TableSpace
  برای آزاد کردن فضای مربوط به کل اشیاء حذف شده یک TableSpace:

purge Tablespace Tablespace_name;
  
یه میان بر هم برای اینکه بعد از حذف یک شیء به داخل RECYCLEBIN نره استفاده ازاین دستور:
DROP TABLE Table_Name PURGE;
 
مطالب
آغاز به کار با Twitter Bootstrap در ASP.NET MVC
Twitter Bootstrap یک فریم ورک CSS بسیار محبوب سورس باز تولید برنامه‌های وب به کمک HTML، CSS و جاوا اسکریپت است. این فریم ورک حاوی بسیاری از المان‌های مورد نیاز جهت تولید وب سایت‌هایی زیبا، مانند دکمه‌ها، عناصر فرم‌ها، منوها، ویجت‌ها و غیره است. تمام این‌ها نیز همانطور که عنوان شد برمبنای HTML، CSS و جاوا اسکریپت تهیه شده‌اند؛ بنابراین در هر نوع فناوری سمت سروری مانند ASP.NET، PHP، روبی و امثال آن قابل استفاده است.


دریافت Twitter Bootstrap

محل اصلی دریافت Twitter Bootstrap، آدرس ذیل است:
البته ما از آن در اینجا به شکل خام فوق استفاده نخواهیم کرد؛ زیرا نیاز است قابلیت‌های استفاده در محیط‌های راست به چپ فارسی نیز به آن اضافه شود. برای این منظور می‌توانید از یکی از دو بسته نیوگت ذیل استفاده نمائید:


و یا حتی از منابع سایت http://rbootstrap.ir نیز می‌توان استفاده کرد.
برای نمونه دستور زیر را در کنسول پاورشل ویژوال استودیو وارد نمائید تا اسکریپت‌ها و فایل‌های CSS مورد نیاز به پروژه جاری اضافه شوند:
 PM> Install-Package Twitter.BootstrapRTL
پس از نصب، شاهد افزوده شدن چنین فایل‌هایی به یک پروژه ASP.NET MVC خواهیم بود:


در اینجا فایل‌های min، نگارش‌های فشرده شده فایل‌های js یا css هستند که با توجه به امکانات اضافه شده به ASP.NET MVC4، از آن‌ها استفاده نخواهیم کرد و برای افزودن و تعریف آن‌ها از امکانات Bundling and minification توکار فریم ورک ASP.NET MVC به نحوی که در ادامه توضیح داده خواهد شد، استفاده می‌کنیم.
فایل‌های png اضافه شده، آیکون‌های مخصوص Twitter Bootstrap هستند که اصطلاحا به آن‌ها sprite images نیز گفته می‌شود. در این نوع تصاویر، تعداد زیادی آیکون در کنار هم، برای بهینه سازی تعداد بار رفت و برگشت به سرور جهت دریافت تصاویر، طراحی شده و قرار گرفته‌اند.
فایل‌های js این مجموعه اختیاری بوده و برای استفاده از ویجت‌های Twitter Bootstrap مانند آکاردئون کاربرد دارند. این فایل‌ها برای اجرا، نیاز به jQuery خواهند داشت.


افزودن تعاریف اولیه Twitter Bootstrap به یک پروژه ASP.NET MVC

امکانات Bundling and minification در نوع پروژه‌های نسبتا جامع‌تر ASP.NET MVC به صورت پیش فرض لحاظ شده است. اما اگر یک پروژه خالی را شروع کرده‌اید، نیاز است بسته نیوگت آن‌را نیز نصب کنید:
 PM> Install-Package Microsoft.AspNet.Web.Optimization
پس از آن، کلاس کمکی BundleConfig ذیل را به پروژه جاری اضافه نمائید. به کمک آن قصد داریم امکانات Bundling and minification را فعال کرده و همچنین به آن بگوییم اسکریپت‌ها و فایل‌های CSS تعریف شده را به همان نحوی که معرفی می‌کنیم، پردازش کن (توسط کلاس سفارشی AsIsBundleOrderer).

using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Web.Optimization;

namespace Mvc4TwitterBootStrapTest.Helper
{
    /// <summary>
    /// A custom bundle orderer (IBundleOrderer) that will ensure bundles are 
    /// included in the order you register them.
    /// </summary>
    public class AsIsBundleOrderer : IBundleOrderer
    {
        public IEnumerable<FileInfo> OrderFiles(BundleContext context, IEnumerable<FileInfo> files)
        {
            return files;
        }
    }

    public static class BundleConfig
    {
        private static void addBundle(string virtualPath, bool isCss, params string[] files)
        {
            BundleTable.EnableOptimizations = true;

            var existing = BundleTable.Bundles.GetBundleFor(virtualPath);
            if (existing != null)
                return;

            Bundle newBundle;
            if (HttpContext.Current.IsDebuggingEnabled)
            {
                newBundle = new Bundle(virtualPath);
            }
            else
            {
                newBundle = isCss ? new Bundle(virtualPath, new CssMinify()) : new Bundle(virtualPath, new JsMinify());
            }
            newBundle.Orderer = new AsIsBundleOrderer();

            foreach (var file in files)
                newBundle.Include(file);

            BundleTable.Bundles.Add(newBundle);
        }

        public static IHtmlString AddScripts(string virtualPath, params string[] files)
        {
            addBundle(virtualPath, false, files);
            return Scripts.Render(virtualPath);
        }

        public static IHtmlString AddStyles(string virtualPath, params string[] files)
        {
            addBundle(virtualPath, true, files);
            return Styles.Render(virtualPath);
        }

        public static IHtmlString AddScriptUrl(string virtualPath, params string[] files)
        {
            addBundle(virtualPath, false, files);
            return Scripts.Url(virtualPath);
        }

        public static IHtmlString AddStyleUrl(string virtualPath, params string[] files)
        {
            addBundle(virtualPath, true, files);
            return Styles.Url(virtualPath);
        }
    }
}
نکته دیگری که در این کلاس سفارشی درنظر گرفته شده، عدم فعال سازی مباحث Bundling and minification در حالت Debug است. اگر در وب کانفیگ، تنظیمات پروژه را بر روی Release قرار دهید فعال خواهد شد (مناسب برای توزیع) و در حالت توسعه برنامه (حالت دیباگ)، این اسکریپت‌ها و فایل‌های CSS، قابلیت دیباگ و بررسی را نیز خواهند داشت.

پس از افزودن کلاس‌های کمکی فوق، به فایل layout پروژه مراجعه کرده و تعاریف ذیل را به ابتدای فایل اضافه نمائید:
@using Mvc4TwitterBootStrapTest.Helper
<!DOCTYPE html>
<html>
<head>
    <title>@ViewBag.Title</title>
    @BundleConfig.AddStyles("~/Content/css",
                            "~/Content/bootstrap.css",
                            "~/Content/bootstrap-responsive.css",
                            "~/Content/Site.css"
                            )
    @BundleConfig.AddScripts("~/Scripts/js",
                            "~/Scripts/jquery-1.9.1.min.js",
                            "~/Scripts/jquery.validate.min.js",
                            "~/Scripts/jquery.unobtrusive-ajax.min.js",
                            "~/Scripts/jquery.validate.unobtrusive.min.js",
                            "~/Scripts/bootstrap.min.js"
                            )
    @RenderSection("JavaScript", required: false)
</head>
و اگر برنامه را اجرا کنید، بجای حداقل 8 مدخل اشاره کننده به فایل‌های اسکریپت و CSS مورد نیاز یک برنامه ASP.NET MVC، فقط دو مدخل ذیل را مشاهده خواهید نمود:
<!DOCTYPE html>
<html>
<head>
    <title>Index</title>
    <link href="/Content/css?v=vsUQD0OJg4AJ-RZH8jSRRCu_rjl2U1nZrmSsaUyxoAc1" rel="stylesheet"/>
    <script src="/Scripts/js?v=GezdoTDiWY3acc3mI2Ujm_7nKKzh6Lu1Wr8TGyyLpW41"></script>
</head>
به این ترتیب تعداد رفت و برگشت‌های مرورگر به سرور، برای دریافت فایل‌های مورد نیاز جهت رندر صحیح یک صفحه، به شدت کاهش خواهد یافت. همچنین این فایل‌ها در حالت release فشرده نیز خواهند شد؛ به همراه تنظیم خودکار هدر کش شدن آن‌ها برای مدتی مشخص، جهت کاهش بار سرور و کاهش پهنای باند مصرفی آن.


مفاهیم پایه‌ای Twitter Bootstrap

الف) Semantic class names
به عبارتی کلاس‌های Twitter Bootstrap دارای نام‌هایی معنا دار و مفهومی می‌باشند؛ مانند کلاس‌های CSS‌ایی، به نام‌های Succes، Error، Info و امثال آن. این نام‌ها مفهومی را می‌رسانند؛ اما در مورد نحوه پیاده سازی آن‌ها جزئیاتی را بیان نمی‌کنند.
برای نمونه می‌توان کلاسی را به نام redText ایجاد کرد. هر چند این نام، توضیحاتی را در مورد علت وجودی‌اش بیان می‌کند، اما بسیار ویژه بوده و در مورد جزئیات پیاده سازی آن نیز اطلاعاتی را ارائه می‌دهد. در این حالت redText معنایی ندارد. چرا یک Text باید قرمز باشد؟ برای مثال این متن قرمز است چون مثلا شخصی، به آن رنگ ویژه علاقه دارد، یا اینکه قرمز است بخاطر نمایش خطایی در صفحه؟ به همین جهت در Twitter Bootstrap از نام‌های مفهومی یاده شده، مانند Error استفاده می‌شود. نام‌هایی معنا دار اما بدون دقیق شدن در مورد ریز جزئیات پیاده سازی آن‌ها. در این حالت می‌توان قالب جدیدی را تدارک دید و با ارائه تعاریف جدیدی برای کلاس Error و نحوه نمایش دلخواهی را به آن اعمال نمود.
یا برای نمونه نام rightside را برای نمایش ستونی در صفحه، درنظر بگیرید. این نام بسیار ویژه است؛ اما Sematic name آن می‌تواند sidebar باشد تا بدون دقیق شدن در جزئیات پیاده سازی آن، در چپ یا راست صفحه قابل اعمال باشد.
Semantic class names کلیدهایی هستند جهت استفاده مجدد از قابلیت‌های یک فریم ورک CSS.

ب) Compositional classes
اکثر کلاس‌های Twitter Bootstrap دارای محدوده کاری کوچکی هستند و به سادگی قابل ترکیب با یکدیگر جهت رسیدن به نمایی خاص می‌باشند. برای مثال به سادگی می‌توان به یک table سه ویژگی color، hover و width برگرفته شده از Twitter Bootstrap را انتساب داد و نهایتا به نتیجه دلخواه رسید؛ بدون اینکه نگران باشیم افزودن کلاس جدیدی در اینجا بر روی سایر کلاس‌های انتساب داده شده، تاثیر منفی دارد.

ج) Conventions
برای استفاده از اکثر قابلیت‌های این فریم ورک CSS یک سری قراردادهای پیش فرضی وجود دارند. برای مثال اگر از کلاس توکار pagination به همراه یک سری ul و li استفاده کنید، به صورت خودکار یک pager شکیل ظاهر خواهد شد. یا برای مثال اگر به یک html table کلاس‌های table table-striped table-hover را انتساب دهیم، به صورت خودکار قراردادهای پیش فرض table مجموعه Twitter Bootstrap به آن اعمال شده، به همراه رنگی ساختن یک درمیان زمینه ردیف‌ها و همچنین فعال سازی تغییر رنگ ردیف‌ها با حرکت ماوس از روی آن‌ها.


طرحبندی صفحات یک سایت به کمک Twitter Bootstrap

بررسی Grid layouts

Layout به معنای طرحبندی و چیدمان محتوا در یک صفحه است. یکی از متداولترین روش‌های طرحبندی صفحات چه در حالت چاپی و چه در صفحات وب، چیدمان مبتنی بر جداول و گریدها است. از این جهت که نحوه سیلان و نمایش محتوا از چپ به راست و یا راست به چپ را به سادگی میسر می‌سازند؛ به همراه اعمال حاشیه‌های مناسب جهت قسمت‌های متفاوت محتوای ارائه شده. Grid در طرحبندی، نمایش بصری نخواهد داشت اما در ساختار صفحه وجود داشته و مباحثی مانند جهت، موقعیت و یکپارچگی و یکدستی طراحی را سبب می‌شود.
به علاوه مرورگرها و مفهوم Grid نیز به خوبی با یکدیگر سازگار هستند. در دنیای HTML و ،CSS طراحی‌ها بر اساس مفهوم ساختار مستطیلی اشیاء صورت می‌گیرد:


برای نمونه در اینجا تصویر CSS Box Model را مشاهده می‌کنید. به این ترتیب، هر المان دارای محدوده‌ای مستطیلی با طول و عرض مشخص، به همراه ویژگی‌هایی مانند Margin، Border و Padding است.
در سال‌های اولیه طراحی وب، عموما کارهای طراحی صفحات به کمک HTML Tables انجام می‌شد. اما با پخته‌تر شدن CSS، استفاده از Tables برای طراحی صفحات کمتر و کمتر گشت تا اینکه نهایتا فریم ورک‌های CSS ایی پدید آمدند تا طراحی‌های مبتنی بر CSS را با ارائه گرید‌ها، ساده‌تر کنند. مانند Blue print، 960 GS و ... Twitter Bootstrap که طراحی مبتنی بر گرید‌های CSS ایی را به مجموعه قابلیت‌های دیگر خود افزوده است.


بررسی Fixed Grids

در اینجا در صفحه layout برنامه، یک Div دربرگیرنده دو Div دیگر را مشاهده می‌کنید:
<body>
    <div>
        <div>
            <h1>
                Title Title
            </h1>
            Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text
        </div>
        <div>
            @RenderBody()
        </div>
    </div>
</body>
اگر در این حالت صفحه را در مرورگر بررسی کنیم، تمام قسمت‌های نمایش داده شده به همین نحو از بالا به پایین صفحه قرار خواهند گرفت. اما قصد داریم این محتوا را، دو ستونی نمایش دهیم. Div به همراه Title در یک طرف صفحه و Div دربرگیرنده محتوای صفحات، در قسمتی دیگر.
برای اینکار در Twitter Bootstrap از کلاسی به نام row استفاده می‌شود که بیانگر یک ردیف است. این کلاس را به خارجی‌ترین Div موجود اعمال خواهیم کرد. در یک صفحه، هر تعداد row ایی را که نیاز باشد، می‌توان تعریف کرد. داخل این ردیف‌ها، امکان تعریف ستون‌های مختلف و حتی تعریف ردیف‌های تو در تو نیز وجود دارد. هر ردیف Twitter Bootstrap از 12 ستون تشکیل می‌شود و برای تعریف آن‌ها از کلاس span استفاده می‌گردد. در این حالت جمع اعداد ذکر شده پس از span باید 12 را تشکیل دهند.
<body>
    <div class="row">
        <div class="span7">
            <h1>
                Title Title
            </h1>
            Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text Text
        </div>
        <div  class="span5">
            @RenderBody()
        </div>
    </div>
</body>
برای نمونه در اینجا کلاس row به خارجی‌ترین Div موجود اعمال شده است. دو Div داخلی هر کدام دارای کلاس‌های span ایی جهت تشکیل ستون‌های داخل این ردیف هستند. جمع اعداد بکارگرفته شده پس از آن‌ها، عدد 12 را تشکیل می‌دهد. به این ترتیب به یک طراحی دو ستونی خواهیم رسید.


در این تصویر، قسمت RenderBody کار رندر اکشن متد Index کنترلر Home برنامه را با Viewایی معادل کدهای ذیل، انجام داده است:
@{
    ViewBag.Title = "Index";
}
<h2>
    Index</h2>
<div class="hero-unit">
    <h2>@ViewBag.Message</h2>
    <p>
        This is a template to demonstrate the way to beautify the default MVC template
        using Twitter Bootstrap website. It is pretty simple and easy.</p>
    <p>
        <a href="http://asp.net/mvc" class="btn btn-primary btn-large" style="color: White;">
            To learn more about ASP.NET MVC visit &raquo;</a></p>
</div>

درک نحوه عملکرد Grid در Twitter Bootstrap

در مثال ذیل 5 ردیف را مشاهده می‌کنید:
    <div class="row">
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
        <div class="span1">1</div>
    </div>
    <div class="row">
        <div class="span3">3</div>
        <div class="span4">4</div>
        <div class="span5">5</div>
    </div>
    <div class="row">
        <div class="span5">5</div>
        <div class="span7">7</div>
    </div>
    <div class="row">
        <div class="span3">3</div>
        <div class="span7 offset2">7 offset 2</div>
    </div>
    <div class="row">
        <div class="span12">12</div>
    </div>
در هر ردیف، امکان استفاده از حداکثر 12 ستون وجود دارد که یا می‌توان مانند ردیف اول، 12 ستون را ایجاد کرد و یا مانند ردیف دوم، سه ستون با عرض‌های متفاوت (و یا هر ترکیب دیگری که به جمع 12 برسد).
در ستون چهارم، از کلاس offset نیز استفاده شده است. این مورد سبب می‌شود ستون جاری به تعدادی که مشخص شده است به سمت چپ (با توجه به استفاده از حالت RTL در اینجا) رانده شود و سپس ترسیم گردد.
یا اینکه می‌توان مانند ردیف آخر، یک ستون را به عرض 12 که در حقیقت 940 پیکسل است، ترسیم نمود.
برای اینکه بتوانیم این گرید تشکیل شده و همچنین ستون‌ها را بهتر مشاهده کنیم، به فایل style.css سایت، تنظیم زیر را اضافه کنید:
[class*="span"]
{
background-color: lightblue;
text-align: center;
margin-top: 15px;
}


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


یک نکته
اگر نمی‌خواهید که چنین رفتار واکنش‌گرایی بروز کند، نیاز است کلیه ردیف‌ها را در div ایی با کلاسی به نام container محصور کنید.
به این ترتیب ابتدا گرید نمایش داده شده، در میانه صفحه ظاهر خواهد شد (پیشتر از سمت راست شروع شده بود). همچنین دیگر با کوچک و بزرگ شدن اندازه مرورگر، ستون‌ها به شکل یک پشته بر روی هم قرار نخواهند گرفت. (اگر پس از این تنظیم، چنین قابلیتی را مشاهده نکردید و هنوز هم طراحی، واکنشگرا بود، تعریف bootstrap-responsive.css را نیاز است برای آزمایش، از هدر صفحه حذف کنید)


بررسی Fluid Grids

به گرید قسمت قبل از این جهت Fixed Grid گفته می‌شود که عرض هر span آن با یک عدد مشخص تعیین گشته است. اما در حالت Fluid Grid، عرض هر span برحسب درصد تعیین می‌شود. بکارگیری درصد در اینجا به معنای امکان تغییر عرض یک ستون بر اساس عرض جاری Container آن است. در اینجا span12 دارای عرض 100 درصد خواهد بود.
در مثال قبل، برای استفاده از Fluid grids، تنها کافی است هرجایی کلاسی مساوی row وجود دارد، به row-fluid تغییر کند. همچنین کلاس container را به container-fluid تغییر دهید.
برای آزمایش آن، اندازه و عرض نمایشی مرورگر خود را تغییر دهید. اینبار مشاهده خواهید کرد که برخلاف حالت Fixed Grid، عرض ستون‌ها به صورت خودکار کم و زیاد می‌شوند. این مورد بر روی محتوای قرار گرفته در این ستون‌ها نیز تاثیر گذار است. برای مثال اگر یک تصویر را در حالت Fluid grid در ستونی قرار دهید، با تغییر عرض مرورگر، اندازه این تصویر نیز تغییر خواهند کرد؛ اما در حالت Fixed Grid خیر.
حالت Fluid، شیوه متداول استفاده از bootstrap در اکثر سایت‌های مهمی است که تابحال از این فریم ورک CSS استفاده کرده‌اند.



مروری بر طراحی واکنشگرا یا Responsive

این روزها تعدادی از کاربران، با استفاده از ابزارهای موبایل و تبلت‌ها از وب سایت‌ها بازدید می‌کنند. هر کدام از این‌ها نیز دارای اندازه نمایشی متفاوتی می‌باشند. بنابراین نیاز خواهد بود تا حالت بهینه‌ای را جهت اینگونه وسایل نیز طراحی نمود. حالت بهینه در اینجا به معنای قابل خواندن بودن متون، امکان استفاده از لینک‌های ورود به صفحات مختلف و همچنین حذف اسکرول و مباحث زوم جهت مشاهده صفحات است.
یکی از ویژگی‌های CSS به نام media چنین قابلیتی را فراهم می‌سازد. برای نمونه قسمتی از فایل bootstrap-responsive.css دارای چنین تعاریفی است:
@media (min-width: 768px) and (max-width: 979px) {
    .hidden-desktop {
        display: inherit !important;
    }
توسط این تنظیمات، شیوه نامه تعیین شده، تنها به صفحاتی با اندازه‌هایی بین 768 تا 979 پیکسل اعمال می‌شود (تعدادی از تبلت‌ها برای نمونه).
Bootstrap برای مدیریت اندازه‌های مختلف وسایل موبایل، شیوه‌نامه‌های خاصی را تدارک دیده است که از اندازه px480 و یا کمتر، تا px1200 و یا بیشتر را پوشش می‌دهد.
به این ترتیب با اندازه px940 که پیشتر در مورد آن بحث شد، اندازه span12 به صورت خودکار به اندازه‌های متناسب با صفحات نمایشی کوچکتر تنظیم می‌گردد. همچنین برای اندازه‌های صفحات کوچکتر از 768px به صورت خودکار از Fluid columns استفاده می‌گردد.
تنها کاری که برای اعمال این قابلیت باید صورت گیرد، افزودن تعاریف فایل bootstrap-responsive.css به هدر صفحه است که در قسمت قبل انجام گردید. این فایل باید پس از فایل اصلی bootstrap.css اضافه شود.


کلاس‌های کمکی طراحی واکنشگرا

Bootstrap شامل تعدادی کلاس کمکی در فایل bootstrap-responsive.css خود می‌باشد شامل visible-phone، visible-tablet و visible-desktop به علاوه hidden-phone، hidden-tablet و hidden-desktop. به این ترتیب می‌توان محتوای خاصی را جهت وسایل ویژه‌ای نمایان یا مخفی ساخت.
برای مثال محتوای مشخص شده با کلاس hidden-desktop، در اندازه وسایل دسکتاپ نمایش داده نخواهد شد.
برای آزمایش آن، شش div را با کلاس‌های یاد شده و محتوایی دلخواه ایجاد کرده و سپس اندازه عرض مرورگر را تغییر دهید تا بهتر بتوان مخفی یا نمایان ساختن محتوا را بر اساس اندازه صفحه جاری درک کرد.
یکی از کاربردهای این قابلیت، قرار دادن تبلیغاتی با اندازه‌های تصاویری مشخص برای وسایل مختلف است؛ بجای اینکه منتظر شویم تا Fluid layout اندازه تصاویر را به صورت خودکار کوچک یا بزرگ کند، که الزاما بهترین کیفیت را حاصل نخواهد ساخت.
    <div class="container-fluid">
        <div class="row-fluid">
            <div class="span4">
                <div class="visible-phone">
                    visible-phone</div>
            </div>
            <div class="span4">
                <div class="visible-tablet">
                    visible-tablet</div>
            </div>
            <div class="span4">
                <div class="visible-desktop">
                    visible-desktop</div>
            </div>
        </div>
    </div>
    <div class="container-fluid">
        <div class="row-fluid">
            <div class="span4">
                <div class="hidden-phone">
                    hidden-phone</div>
            </div>
            <div class="span4">
                <div class="hidden-tablet">
                    hidden-tablet</div>
            </div>
            <div class="span4">
                <div class="hidden-desktop">
                    hidden-desktop</div>
            </div>
        </div>
    </div>

مطالب
استفاده از اسمبلی‌های دات نت 2 در یک پروژه دات نت 4

تنظیمات برنامه BloggerToChm را به دات نت 4 تغییر دادم و بدون مشکل کامپایل شد. اما حین اجرا بلافاصله با خطای زیر برنامه اجرا نمی‌شد:

Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.
مشکل هم از اسمبلی‌های مرتبط با SQLite است که هنوز برای دات نت 4 کامپایل نشده‌اند. برای رفع این مشکل باید تغییر زیر را (تنظیم گزینه useLegacyV2RuntimeActivationPolicy) به فایل app.config برنامه اضافه کرد:
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
</configuration>
برای برنامه‌های ASP.NET نیز به همین صورت است. در آنجا این تغییرات باید به Web.Config اضافه شوند.

نظرات مطالب
ASP.NET MVC #12
خواهش می‌کنم. یک مورد دیگر هم هست:
- متدی مانند Html.DisplayFor بر اساس خواص مدل‌های برنامه، به صورت Strongly typed تعریف می‌شود (به کمک همین lambda expressions ایی که مشاهده می‌کنید). اگر مدلی تغییر کند، این View دیگر اجرا نخواهد شد و خطای کامپایل رو دریافت می‌کنید. اما در حالت نوشتن مستقیم و معمولی مثلا یک برچسب یا یک تکست باکس و موارد مشابه، این compile time checking رو از دست خواهید داد.
نظرات مطالب
EF Code First #12
جهت اطلاعات بیشتر مراجعه کنید به مطلب MARS؛ یک شیء اتصالی با یک کانکشن با چندین درخواست و یا یک شیء اتصالی و چندین بار باز و بسته شدن اتصال‌های مدیریت شده‌ی توسط آن. هر دو مورد با یک Context ممکن است. اما در طی یک Context یک شیء اتصالی بیشتر ایجاد نمی‌شود (تغییرات شماره IDهای اتصال را در DNTProfiler بررسی کنید).
مطالب
استفاده از چند Routing در یک پروژه ASP.NET MVC بدون درد و خونریزی

کار کردن با مسیریابی برای یک پروژه ساده ، نیاز به طراحی پیچیده ندارد. مسیریابی پیش فرض موجود در فایل RoutConfig.cs برای کارهای ابتدایی کافیست. اما اگر کمی کار پیچیده شود و صفحات مختلفی با منطق‌های متفاوتی ایجاد کنیم، ممکن است با مشکل روبرو شویم. در MVC5 به کمک دخالت ویژگی‌ها در مسیریابی، کار ساده شده است اما در MVC4 و قبل از آن چه باید کرد؟ پیش از بسط مساله، ابتدا این سوال را پاسخ میدهیم که چگونه صفحه‌ی start پروژه انتخاب میشود؟ 

مسیریابی پیش فرض یک پروژه MVC به شکل زیر است : 

routes.MapRoute(
           name: "Default",
           url: "{controller}/{action}/{id}",
           defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
       );

وقتی یک پروژه MVC را بررسی کنید، مشاهده می‌کنید که در شاخه‌ی اصلی آن، فایل index یا default وجود ندارد و اصولا منطق کار با اکشن‌ها و صدا زدن آنها و دیدن پاسخ‌ها توسط View، این اتفاق را توجیه میکند. پس وقتی درخواستی به سمت شاخه‌ی اصلی ما فرستاده میشود، مسیریابی وارد عمل می‌شود. وقتی پروژه را اجرا میکنید، متد RegisterRoutes از شیءایی که از کلاس RoutingConfig.cs ساخته شده (به فایل global.asax نگاه کنید) فراخوانی می‌شود و شیء Routes از " قالب آدرس " هایی که ما تعیین کرده ایم پُر می‌شود. بخشی از فایل RoutConfig.cs را در تصویر زیر می‌بینیم. 

به Url کد فوق نگاه کنید که در حقیقت آدرسی نسبی است. آدرس ما به طور کامل به شکل زیر قابل تعریف است: 

http://mydomain.com/{controller}/{action}/{id}

واضح است که درخواست اولیه به سایت ما دارای بخش‌های controller و action و بخش اختیاری id نیست. پس به شکل پیشفرض بر اساس آنچه در جلوی خصوصیت defaults نوشته شده است، فراخوانی خواهد شد. یعنی اکشن Index از داخل کنترلر Home صدا زده می‌شود و چون id نداریم، هیچ نوع id به متد (اکشن) index ارسال نخواهد شد.

یک روتینگ دیگر به شکل زیر در بالای کد فوق اضافه کنید : 

routes.MapRoute(
          name: "Default1",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "News", action = "Index", id = UrlParameter.Optional }
      );

حتما متوجه شدید که اینبار با اجرای پروژه، متد(اکشن) Index از کنترلر News فراخوانی خواهد شد. در حقیقت اولین روتینگ ساخته شده همان چیزیست که Asp.NET MVC پس از دریافت ریکوئست به آن رجوع میکند. دقت کنید که مقدار name در دو MapRouting فوق متفاوت است. 

اما این اسم‌ها(name ها) به چه دردی می‌خورند؟

 قبل از توضیح در این مورد، default1 را از پروژه حذف میکنیم و با همان MapRouting پیش فرض به کار خود ادامه می‌دهیم. ابتدا مروری بر عملکرد MapRouting انجام میدهیم. 

همانطور که می‌دانید برای ایجاد یک لینک از طریق ویوی Razor از چیزی شبیه دستور زیر می‌توانیم استفاده کنیم: 

<a href="@Url.Action("post", "News", new { id = @item.PostName }, null)"> @item.PostTitle </a>

فرض کنید یک سایت خبری داریم و گروه‌های مختلف آن شامل مطالب متفاوتی هستند. کنترلری به نام News ایجاد کرده‌ایم، که دارای یک اکشن به نام post است که نام مطلب را دریافت کرده و یک View به ما برمی‌گرداند. بخش هایی از صفحه اول را در تصاویر زیر می‌بینید. تصویر آخر، بخش پایین صفحه اخبار است، که بوسیله لینک (لینک های) موجود صفحات جابجا میشوند. 

وقتی کد فوق در یک صفحه فراخوانی میشود، کالکشن Routes بررسی می‌شود و لینک مورد نظر، با توجه به ورودی‌های Url.Action ساخته می‌شود. ما یک MapRouting به نام Default در کالکشن Routes داریم، پس Url.Action لینک زیر را می‌سازد: 

http://mydomain.com/news/post/نام-مقاله

استفاده از MapRouting مثل یک جعبه است که ورودی آن مقادیر موجود در Url.Action می‌باشد و آنچه به ما بر می‌گرداند یک آدرس برای فراخوانی اکشن مورد نیاز است. (آخرین پارامتر، null قرار داده شده که برای تعیین نوع پروتکل استفاده میشود که http یا https میتواند باشد.)

برای فراخوانی صفحه‌ی اخبار (لیست اخبار) می‌توانید دستور زیر را بنویسید : 

@Html.ActionLink("اخبار", "Index", "News", null, new { @class="btn btn-success"})

این دستور تگ anchor را نیز می‌سازد و به کمک MapRouting  آدرسی شبیه آدرس زیر را به ما بر می‌گرداند. (به تفاوت دو دستور دقت کنید): 

<a href="/News">اخبار</a>

واضح است که چون MapRouting یک حالت پیش فرض درونی دارد که دارای اکشن دیفالت index است، پس ActionLink اگر ببیند لینکش در صفحه قرار است به شکل /news/index تعریف شود خود بخود بخش index را حذف میکند.

فرض کنید بعد از کلیک روی لینک فوق، اکشن index ، یک آبجکتِ لیستی با 10 خبر آخر، ایجاد میکند. پس ما میخواهیم قابلیت صفحه بندی را برای لیست اخبار فعال کنیم و در هر صفحه 10 مطلب را نمایش دهیم. مشکل از همینجا آغاز میشود. MapRouting فعلی جوابگوی ما نخواهد بود و آدرس را به شکل زیر نمایش میدهد. 

<a href="/News/index?pid=2">صفحه بعد</a>

و آنچه ما در View نوشته‌ایم چیزی شبیه کد زیر است :

@Html.ActionLink("صفحه بعد", "index", "News", new { pid = …. }, null)

مشکلی در ارجاع به صفحات وجود ندارد و با کلیک روی لینک "صفحه بعد" مقدار عدد 2 به اکشن index ارسال میشود و اگر کد نویسی را برای take و skip کردن لیست، درست انجام شده باشد، نتیجه مورد نظر نمایش داده خواهد شد. اما آدرس فوق آدرس زیبایی نیست. اولین فکری که به ذهن برنامه نویس میرسد، ایجاد یک مسیریابی دیگر است. فکر درستیست؛ اما اگر چند بار دیگر این اتفاق بیفتد و در بخش هایی از برنامه نیاز به روتینگ پیدا کنید و روتینگ‌های جدید ایجاد کنید متوجه خواهید شد که مدیریت این MapRouting ‌ها کار خسته کننده و طاقت فرسایی خواهد شد، مخصوصا اگر بدانید که فقط مجاز به استفاده از یک  پارامتر optional در هر MapRouting هستید! دست شما کاملا بسته است. لینک‌های بالای سایت را اصلاح میکنید ولی لینک‌های پایین سایت خراب میشوند و بالعکس.

به هر حال MapRouting زیر را به RoutConfig.cs اضافه میکنیم : 

           routes.MapRoute("PostPaging", "{controller}/{action}/{id}/{pid}",
            defaults: new
            {
                controller = "News",
                action = "Index",
                id = "all",
                pid = UrlParameter.Optional

            }
            );
-  اسم این MapRouting ، دیگر Default نیست.
-  یک پارامتر pid اضافه‌تر از MapRouting اولی دارد.
-   pid به عنوان  یک پارامتر اختیاری تعریف شده است، پس "قالب آدرس" بسیار شبیه مپ روتینگ قبلی است.
-  مقدار id اختیاری نیست، چون قرار است در آینده بتوانیم گروه‌های مختلف موجود در بخش اخبار را صفحه بندی کنیم و قرار نیست پشت سر هم MapRouting ایجاد کنیم و کافیست به جای id اسم گروه را بنویسیم. در حالتیکه اسمی از گروه درلینکهایمان نبرده باشیم به شکل پیشفرض all قرار داده میشود که یعنی کل اخبار مد نظر است. (در اکشن مربوطه باید این تصمیمات را لحاظ کنیم)
-  حتما این MapRouting را بعد از MapRouting اولیه بنویسید، کمی پیشتر، علت این امر توضیح داده شد و گفته شد اولین چیزی که MVC پس از درخواست ما میبیند به عنوان Routing بررسی می‌کند (درخواست اولیه) و چون ساختار  MapRouting فوق تا اندازه ای شبیه ساختار Default MapRouting است ممکن است با فراخوانی سایت مشکل ایجاد شود.
- میتوانید MapRouting
را کمی خاص‌تر هم بنویسیم : 
routes.MapRoute("NewsPaging", "News/index/{id}/{pid}",
            defaults: new
            {
                controller = "News",
                action = "Index",
                id = "all",
                pid = UrlParameter.Optional
            }
            );
اینکار بستگی به پروژه‌ی شما دارد، مپ روتینگ فوق این مزیت را دارد که با مپ روتینگ‌های دیگر به سختی قاطی میشود! یعنی حتا اگر قبل از مپ روتینگ دیفالت نوشته شود برنامه با مشکل مواجه نخواهد شد، چون اصلا شکل درخواست اولیه به سایت، چیزی شبیه این آدرس نیست. اما خاص بودن آن و همچنین نوع بهره گیری از آن با کمک Action یا ActioLink شاید شما را سردرگم خواهد کند.  
اما مشکل این MapRouting ‌ها چیست؟
درخواست به سایت آمده و قرار است سایت بارگذاری شود؛ ترتیب زیر در شیء routes ثبت شده است :  

در صفحه اول ما لینکی به شکل زیر گذاشته ایم : 
@Html.ActionLink("اخبار", "Index", "News", null, new { @class="btn btn-success"})
مشکلی پیش نخواهد آمد. روند فوق تکرار میشود و ActionLink برای ساختن لینک نهایی از MapRouting اول استفاده میکند (بدون اینکه به MapRouting دوم نگاه کند).

در صفحه اخبار، لینک "صفحه بعد" وجود دارد ، آیا این لینک به شکل صحیح نمایش داده میشود؟ خیر! نتیجه کار را ببینید : 
<a href="/News/index?pid=2">صفحه بعد</a>
 علت واضح است، ActionLink اصلا MapRouting دومی را نمی‌بیند و با همان MapRouting اولی لینک فوق را ساخته است. جای MapRouting ‌ها را عوض کنیم؟ مطمئنید با اینکار درخواست مربوط به صفحه اول شما سلامت به مقصد میرسد؟ مطمئنید با اینکار بقیه Action ‌ها و ActionLink ‌های موجود در صفحه اول شما به درستی تبدیل میشوند؟
به نظر میرسد یک طرح دقیق برای آدرس دهی شاید این مسائل را حل کند ولی نه وقت داریم و نه اعصاب. 


دقت کنید ما برای کار با روتینگ با دو مساله مواجه هستیم :
1. ساخته شدن لینک‌ها توسط هلپر‌ها (که از مپ روتینگ‌های ثبت شده ما پیروی میکند)
2. واکنش پروژه به درخواست‌های دریافتی و هدایت آن به اکشن‌های مورد نظر که کاملا به ترتیب ثبت مپ روتینگ‌ها در کالکشن Routes بستگی دارد .


 خوشبختانه یک هلپر به شکل زیر در MVC وجود دارد : 
 @Html.RouteLink("صفحه بعد", "PostPaging", new { action = "cat", controller = "News", id =. .., pid = ...})
مقدار اول متن لینک، مقدار دوم نام MapRouting ،مقدار سوم یک آبجکت است که نوع اکشن و کنترلر و پارامترهای مورد نظر MapRouting را تعیین میکند. به همین سادگی! این پاسخ به همان سوالیست که در ابتدای مقاله مطرح شد. "نام گذاری مپ روتینگ‌ها به چه درد میخورد؟"
نتیجه برای لینک موجود در صفحه اخبار چیزی شبیه شکل زیر خواهد شد :
<a href="/News/cat/all/2">صفحه بعد</a>
چند شکل دیگر از این هلپر را ببینید : 
@Html.RouteLink("اخبار", "Default", new { controller = "news" })

@Html.RouteLink("درباره ما", "pages", new RouteValueDictionary(new { controller="Page", action="Index", pagename="درباره-ما"}), new Dictionary<string, Object> { { "data-toggle", "popover" }, { "data-placement", "top" } })

همانطور که میبینید در RoutLink اولی، اخبار را به کمک MapRouting با نام default بازنویسی میکنیم و نتیجه چیزی شبیه کد زیر خواهد شد : 

<a href="/News">اخبار</a>

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

اما دقت کنید که از یک MapRouting جدید استفاده کردیم که نامش pages است، 

 این MapRoutnig را قبل از دیگر Routing ‌ها می‌نویسیم؟ وسط دو MapRouting قبلی مینویسیم؟ آخر MapRouting ‌ها مینویسیم؟ آیا فرقی میکند؟ اگر سریع بگوییم خــیر! اشتباه کرده ایم. واقعا فرق میکند.

دقت کنید موضوع MapRouting فقط ایجاد یک لینک‌تر و تمیز نیست؛ RoutLink یک لینک تمیز بر اساس مپ روتینگی که نامش برده شده ایجاد میکند اما تضمین نمیکند که با کلیک بر روی لینک به هدف برسیم و به خطای 404 برخورد نکنیم! اگر روی لینک کلیک کنید آدرس شروع به تفسیر شدن میکند و این تفسیر اصلا ربطی به نامی که به RoutLink داده ایم ندارد و ترتیب موجود در کالکشن ایجاد شده در RoutConfig تعیین کننده است.(آبجکت Routes ) اگر MapRouting فوق را در انتهای بقیه بگذاریم صفحه اول لود میشود ولی با کلیک روی "درباره ما" صفحه پیغام خطا خواهد داد.
 باید به یاد داشته باشیم برای اجرای درخواست (کلیک روی لینک)، آنچه برای ASP.NET MVC اهمیت دارد، ترتیب قرار گیری MapRouting ‌ها در RouteRegister است  و ما به کمک RoutLink تنها مشکل ساخت لینک‌ها بر اساس قالب MapRouting مورد نظرمان را حل کردیم و این به ما تضمینی برای هدایت آن لینک به مکان درست را نخواهد داد. 

اگر ترتیب به شکل زیر باشد :

1
2
3
باشد. درخواست اولیه برای بالا آمدن سایت به مشکل برخورد نمی‌کند چون همان مپ روتینگ 1 اجرا میشود. اما مشکل فوق به وجود خواهد آمد و خطای 404 با کلیک بر روی "درباره ما" نمایش داده خواهد شد چون با کلیک روی "درباره ما" مپ روتینگ شماره 1 وارد عمل میشود.

اگر ترتیب به شکل زیر باشد :

3
2
1
آیا اصلا صفحه اول سالم لود خواهد شد؟ خیر! درخواست نسبی " / " (یا به طور کامل http://mydomain.com ) شماره 3 را به خیر پشت سر میگذارد، چون اصلا چیزی به نام page در آدرس وجود ندارد که از این MapRouting بخواهد پیروی کند. اما در شماره 2 گیر می‌افتد چون این فرمت را حفظ کرده است :
"{controller}/{action}/{id}/{pid}"
Pid که اختیاریست (بر اساس قوانین تعریف شده در مپ روتینگ شماره 2) بقیه موارد نیز حالت دیفالت دارند، یعنی اگر تعریف نشده باشند (مثل همین درخواست) خودبخود جایگذاری میشوند. پس به طور کلی صفحه اول ما تغییر می‌کند و اکشن index از کنترلر Home اجرا نمیشود و به جایش اکشن Index از کنترلر News اجرا میشود و صفحه اول را از دست میدهیم و صفحه اخبار را میبینیم! نتیجه اینکه نباید هرگز 2 را قبل از 1 قرار دهیم.
اگر ترتیب به شکل زیر باشد :
3
1
2
این همان چیزیست که مد نظر ماست. اولا 1 قبل از 2 است و صفحه اول برای لود شدن به مشکل برخورد نمیکند.
ثانیا وقتی روی "درباره ما" کلیک میکنیم همان شماره 3 فراخوانی میشود و بقیه مپ روتینگ‌ها اعمال نمیشوند.

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

مطالب
کامپایل خودکار یک پروژه برای دو فریم ورک
فرض کنید می‌خواهید زمانیکه دکمه‌ی build در VS.NET فشرده شد، دو نسخه‌ی دات نت 4 و دات نت 4.5، از پروژه‌ی شما در پوشه‌های مجزایی کامپایل شده و قرار گیرند. در ادامه نحوه‌ی انجام این‌کار را بررسی خواهیم کرد.


پروژه نمونه
تنظیمات ذیل را بر روی یک پروژه از نوع class library دات نت 4 در VS 2013 اعمال خواهیم کرد.


ویرایش فایل پروژه برنامه

برای اینکه تنظیمات کامپایل خودکار مخصوص دات نت 4.5 را نیز به این پروژه دات نت 4 اضافه کنیم، نیاز است فایل csproj آن‌را مستقیما ویرایش نمائیم. این تغییرات شامل مراحل ذیل هستند:
الف) تعریف متغیر Framework
  <PropertyGroup>
    <!-- ...-->
    <Framework Condition=" '$(Framework)' == '' ">NET40</Framework>
  </PropertyGroup>
به ابتدای فایل csproj در قسمت PropertyGroup آن یک متغیر جدید را به نام Framework اضافه کنید. از این متغیر در شرط‌های کامپایل استفاده خواهد شد.


ب) ویرایش مسیر خروجی تنظیمات کامپایل فعلی
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <!-- ...-->
    <OutputPath>bin\$(Configuration)\$(Framework)\</OutputPath>
  </PropertyGroup>
در حال حاضر حداقل تنظیمات کامپایل حالت debug، در فایل پروژه موجود است. مقدار OutputPath آن‌را به نحو فوق تغییر دهید تا خروجی نهایی را در پوشه‌ای مانند bin\Debug\NET40 ایجاد کند.
بدیهی است اگر حالت release هم وجود دارد، نیاز است مقدار OutputPath آن‌را نیز به همین ترتیب ویرایش کرد.


ج) افزودن تنظیمات کامپایل دات نت 4.5 به پروژه جاری
  <PropertyGroup Condition=" '$(Framework)' == 'NET45' And '$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>bin\$(Configuration)\$(Framework)\</OutputPath>
    <DefineConstants>DEBUG;TRACE;NET45</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(Framework)' == 'NET45' And '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
    <PlatformTarget>AnyCPU</PlatformTarget>
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\$(Configuration)\$(Framework)\</OutputPath>
    <DefineConstants>TRACE;NET45</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
در اینجا تنظیمات حالت debug و release مخصوص دات نت 4.5 را مشاهده می‌کنید. برای نگارش‌های دیگر، تنها کافی است مقدار TargetFrameworkVersion را ویرایش کنید.
همچنین اگر به DefineConstants آن دقت کنید، مقدار NET45 نیز به آن اضافه شده‌است. این مورد سبب می‌شود که بتوانید در پروژه‌ی جاری، شرطی‌هایی را ایجاد کنید که کدهای آن فقط در حین کامپایل برای دات نت 4.5 به خروجی اسمبلی نهایی اضافه شوند:
 #if NET45
public class ExtensionAttribute : Attribute { }
#endif


د) افزودن تنظیمات پس از build
در انتهای فایل csproj قسمت AfterBuild به صورت کامنت شده موجود است. آن‌را به نحو ذیل تغییر دهید:
  <Target Name="AfterBuild">
    <Message Text="Enter After Build TargetFrameworkVersion:$(TargetFrameworkVersion) Framework:$(Framework)" Importance="high" />
    <MSBuild Condition=" '$(Framework)' != 'NET45'" Projects="$(MSBuildProjectFile)" Properties="Framework=NET45" RunEachTargetSeparately="true" />
    <Message Text="Exiting After Build TargetFrameworkVersion:$(TargetFrameworkVersion) Framework:$(Framework)" Importance="high" />
  </Target>
این تنظیم سبب می‌شود تا کامپایل مخصوص دات نت 4.5 نیز به صورت خودکار فعال گردد و خروجی آن در مسیر bin\Debug\NET45 به صورت جداگانه‌ای قرار گیرد.



برای آزمایش بیشتر، فایل csproj نهایی را از اینجا می‌توانید دریافت کنید:
DualTargetFrameworks.zip
 
مطالب
پیاده سازی Basic Authentication در ASP.NET MVC
در سیستم‌های اتصال از راه دور به خصوص اتصال تلفن‌های همراه به وب سرویس، یکی از مواردی که مرتبا قبل از هر درخواستی بررسی میگردد، صحت نام کاربری و کلمه عبور درخواستی است. در این روش کاربر، الزامات امنیتی (نام کاربری و کلمه عبور) را در بدنه درخواست قرار داده و هر api باید قبل از انجام عملیات، صحت آن را بررسی کند. این مورد باعث میشود که کدها از حالت بهینه خارج شده و در سمت سرور، Api مربوطه باید صحت آن را بررسی کند. در این مقاله قصد داریم با پیاده سازی Basic Authentication این مشکل را رفع کرده تا به کد یکدست‌تری برسیم. Basic Authentication طبق گفته ویکی پدیا در واقع یک روش در درخواست‌های ارسالی http میباشد که با فراهم سازی الزامات امنیتی، درخواست خود را به سرور ارائه میدهد. در این روش نام کاربری و کلمه عبور به جای ارسال در بدنه درخواست، در هدر درخواست قرار گرفته و الزامات امنیتی به صورت رمزگذاری شده (عموما Base64) به سمت سرور ارسال می‌شوند.
این روش به دلیل عدم وجود ذخیره کوکی، ایجاد Session و دیگر مباحث امنیتی، جز ساده‌ترین روش‌های تشخیص هویت میباشد و همچنین مشخص است به دلیل رمزگذاری ساده‌ای چون Base64 و همچنین در صورت ارسال از طریق http نه https، حفاظت چندانی در ارسال الزامات امنیتی ندارند. ولی عموما برای گوشی‌های همراه تا حد زیادی قابل قبول است. همچنین در این روش مکانیزم logout فراهم نیست و تنها در طی یک درخواست پردازش اطلاعات انجام شده و بعد از آن هویت شما نابود خواهد شد.

برای ارسال اطلاعات در هدر درخواستی، به شیوه زیر اطلاعات را ارسال میکنیم:
Authorization: Basic  Username:Password
از آنجا که گفتیم اطلاعات بالا بهتر هست در قالب خاصی رمزگذاری (encoding) شوند، خط بالا به صورت نهایی، به شکل زیر خواهد بود:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
از آنجا که این کد اعتبارسنجی باید مرتبا بررسی شود، باعث میشود که اصل تک مسئولیتی اکشن زیر سوال رفته و کدها در ابتدا مرتبا تکرار شوند. این وظیفه را بر عهده Attribute ‌ها میگذاریم. در Attribute‌های موجود در سیستم دات نت، می‌توان به AuthorizationFilterAttribute اشاره کرد که امکانات Authorization را در اختیار ما قرار میدهد. کد زیر را برای آن وارد میکنیم:
public class BasicAuthetication:AuthorizationFilterAttribute
    {
        public override void OnAuthorization(HttpActionContext actionContext)
        {
            if (actionContext.Request.Headers.Authorization == null)
            {
                SayYouAreUnAuthorize(actionContext);
                return;
            }

            var param = actionContext.Request.Headers.Authorization.Parameter;
            var decodedCridential = Encoding.UTF8.GetString(Convert.FromBase64String(param));
            var splitedArray = decodedCridential.Split(':');
            if (splitedArray[0] == "username" && splitedArray[1] == "password")
            {
                var userId = 1;
                var genericIdentity=new GenericIdentity(userId.ToString());
                var genericPrincipal=new GenericPrincipal(genericIdentity,null);
                Thread.CurrentPrincipal = genericPrincipal;
                return;
            }
            SayYouAreUnAuthorize(actionContext);
        }

        private void SayYouAreUnAuthorize(HttpActionContext actionContext)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);            
        }
    }

  در اولین خط از کد بررسی میشود که خاصیت Authorization در هدر درخواست وجود دارد یا خیر؛ در صورت که وجود نداشته باشد متد SayYouAreUnAuthorize  صدا زده میشود. این متد وظیفه دارد، در صورتیکه صحت نام کاربری و کلمه عبور تایید نشد، خطای مورد نظر را که با کد 401 شناخته میشود، بازگرداند که در متد اصلی دو بار مورد استفاده قرار گرفته است. با استفاده از این بازگردانی و دادن پاسخ کاربر، دیگر api مورد نظر به مرحله اجرا نمیرسد.

در صورتیکه کد ادامه پیدا کند، مقدار تنظیم شده Authorization را دریافت کرده و در خط بعدی آن را از حالت انکود شده خارج میکنیم. از آنجاکه الگوی ارسالی ما به شکل username:password میباشد، با استفاده از متد الحاقی Split هر دو عبارت را جدا کرده و در خط بعد، صحت آن دو را مشخص میکنیم. از آنجا که این تکه کد تنها یک تست است، من آن را به شکل ساده و استاتیک نوشته‌ام. بدیهی است که در یک مثال واقعی، این تایید صحت توسط یک منبع داده انجام می‌شود. در صورتی که صحت آن تایید نشود مجددا متد SayYouAreUnAuthorize مورد استفاده قرار میگیرد. در غیر این صورت باید این تایید هویت را در ترد جاری تایید کرده و حفظ کنیم و حتی در صورت لزوم Id کاربر و نقش‌های آن را ذخیره کنیم تا اگر api مورد نظر بر اساس آن تصمیم میگیرد، آن‌ها را در اختیار داشته باشیم. به عنوان مثال ممکن است اطلاعات دریافتی پرداختی‌های یک کاربر باشد که به UserId نیاز است.

از آنجا که در این روش نه ذخیره session و نه کوکی و نه مورد دیگری داریم و قصد داریم تنها در ترد یا درخواست جاری این اعتبار را نگه داریم، میتوانیم از thread.CurrentPrincipal استفاده کنیم. این خصوصیت نوع GenericPrincipal را نیاز دارد که دو پارامتر   GenereicIdentity و آرایه‌ای از نوع رشته را دریافت میکنید. این آرایه‌ها متعلق به زمانی است که شما قصد دارید نقش‌های یک کاربر را ارسال کنید و شیء GenericIdentity شیءایی است که شامل اطلاعات Authorization بوده و هیچ وابستگی به windows Domain ندارد. این شیء از شما تنها یک نام را به صورت رشته میگیرد که میتوانیم از طریق آن UserId را پاس کنیم. برای دریافت آن نیز داریم:
var userId=int.Parse(Thread.CurrentPrincipal.Identity.Name);
حال برای api به کد زیر نیاز داریم:
[BasicAuthetication]
        public HttpResponseMessage GetPayments(int type)
        {
            var userId = int.Parse(Thread.CurrentPrincipal.Identity.Name);
            if (type == 1)
            {
                return Request.CreateResponse(HttpStatusCode.OK, new
                {
                    Id = 4,
                    Price = 20000,
                    userId=userId
                });
            }
          
                return Request.CreateResponse(HttpStatusCode.OK, new
                {
                    Id = 3,
                    Price = 10000,
                    userId = userId
                });
            
        }

در api بالا attribute مدنظر بر روی آن اعمال شده و تنها مقادیر ورودی این api همان پارامترهای درخواست اصلی میباشد و ویژگی BasicAuthentication کدهای اعتبارسنجی را از کد اصلی دور نگه داشته است و api اصلی هیچگاه متوجه قضیه نمی‌شود و تنها کار اصلی خود را انجام می‌دهد. همچین در اینجا از طریق کلاس thread به UserId دسترسی داریم.

حال تنها لازم است کلاینت، قسمت هدر را پر کرده و آن را به سمت سرور ارسال کند. در اینجا من از نرم افزار I'm only resting استفاده میکنیم تا درخواست رسیده را در WebApi تست نمایم.