مطالب
استفاده یکپارچه از NUnit در VS.NET بدون نیاز به افزونه‌ها

برای استفاده ساده‌تر از ابزارهای unit testing در ویژوال استودیو افزونه‌های زیادی وجود دارند، از ری شارپر تا CodeRush  تا حتی امکانات نسخه‌ی کامل VS.NET که با MSTest یکپارچه است. اما اگر نخواهیم از MSTest استفاده کنیم و همچنین افزونه‌ها را هم بخواهیم حذف کنیم (مثلا از نسخه‌ی رایگان express استفاده کنیم)، چطور؟
برای حل این مشکل چندین روش وجود دارد. یا می‌شود از test runner این‌ها استفاده کرد که اصلا نیازی به IDE ندارند و مستقل است؛ یا می‌توان به صورت زیر هم عمل کرد:
به خواص پروژه در VS.NET مراجعه کنید. برگه‌ی Build events را باز کنید. در اینجا می‌خواهیم post-build event را مقدار دهی کنیم. به این معنا که پس از هر build موفق، لطفا این دستورات خط فرمان را اجرا کن.
NUnit به همراه test runner خط فرمان هم ارائه می‌شود و نام آن nunit-console.exe است. اگر به محل نصب آن مراجعه کنید، عموما در آدرس C:\Program Files\NUnit xyz\bin\nunit-console.exe قرار دارد. برای استفاده از آن تنها کافی است تنظیم زیر صورت گیرد:

c:\path\nunit-console.exe /nologo $(TargetPath)

TargetPath به صورت خودکار با نام اسمبلی جاری پروژه در زمان اجرا جایگزین می‌شود.
اکنون پس از هر Build، به صورت خودکار nunit-console.exe اجرا شده، اسمبلی برنامه که حاوی آزمون‌های واحد است به آن ارسال گردیده و سپس خروجی کار در output window نمایش داده می‌شود. اگر خطایی هم رخ داده باشد در قسمت errors قابل مشاهده خواهد بود.
در اینجا حتی بجای برنامه کنسول یاده شده می‌توان از برنامه nunit.exe هم استفاده کرد. در این حالت GUI اصلی پس از هر Build نمایش داده می‌شود:

c:\path\nunit.exe $(TargetPath)


چند نکته:
1- برنامه nunit-console.exe چون در حال حاضر برای دات نت 2 کامپایل شده امکان بارگذاری dll های دات نت 4 را ندارد. به همین منظور فایل nunit-console.exe.config را باز کرده و تنظیمات زیر را به آن اعمال کنید:

<configuration>  
<startup>
<supportedRuntime version="v4.0.30319" />
</startup>

و همچنین:

<runtime>  
<loadFromRemoteSources enabled="true" />

2- خروجی نتایج اجرای آزمون‌ها را به صورت XML هم می‌توان ذخیره کرد. مثلا:

c:\path\nunit-console.exe /xml:$(ProjectName)-tests.xml /nologo $(TargetPath)



3- از فایل xml ذکر شده می‌توان گزارشات زیبایی تهیه کرد. برای مثال:
Generating Report for NUnit
NUnit2Report Task


جهت مطالعه بیشتر:
Setting up Visual C#2010 Express with NUnit
Use Visual Studio's Post-Build Events to Automate Unit Testing Running
3 Ways to Run NUnit From Visual Studio


مطالب
آشنایی با CLR: قسمت پانزدهم
در قسمت قبلی نحوه‌ی ساخت اسمبلی را یاد گرفتیم. ولی ممکن است که بخواهید اسمبلی را از طریق Assembly Linker یا AL.exe ایجاد کنید. این روش موقعی سودمند است که بخواهید یک اسمبلی از ماژولها از کامپایلرهای مختلف را ایجاد کنید یا اینکه کامپایلر شما مانند کامپایلر سی شارپ از دستور یا سوئیچی مشابه addmodule استفاده نمی‌کند. یا حتی اینکه در زمان کامپایل هنوز اطلاعاتی از نیازمندی‌های اسمبلی‌ها ندارید و به بعد موکول می‌کنید. از AL همچنین می‌توانید در زمینه‌ی ساخت اسمبلی‌های فقط ریسورس هم استفاده کنید که می‌تواند جهت انجام localization به کار رود. AL می‌تواند یک فایل dll یا exe تولید کند که شامل یک فایل manifest بوده که اشاره به ماژول‌های تشکیل دهنده‌اش دارد.

نحوه‌ی ساخت اسمبلی با استفاده از ابزار AL :
csc /t:module RUT.cs
csc /t:module FUT.cs
al /out: MultiFileLibrary.dll /t:library FUT.netmodule RUT.netmodule
تصویر زیر نتیجه‌ی دستور بالاست:


در این مثال ما دو ماژول جدا به نام‌های RUT.netmodule و FUT.netmodule را در یک اسمبلی ایجاد کرده‌ایم. داخل این اسمبلی‌ها جدول متادیتا یا بخش IL از ماژول‌ها به چشم نمی‌خورد. به این معنی که کد IL و جداول مربوطه به آن، هر کدام داخل ماژول یا فایل خودش بوده و در اسمبلی کدی وجود ندارد و تنها یک جدول مانیفست جهت شناسایی ماژول‌هایش دارد. شکل بالا گویای اطلاعات داخلی اسمبلی است که می‌توانید با تصویری که در قسمت قبلی درج شده مقایسه کنید.
تصویر قسمت قبلی جهت مقایسه:


در این حالت سه فایل تشکیل شده است که یکی از آن‌ها MultiFileLibrary.dll ، FUT.netmodule و RUT.netmodule است و در استفاده از این ابزار هیچ راهی برای داشتن یک تک فایل وجود ندارد.
این ابزار همچنین می‌تواند فایل‌های CUI ,GUI و ... را با سوئیچ‌های زیر هم تولید کند:
/t[arget]:exe, /t[arget]:winexe, or /t[arget]:appcontainerexe

 البته اینکار تا حدی غیر معمول است که یک فایل exe بخواهد کدهای IL ابتدایی را از ماژول‌های جداگانه بخواند. در صورتیکه چنین قصدی را دارید، باید یکی از ماژول‌ها را به عنوان مدخل ورودی Main تعریف کنید تا برنامه از آنجا آغاز به کار کند. نحوه‌ی ساخت یک فایل اجرایی و معرفی ماژول Main به شکل زیر است:
csc /t:module /r:MultiFileLibrary.dll Program.cs
al /out:Program.exe /t:exe /main:Program.Main Program.netmodule
در اولین خط مانند سابق فایل netmodule تهیه می‌گردد و در خط دوم، داخل اسمبلی قرار می‌گیرد. ولی به علت استفاده از سوئیچ main یک تابع عمومی global به نام EntryPoint__ هم تعریف می‌گردد که کد IL آن به شرح زیر است:
.method privatescope static void __EntryPoint$PST06000001() cil managed
{
.entrypoint
// Code size 8 (0x8)
.maxstack 8
IL_0000: tail.
IL_0002: call void [.module 'Program.netmodule']Program::Main()
IL_0007: ret
} // end of method 'Global Functions'::__EntryPoint

کد بالا یک کد ساده است که می‌گوید داخل فایل Program.netmodule در نوع Program متدی وجود دارد به نام Main که محل آغازین برنامه است. البته این روش ایجاد فایل‌های EXE، بدین شکل توصیه چندانی نمی‌شود و ذکر این مطلب فقط اطلاع از وجود چنین قابلیتی بود.
مطالب
اجرای برنامه‌های ASP.NET توسط Mono در Ubuntu
در ادامه مباحث بررسی اجرای برنامه‌های دات نت بر روی لینوکس، قصد داریم برنامه‌های ASP.NET را به کمک Mono 3.0 و یک وب سرور لینوکسی، بر روی Ubuntu اجرا کنیم.


پیشنیازها
دو پروژه خالی ASP.NET Web forms و ASP.NET MVC را در VS.NET تحت ویندوز ایجاد نمائید. آن‌ها را یکبار کامپایل کرده و اجرا کنید. سپس فایل‌‌های آن‌ها را به ubuntu منتقل کنید (پوشه‌های bin پروژه‌ها فراموش نشوند؛ خصوصا نگارش MVC که به همراه یک سری کتابخانه جانبی است).
برای انتقال فایل‌ها به لینوکس، اگر از VMWare workstation برای اجرا و آزمایش Ubuntu استفاده می‌کنید، کپی و paste مستقیم فایل‌ها از ویندوز به درون ماشین مجازی لینوکس پشتیبانی می‌شود.


نصب وب سرور آزمایشی مونو یا XSP
اگر نیاز به یک وب سرور آزمایشی، چیزی شبیه به وب سرور توکار VS.NET داشتید، پروژه XSP جهت این نوع آزمایشات ایجاد شده است.
پس از نصب آن (که به همراه همان بسته PPA قسمت قبل، هم اکنون بر روی سیستم شما نصب است)، در ترمینال لینوکس، با استفاده از دستور cd به ریشه وب سایت خود وارد شوید، سپس دستور xsp4 را اجرا کنید تا وب سرور xsp4 مشغول هاست سایت شما شود (برای اجرا در مسیر  /opt/bin/xsp4 نصب شده است).


اجرای برنامه ASP.NET Web forms 4 توسط XSP
بدون هیچ مشکل خاصی در همان ابتدای کار اجرا شد (البته باید دقت داشت که لینوکس به کوچکی و بزرگی حروف حساس است. یعنی حتما باید Default.aspx وارد شود و نه default.aspx):



اجرای برنامه ASP.NET MVC 4 توسط XSP
اجرا نشد! پیام می‌دهد که
 "Missing method System.Web.Security.FormsAuthentication::get_IsEnabled() in assembly System.Web.dll
و یا
Compiler Error Message: CS1703: An assembly with the same identity `mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' 
has already been imported. Consider removing one of the references
علت اینجا است که XSP4 همراه با نسخه PPA، قدیمی است. بنابراین باید نسخه اصلی را از مخزن کد آن دریافت و کامپایل کنیم. پیشنیازهای اینکار مانند Git و Mono، در قسمت قبل دریافت شدند. سپس فرامین ذیل را در خط فرمان لینوکس اجرا کنید:
 git clone git://github.com/mono/xsp.git
cd xsp
./autogen.sh --prefix=/opt
make
sudo make install
پس از کامپایل، اگر این نگارش جدید را اجرا کنید، به خطای ذیل خواهید رسید:
 System.IO.FileNotFoundException: Could not load file or assembly XSP, Version=3.0.0.0
برای رفع این مشکل باید اینبار وب سرور جدید را با فرمان sudo یا دسترسی مدیریتی اجرا کنید تا مشکل برطرف شود.
البته من سورس دریافت شده را در خود monodevelop کامپایل کردم (فایل sln آن‌را در monodevelop باز کرده و پروژه را build کنید). در این حالت دو فایل Mono.WebServer.dll و Mono.WebServer.XSP.exe در پوشه  xsp/src/Mono.WebServer.XSP/bin/Debug ظاهر می‌شوند.
یکی دیگر از دلایل ظاهر شدن خطای فوق، نیاز به نصب این دو فایل در GAC است که به نحو زیر قابل انجام می‌باشد:
 cd xsp/src/Mono.WebServer.XSP/bin/Debug
sudo gacutil -i Mono.WebServer.XSP.exe
sudo gacutil -i Mono.WebServer.dll
بعد این دو فایل dll و exe را در پوشه برنامه MVC خود کپی کنید و سپس دستور ذیل را اجرا نمائید:
 cd myMvcAppPath
sudo mono Mono.WebServer.XSP.exe
اینبار وب سرور جدید، روی پورت 9000 شروع به کار می‌کند. اکنون اگر در فایرفاکس آدرس http://localhost:9000 را باز کنید، برنامه اجرا شده اما به خطای ذیل خواهید رسید:
 CS0234: The type or namespace name `Helpers' does not exist in the namespace `System.Web'.
Are you missing an assembly reference?
برای رفع این مشکل باید اندکی فایل web.config برنامه را ویرایش کرد:
  <system.web> 
    <compilation debug="true" targetFramework="4.0">
      <assemblies>
        <add assembly="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />        
        <add assembly="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />        
      </assemblies>
    </compilation>
سعی بعدی ... اجرا نشد! با هر بار refresh صفحه یک خطای جدید نمایش می‌داد که ... Type خاصی را نمی‌تواند بارگذاری کند (به همراه نام اسمبلی مربوطه). برای رفع این مشکل dllهای ذیل را از پوشه bin پروژه MVC خود که از ویندوز به لینوکس کپی کرده‌اید، حذف کنید:
Microsoft.Web.Infrastructure.dll
System.Net.Http.dll
System.Net.Http.Formatting.dll
System.Web.Http.dll
System.Web.Http.WebHost.dll
این فایل‌ها توسط تیم Mono به صورت مستقل پیاده سازی شده‌اند و نیازی نیست تا از ویندوز به لینوکس کپی شوند.
بعد از حذف این فایل‌های اضافی، برنامه ASP.NET MVC نیز اجرا شد:



چند نکته تکمیلی
- نحوه تشخیص موجود بودن یک DLL خاص، در نگارش جاری Mono نصب شده:
 $ gacutil -l Microsoft.Web.Infrastructure
The following assemblies are installed into the GAC:
Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
Number of items = 1
- اگر می‌خواهید مطمئن شوید که تمام اسمبلی‌های موجود در GAC درست نصب شده‌اند یا خیر، فرمان ذیل را اجرا کنید:
cd /opt/lib/mono/gac # assuming this is your main gac
sudo find . */*/*.dll -exec gacutil -i '{}' \;
- در نسخه لینوکسی System.Web ممکن است یک سری از فضاهای نام هنوز موجود نباشند. لیست آن‌ها را در این آدرس می‌توانید مشاهده کنید:
مطالب
پیاده سازی یک تامین کننده MySQL برای ASP.NET Identity
در این مقاله جایگزینی پیاده سازی پیش فرض ASP.NET Identity را بررسی می‌کنیم. در ادامه خواهید خواند:

  • جزئیات نحوه پیاده سازی یک Storage Provider برای ASP.NET Identity
  • تشریح اینترفیس هایی که باید پیاده سازی شوند، و نحوه استفاده از آنها در ASP.NET Identity
  • ایجاد یک دیتابیس MySQL روی Windows Azure
  • نحوه استفاده از یک ابزار کلاینت (MySQL Workbench) برای مدیریت دیتابیس مذکور
  • نحوه جایگزینی پیاده سازی سفارشی با نسخه پیش فرض در یک اپلیکیشن ASP.NET MVC
در انتهای این مقاله یک اپلیکیشن ASP.NET MVC خواهیم داشت که از ASP.NET Identity و تامین کننده سفارشی جدید استفاده می‌کند. دیتابیس اپلیکیشن MySQL خواهد بود و روی Windows Azure میزبانی می‌شود. سورس کد کامل این مثال را هم می‌توانید از این لینک دریافت کنید.


پیاده سازی یک Storage Provider سفارشی برای ASP.NET Identity

ASP.NET Identity سیستم توسعه پذیری است که می‌توانید بخش‌های مختلف آن را جایگزین کنید.در این سیستم بناهای سطح بالایی مانند Managers و Stores وجود دارند.
Managers کلاس‌های سطح بالایی هستند که توسعه دهندگان از آنها برای اجرای عملیات مختلف روی ASP.NET Identity استفاده می‌کنند. مدیریت کننده‌های موجود عبارتند از UserManager و RoleManager. کلاس UserManager برای اجرای عملیات مختلف روی کاربران استفاده می‌شود، مثلا ایجاد کاربر جدید یا حذف آنها. کلاس RoleManager هم برای اجرای عملیات مختلف روی نقش‌ها استفاده می‌شود.

Stores کلاس‌های سطح پایین‌تری هستند که جزئیات پیاده سازی را در بر می‌گیرند، مثلا اینکه موجودیت‌های کاربران و نقش‌ها چگونه باید ذخیره و بازیابی شوند. این کلاس‌ها با مکانیزم ذخیره و بازیابی تلفیق شده اند. مثلا Microsoft.AspNet.Identity.EntityFramework کلاسی با نام UserStore دارد که برای ذخیره و بازیابی User‌ها و داده‌های مربوطه توسط EntityFramework استفاده می‌شود.

Managers از Stores تفکیک شده اند و هیچ وابستگی ای به یکدیگر ندارند. این تفکیک بدین منظور انجام شده که بتوانید مکانیزم ذخیره و بازیابی را جایگزین کنید، بدون اینکه اپلیکیشن شما از کار بیافتد یا نیاز به توسعه بیشتر داشته باشد. کلاس‌های Manager می‌توانند با هر Store ای ارتباط برقرار کنند. از آنجا که شما از API‌های سطح بالای UserManager برای انجام عملیات CRUD روی کاربران استفاده می‌کنید، اگر UserStore را با پیاده سازی دیگری جایگزین کنید، مثلا AzureTable Storage یا MySql، نیازی به بازنویسی اپلیکیشن نیست.

در مثال جاری پیاده سازی پیش فرض Entity Framework را با یک  تامین کننده MySQL جایگزین می‌کنیم.

پیاده سازی کلاس‌های Storage
برای پیاده سازی تامین کننده‌های سفارشی، باید کلاس هایی را پیاده سازی کنید که همتای آنها در Microsoft.AspNet.Identity.EntityFramework وجود دارند:
  • <UserStore<TUser
  • IdentityUser
  • <RoleStore<TRole
  • IdentityRole
پیاده سازی پیش فرض Entity Framework را در تصاویر زیر مشاهده می‌کنید.
Users

Roles

در مخزن پیش فرض ASP.NET Identity EntityFramework کلاس‌های بیشتری برای موجودیت‌ها مشاهده می‌کنید.

  • IdentityUserClaim
  • IdentityUserLogin
  • IdentityUserRole
همانطور که از نام این کلاس‌ها مشخص است، اختیارات، نقش‌ها و اطلاعات ورود کاربران توسط این کلاس‌ها معرفی می‌شوند. در مثال جاری این کلاس‌ها را پیاده سازی نخواهیم کرد، چرا که بارگذاری اینگونه رکوردها از دیتابیس به حافظه برای انجام عملیات پایه (مانند افزودن و حذف اختیارات کاربران) سنگین است. در عوض کلاس‌های backend store اینگونه عملیات را بصورت مستقیم روی دیتابیس اجرا خواهند کرد. بعنوان نمونه متد ()UserStore.GetClaimsAsync را در نظر بگیرید. این متد به نوبه خود متد (userClaimTable.FindByUserId(user.Id را فراخوانی می‌کند که یک کوئری روی جدول مربوطه اجرا می‌کند و لیستی از اختیارات کاربر را بر می‌گرداند.
public Task<IList<Claim>> GetClaimsAsync(IdentityUser user)
{
    ClaimsIdentity identity = userClaimsTable.FindByUserId(user.Id);
    return Task.FromResult<IList<Claim>>(identity.Claims.ToList());
}
برای پیاده سازی یک تامین کننده سفارشی MySQL مراحل زیر را دنبال کنید.
1. کلاس کاربر را ایجاد کنید، که اینترفیس IUser را پیاده سازی می‌کند.
public class IdentityUser : IUser
{
    public IdentityUser(){...}

    public IdentityUser(string userName) (){...}

    public string Id { get; set; }

    public string UserName { get; set; }

    public string PasswordHash { get; set; }

    public string SecurityStamp { get; set; }
}
2. کلاس User Store را ایجاد کنید، که اینترفیس‌های IUserStore, IUserClaimStore, IUserLoginStore, IUserRoleStore و IUserPasswordStore را پیاده سازی می‌کند. توجه کنید که تنها اینترفیس IUserStore را باید پیاده سازی کنید، مگر آنکه بخواهید از امکاناتی که دیگر اینترفیس‌ها ارائه می‌کنند هم استفاده کنید.
public class UserStore : IUserStore<IdentityUser>,
                         IUserClaimStore<IdentityUser>,
                         IUserLoginStore<IdentityUser>,
                         IUserRoleStore<IdentityUser>,
                         IUserPasswordStore<IdentityUser>
{
    public UserStore(){...}

    public Task CreateAsync(IdentityUser user){...}

    public Task<IdentityUser> FindByIdAsync(string userId){...}   
...
}
3. کلاس Role را ایجاد کنید که اینترفیس IRole را پیاده سازی می‌کند.
public class IdentityRole : IRole
{
    public IdentityRole(){...}

    public IdentityRole(string roleName) (){...}

    public string Id { get; set; }

    public string Name { get; set; }
}
4. کلاس Role Store را ایجاد کنید که اینترفیس IRoleStore را پیاده سازی می‌کند. توجه داشته باشید که پیاده سازی این مخزن اختیاری است و در صورتی لازم است که بخواهید از نقش‌ها در سیستم خود استفاده کنید.
public class RoleStore : IRoleStore<IdentityRole>                        
{
    public RoleStore(){...}

    public Task CreateAsync(IdentityRole role){...}

    public Task<IdentityRole> FindByIdAsync(string roleId){...}   
....
}
کلاس‌های بیشتری هم وجود دارند که مختص پیاده سازی مثال جاری هستند.
  • MySQLDatabase: این کلاس اتصال دیتابیس MySql و کوئری‌ها را کپسوله می‌کند. کلاس‌های UserStore و RoleStore توسط نمونه ای از این کلاس وهله سازی می‌شوند.
  • RoleTable: این کلاس جدول Roles و عملیات CRUD مربوط به آن را کپسوله می‌کند.
  • UserClaimsTable: این کلاس جدول UserClaims و عملیات CRUD مربوط به آن را کپسوله می‌کند.
  • UserLoginsTable: این کلاس جدول UserLogins و عملیات CRUD مربوط به آن را کپسوله می‌کند.
  • UserRolesTable: این کلاس جدول UserRoles و عملیات CRUD مربوطه به آن را کپسوله می‌کند.
  • UserTable: این کلاس جدول Users و عملیات CRUD مربوط به آن را کپسوله می‌کند.

ایجاد یک دیتابیس MySQL روی Windows Azure

1. به پورتال مدیریتی Windows Azure وارد شوید.
2. در پایین صفحه روی NEW+ کلیک کنید و گزینه STORE را انتخاب نمایید.

در ویزارد Choose Add-on به سمت پایین اسکرول کنید و گزینه ClearDB MySQL Database را انتخاب کنید. سپس به مرحله بعد بروید.

4. راهکار Free بصورت پیش فرض انتخاب شده، همین گزینه را انتخاب کنید و نام دیتابیس را به IdentityMySQLDatabase تغییر دهید. نزدیک‌ترین ناحیه (region) به خود را انتخاب کنید و به مرحله بعد بروید.

5. روی علامت checkmark کلیک کنید تا دیتابیس شما ایجاد شود. پس از آنکه دیتابیس شما ساخته شد می‌توانید از قسمت ADD-ONS آن را مدیریت کنید.

6. همانطور که در تصویر بالا می‌بینید، می‌توانید اطلاعات اتصال دیتابیس (connection info) را از پایین صفحه دریافت کنید.

7. اطلاعات اتصال را با کلیک کردن روی دکمه مجاور کپی کنید تا بعدا در اپلیکیشن MVC خود از آن استفاده کنیم.


ایجاد جداول ASP.NET Identity در یک دیتابیس MySQL

ابتدا ابزار MySQL Workbench را نصب کنید.
1. ابزار مذکور را از اینجا دانلود کنید.
2. هنگام نصب، گزینه Setup Type: Custom را انتخاب کنید.
3. در قسمت انتخاب قابلیت ها، گزینه‌های Applications و MySQLWorkbench را انتخاب کنید و مراحل نصب را به اتمام برسانید.
4. اپلیکیشن را اجرا کرده و روی MySQLConnection کلیک کنید تا رشته اتصال جدیدی تعریف کنید. رشته اتصالی که در مراحل قبل از Azure MySQL Database کپی کردید را اینجا استفاده کنید. بعنوان مثال:
 Connection Name: AzureDB; Host Name: us-cdbr-azure-west-b.cleardb.com; Username: <username>; Password: <password>; Default Schema: IdentityMySQLDatabase 
5. پس از برقراری ارتباط با دیتابیس، یک برگ Query جدید باز کنید. فرامین زیر را برای ایجاد جداول مورد نیاز کپی کنید.
CREATE TABLE `IdentityMySQLDatabase`.`users` (
  `Id` VARCHAR(45) NOT NULL,
  `UserName` VARCHAR(45) NULL,
  `PasswordHash` VARCHAR(100) NULL,
  `SecurityStamp` VARCHAR(45) NULL,
  PRIMARY KEY (`id`));

CREATE TABLE `IdentityMySQLDatabase`.`roles` (
  `Id` VARCHAR(45) NOT NULL,
  `Name` VARCHAR(45) NULL,
  PRIMARY KEY (`Id`));

CREATE TABLE `IdentityMySQLDatabase`.`userclaims` (
  `Id` INT NOT NULL AUTO_INCREMENT,
  `UserId` VARCHAR(45) NULL,
  `ClaimType` VARCHAR(100) NULL,
  `ClaimValue` VARCHAR(100) NULL,
  PRIMARY KEY (`Id`),
  FOREIGN KEY (`UserId`)
    REFERENCES `IdentityMySQLDatabase`.`users` (`Id`) on delete cascade);

CREATE TABLE `IdentityMySQLDatabase`.`userlogins` (
  `UserId` VARCHAR(45) NOT NULL,
  `ProviderKey` VARCHAR(100) NULL,
  `LoginProvider` VARCHAR(100) NULL,
  FOREIGN KEY (`UserId`)
    REFERENCES `IdentityMySQLDatabase`.`users` (`Id`) on delete cascade);

CREATE TABLE `IdentityMySQLDatabase`.`userroles` (
  `UserId` VARCHAR(45) NOT NULL,
  `RoleId` VARCHAR(45) NOT NULL,
  PRIMARY KEY (`UserId`, `RoleId`),
  FOREIGN KEY (`UserId`)
    REFERENCES `IdentityMySQLDatabase`.`users` (`Id`) 
on delete cascade
on update cascade,
  FOREIGN KEY (`RoleId`)
    REFERENCES `IdentityMySQLDatabase`.`roles` (`Id`)
on delete cascade
on update cascade);
6. حالا تمام جداول لازم برای ASP.NET Identity را در اختیار دارید، دیتابیس ما MySQL است و روی Windows Azure میزبانی شده.


ایجاد یک اپلیکیشن ASP.NET MVC و پیکربندی آن برای استفاده از MySQL Provider

2. در گوشه سمت راست پایین صفحه روی دکمه Download Zip کلیک کنید تا کل پروژه را دریافت کنید.
3. محتوای فایل دریافتی را در یک پوشه محلی استخراج کنید.
4. پروژه AspNet.Identity.MySQL را باز کرده و آن را کامپایل (build) کنید.
5. روی نام پروژه کلیک راست کنید و گزینه Add, New Project را انتخاب نمایید. پروژه جدیدی از نوع ASP.NET Web Application بسازید و نام آن را به IdentityMySQLDemo تغییر دهید.

6. در پنجره New ASP.NET Project قالب MVC را انتخاب کنید و تنظیمات پیش فرض را بپذیرید.

7. در پنجره Solution Explorer روی پروژه IdentityMySQLDemo کلیک راست کرده و Manage NuGet Packages را انتخاب کنید. در قسمت جستجوی دیالوگ باز شده عبارت "Identity.EntityFramework" را وارد کنید. در لیست نتایج این پکیج را انتخاب کرده و آن را حذف (Uninstall) کنید. پیغامی مبنی بر حذف وابستگی‌ها باید دریافت کنید که مربوط به پکیج EntityFramework است، گزینه Yes را انتخاب کنید. از آنجا که کاری با پیاده سازی فرض نخواهیم داشت، این پکیج‌ها را حذف می‌کنیم.

8. روی پروژه IdentityMySQLDemo کلیک راست کرده و Add, Reference, Solution, Projects را انتخاب کنید. در دیالوگ باز شده پروژه AspNet.Identity.MySQL را انتخاب کرده و OK کنید.

9. در پروژه IdentityMySQLDemo پوشه Models را پیدا کرده و کلاس IdentityModels.cs را حذف کنید.

10. در پروژه IdentityMySQLDemo تمام ارجاعات ";using Microsoft.AspNet.Identity.EntityFramework" را با ";using AspNet.Identity.MySQL" جایگزین کنید.

11. در پروژه IdentityMySQLDemo تمام ارجاعات به کلاس "ApplicationUser" را با "IdentityUser" جایگزین کنید.

12. کنترلر Account را باز کنید و متد سازنده آنرا مطابق لیست زیر تغییر دهید.

public AccountController() : this(new UserManager<IdentityUser>(new UserStore(new MySQLDatabase())))
{

}

13. فایل web.config را باز کنید و رشته اتصال DefaultConnection را مطابق لیست زیر تغییر دهید.

<add name="DefaultConnection" connectionString="Database=IdentityMySQLDatabase;Data Source=<DataSource>;User Id=<UserID>;Password=<Password>" providerName="MySql.Data.MySqlClient" />

مقادیر <DataSource>, <UserId> و <Password> را با اطلاعات دیتابیس خود جایگزین کنید.


اجرای اپلیکیشن و اتصال به دیتابیس MySQL

1. روی پروژه IdentityMySQLDemo کلیک راست کرده و Set as Startup Project را انتخاب کنید.
2. اپلیکیشن را با Ctrl + F5 کامپایل و اجرا کنید.
3. در بالای صفحه روی Register کلیک کنید.
4. حساب کاربری جدیدی بسازید.

5. در این مرحله کاربر جدید باید ایجاد شده و وارد سایت شود.

6. به ابزار MySQL Workbench بروید و محتوای جداول IdentityMySQLDatabase را بررسی کنید. جدول users را باز کنید و اطلاعات کاربر جدید را بررسی نمایید.

برای ساده نگاه داشتن این مقاله از بررسی تمام کدهای لازم خودداری شده، اما اگر مراحل را دنبال کنید و سورس کد نمونه را دریافت و بررسی کنید خواهید دید که پیاده سازی تامین کنندگان سفارشی برای ASP.NET Identity کار نسبتا ساده ای است.

مطالب
استفاده از LINQ to XML جهت خواندن فیدهای RSS

مثال زیر را به عنوانی نمونه‌ای از کاربرد LINQ to XML برای خواندن فیدهای RSS که اساسا به فرمت XML هستند می‌توان ارائه داد.
ابتدا کد کامل مثال را در نظر بگیرید:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;

namespace LinqToRSS
{
public static class LanguageExtender
{
public static string SafeValue(this XElement input)
{
return (input == null) ? string.Empty : input.Value;
}

public static DateTime SafeDateValue(this XElement input)
{
return (input == null) ? DateTime.MinValue : DateTime.Parse(input.Value);
}
}

public class RssEntry
{
public string Title { set; get; }
public string Description { set; get; }
public string Link { set; get; }
public DateTime PublicationDate { set; get; }
public string Author { set; get; }
public string BlogName { set; get; }
public string BlogAddress { set; get; }
}

public class Rss
{
static XElement selectDate(XElement date1, XElement date2)
{
return date1 ?? date2;
}

public static List<RssEntry> GetEntries(string feedUrl)
{
//applying namespace in an XElement
XName xn = XName.Get("{http://purl.org/dc/elements/1.1/}creator");//{namespace}root
XName xn2 = XName.Get("{http://purl.org/dc/elements/1.1/}date");

var feed = XDocument.Load(feedUrl);
if (feed.Root == null) return null;

var items = feed.Root.Element("channel").Elements("item");
var feedQuery =
from item in items
select new RssEntry
{
Title = item.Element("title").SafeValue(),
Description = item.Element("description").SafeValue(),
Link = item.Element("link").SafeValue(),
PublicationDate =
selectDate(item.Element(xn2), item.Element("pubDate")).SafeDateValue(),
Author = item.Element(xn).SafeValue(),
BlogName = item.Parent.Element("title").SafeValue(),
BlogAddress = item.Parent.Element("link").SafeValue()
};

return feedQuery.ToList();
}
}

class Program
{
static void Main(string[] args)
{
List<RssEntry> entries = Rss.GetEntries("http://weblogs.asp.net/aspnet-team/rss.aspx");
if (entries != null)
foreach (var item in entries)
Console.WriteLine(item.Title);

Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}

توضیحات:
1- در این مثال فقط جهت سهولت بیان آن در یک صفحه، تمامی کلاس‌های تعریف شده در یک فایل آورده شدند. این روش صحیح نیست و باید به ازای هر کلاس یک فایل جدا در نظر گرفته شود.
2- کلاس LanguageExtender از قابلیت extension methods سی شارپ 3 استفاده می‌کند. به این صورت کلاس XElement دات نت بسط یافته و دو متد به آن اضافه می‌شود که به سادگی در کدهای خود می‌توان از آن‌ها استفاده کرد. هدف آن هم بررسی نال بودن یک آیتم دریافتی و ارائه‌ی حاصلی امن برای این مورد است.
3- کلاس RssEntry به جهت استفاده در خروجی کوئری LINQ تعریف شد. می‌خواهیم خروجی نهایی، یک لیست جنریک از نوع RssEntry باشد.
4- متد اصلی برنامه، GetEntries است. این متد آدرس اینترنتی یک فید را دریافت کرده و پس از آنالیز، آن‌را به صورت یک لیست بر می‌گرداند.

<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://weblogs.asp.net/utility/FeedStylesheets/rss.xsl" media="screen"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
<channel>
<title>Latest Microsoft Blogs</title>
<link>http://weblogs.asp.net/aspnet-team/default.aspx</link>
<description />
<dc:language>en</dc:language>
<generator>CommunityServer 2007 SP1 (Build: 20510.895)</generator>
<item>
<title>Comments on my recent benchmarks.</title>
<link>http://misfitgeek.com/blog/aspnet/comments-on-my-recent-benchmarks/</link>
<pubDate>Mon, 10 Aug 2009 23:33:59 GMT</pubDate>
<guid isPermaLink="false">c06e2b9d-981a-45b4-a55f-ab0d8bbfdc1c:7166225</guid>
<dc:creator>Misfit Geek: msft</dc:creator>
<slash:comments>0</slash:comments>
<wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://weblogs.asp.net/aspnet-team/rsscomments.aspx?PostID=7166225</wfw:commentRss>
<comments>http://misfitgeek.com/blog/aspnet/comments-on-my-recent-benchmarks/#comments</comments>
<description>Overall I’ve been pretty impressed ...</description>
<category domain="http://weblogs.asp.net/aspnet-team/archive/tags/ASP.NET/default.aspx">ASP.NET</category>
</item>
</channel>
</rss>
برای نمونه خروجی یک فید می‌تواند به صورت فوق باشد. آیتم‌های آن به صورت قابل بیان است:
var items = feed.Root.Element("channel").Elements("item");
و نکته مهمی که اینجا وجود دارد، اعمال فضاهای نام بکار رفته در این فایل xml پیشرفته می‌باشد. برای اعمال فضاهای نام به یکی از دو روش زیر می‌توان عمل کرد:

XName.Get("{mynamespace}root");
//or
XName.Get("root", "mynamespace");

نظرات مطالب
ASP.NET MVC #18
ممنونم بابت معرفی این مورد.
این مهم در این سری آموزشی‌های سایت نیست. چون زمان نگارش اصلا SimpleMembership وجود نداشته.
به‌طور خلاصه توضیحاتی بدم:
استفاده از این پروایدر به صورت توکار فقط در تمپلیت Internet Application استفاده شده. خاصیت این سیستم این‌است که سختی آن سیستم قدیمی که از ASP.NET 2.0 باب شد را ندارد و واقعا Simple است.
موقع استفاده در web.config اصلی برنامه در زیر گره‌ی system.web موارد زیر را اضافه کنید:
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
  <providers>
    <clear/>
    <add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
  </providers>
</roleManager>
<membership defaultProvider="SimpleMembershipProvider">
  <providers>
    <clear/>
    <add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
  </providers>
</membership>
 SimpleMembership به شما اجازه میدهد با برنامه‌ی خودتان در یک پایگاه داده ذخیره کنید. و در واقع یکپارچه کنید. برای این کار در AcountModel.cs و در کلاس UserProfile ویژگی‌های دلخواه دیگری را اضافه کنید ( مثلا ایمیل، آدرس یا وب‌سایت). پس از آن باید در کلاس مدل Register فیلدهای مربوطه را اضافه کنید و همچنین data anotationهای مورد نیاز را اضافه کنید. این تغییرات را در رجیستر View هم انجام داده تا کاربر جاهای خالی را پر کند.
حال در کنترلر Account در متد Register با خاصیت HttpPost باید WebSecurity.CreateUserAndAccount را به‌درستی مقدار دهی کرد. چندین overload دارد. در overloadی که دارای قسمت object است میتوانیم از یک Anonymus Type استفاده کرده و اطلاعات مورد نیازمان را ثبت کنیم. مثلا:
WebSecurity.CreateUserAndAccount(
                model.UserName,
                model.Password,
                new { Mobile = model.Mobile },
                false); 
برای اطلاعات بیشتر همراه با مثال: ^
مطالب
دو تنظیم ضروری VS.NET جهت کار با WPF و Silverlight

تنظیم اول: تغییر نحوه‌ی نمایش پیش فرض فایل‌های XAML

اگر فایل XAML شما اندکی حجیم شود نمایش آن در VS.NET کمی طولانی خواهد شد و حالت پیش فرض نمایش در VS.NET هم split view mode است (نمایش XAML و پیش نمایش آن با هم). این مورد هم پس از مدتی تبدیل به عذاب می‌شود. برای رفع آن می‌توان حالت پیش فرض نمایش یک فایل XAML را به XAML View تنها تغییر داد.
برای این منظور به منوی Tools ، گزینه‌ی Options و سپس قسمت تنظیمات Text editor مراجعه کنید. در اینجا در قسمت XAML ، گزینه‌ی Miscellaneous را انتخاب کرده و سپس "Always open documents in full XAML view" را تیک بزنید.



حتی ممکن است این مورد هم رضایت بخش نباشد. در این حالت می‌توان ویرایشگر پیش فرض را کلا تغییر داد. Design tab را در پایین صفحه از دست می‌دهیم اما هنوز intellisense کار می‌کند و اگر نیاز به designer بود فقط کافی است کلیک راست کرده و گزینه‌ی View designer را انتخاب کرد:
روی یک فایل XAML دلخواه کلیک راست کرده و گزینه‌ی Open with را انتخاب کنید. سپس "Source Code (Text) Editor" را انتخاب کرده و روی دکمه‌ی Set as Default کلیک کنید. تمام!
هر چند Blend این مشکلات را ندارد و با فایل‌های حجیم XAML به خوبی کاری می‌کند.


تنظیم دوم: تغییر نحوه‌ی نمایش مشکلات ناشی از Binding

عموما اگر مشکلاتی در حین عملیات Binding در WPF یا Silverlight وجود داشته باشند، خطاها در Debugger Output Window نمایش داده می‌شوند. حالت پیش فرض هم فقط روی Error تنظیم شده است به این معنا که warning ها را مشاهده نخواهید کرد. برای تغییر این مورد باید به صورت زیر عمل کرد:
به منوی Tools ، گزینه‌ی Options و سپس قسمت تنظیمات Debugging مراجعه کنید. گزینه‌ی Output Window -> WPF Trace Settings را انتخاب نمائید. سپس در اینجا قسمت WPF trace settings را یافته و مقدار پیش فرض Data binding را که به Error تنظیم شده است، به Warning تنظیم نمائید.



مطالب
پیاده سازی INotifyPropertyChanged با استفاده از Unity Container
AOP یکی از فناوری‌های مرتبط با توسعه نرم افزار محسوب می‌شود که توسط آن می‌توان اعمال مشترک و متداول موجود در برنامه را در یک یا چند ماژول مختلف قرار داد (که به آن‌ها Aspects نیز گفته می‌شود) و سپس آن‌ها را به مکان‌های مختلفی در برنامه متصل ساخت. عموما Aspects، قابلیت‌هایی را که قسمت عمده‌ای از برنامه را تحت پوشش قرار می‌دهند، کپسوله می‌کنند. اصطلاحا به این نوع قابلیت‌های مشترک، تکراری و پراکنده مورد نیاز در قسمت‌های مختلف برنامه، Cross cutting concerns نیز گفته می‌شود؛ مانند اعمال ثبت وقایع سیستم، امنیت، مدیریت تراکنش‌ها و امثال آن. با قرار دادن این نیازها در Aspects مجزا، می‌توان برنامه‌ای را تشکیل داد که از کدهای تکراری عاری است.

پیاده سازی INotifyPropertyChanged یکی از این مسائل می‌باشد که می‌توان آن را در یک Aspect محصور و در ماژول‌های مختلف استفاده کرد.

مسئله:
کلاس زیر مفروض است:
public class Foo
{
        public virtual int Id { get; set; }

        public virtual string Name { get; set; }
}
اکنون می‌خواهیم  کلاس Foo را به INotifyPropertyChanged مزین، و  یک Subscriber به قسمت set پراپرتی‌های کلاس‌ تزریق کنیم.

راه حل:
ابتدا پکیچ‌های Unity را از Nuget دریافت کنید:
PM> Install-Package Unity.Interception
این پکیچ وابستگی‌های خود را که Unity و CommonServiceLocator هستند نیز دریافت می‌کند.

حال یک Interceptor که اینترفیس IInterceptionBehavior را پیاده سازی می‌کند، می‌نویسیم:
namespace NotifyPropertyChangedInterceptor.Interceptions
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Reflection;
    using Microsoft.Practices.Unity.InterceptionExtension;

    class NotifyPropertyChangedBehavior : IInterceptionBehavior
    {
        private event PropertyChangedEventHandler PropertyChanged;

        private readonly MethodInfo _addEventMethodInfo =
            typeof(INotifyPropertyChanged).GetEvent("PropertyChanged").GetAddMethod();

        private readonly MethodInfo _removeEventMethodInfo =
            typeof(INotifyPropertyChanged).GetEvent("PropertyChanged").GetRemoveMethod();

        
        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            if (input.MethodBase == _addEventMethodInfo)
            {
                return AddEventSubscription(input);
            }

            if (input.MethodBase == _removeEventMethodInfo)
            {
                return RemoveEventSubscription(input);
            }
            
            if (IsPropertySetter(input))
            {
                return InterceptPropertySet(input, getNext);
            }
            
            return getNext()(input, getNext);
        }

        public bool WillExecute
        {
            get { return true; }
        }

        public IEnumerable<Type> GetRequiredInterfaces()
        {
            yield return typeof(INotifyPropertyChanged);
        }

        private IMethodReturn AddEventSubscription(IMethodInvocation input)
        {
            var subscriber = (PropertyChangedEventHandler)input.Arguments[0];
            PropertyChanged += subscriber;

            return input.CreateMethodReturn(null);
        }

        private IMethodReturn RemoveEventSubscription(IMethodInvocation input)
        {
            var subscriber = (PropertyChangedEventHandler)input.Arguments[0];
            PropertyChanged -= subscriber;

            return input.CreateMethodReturn(null);
        }

        private bool IsPropertySetter(IMethodInvocation input)
        {
            return input.MethodBase.IsSpecialName && input.MethodBase.Name.StartsWith("set_");
        }

        private IMethodReturn InterceptPropertySet(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            var propertyName = input.MethodBase.Name.Substring(4);

            var subscribers = PropertyChanged;
            if (subscribers != null)
            {
                subscribers(input.Target, new PropertyChangedEventArgs(propertyName));
            }

            return getNext()(input, getNext);
        }
    }
}

متد Invoke : این متد Behavior مورد نظر را پردازش می‌کند (در اینجا، تزریق یک Subscriber در قسمت set پراپرتی ها).
متد GetRequiredInterfaces : یک روش است برای یافتن کلاس هایی که با اینترفیس IInterceptionBehavior مزین شده‌اند.
پراپرتی WillExecute : ابن پراپرتی به Unity می‌گوید که این Behavior اعمال شود یا نه. اگر مقدار برگشتی آن false باشد، متد Invoke اجرا نخواهد شد.
همانطور که در متد Invoke مشاهد می‌کنید، شرط هایی برای افزودن و حذف یک  Subscriber و چک کردن متد set نوشته شده و در غیر این صورت کنترل به متد بعدی داده می‌شود.

اتصال Interceptor به کلاس ها
در ادامه Unity را برای ساخت یک نمونه از کلاس پیکربندی می‌کنیم:
var container = new UnityContainer();

container.RegisterType<Foo, Foo>(
                new AdditionalInterface<INotifyPropertyChanged>(),
                new Interceptor<VirtualMethodInterceptor>(),
                new InterceptionBehavior<NotifyPropertyChangedBehavior>())
                .AddNewExtension<Interception>();
توسط متد RegisterType یک Type را با پیکربندی دلخواه به Unity معرفی می‌کنیم. در اینجا به ازای درخواست Foo (اولین پارامتر جنریک)، یک Foo (دومین پارامتر جنریک ) برگشت داده می‌شود. این متد تعدادی InjetctionMember (بصورت params) دریافت می‌کند که در این مثال سه InjetctionMember  به آن پاس داه شده است:
  • Interceptor : اطلاعاتی در مورد IInterceptor و نحوه‌ی Intercept یک شیء را نگه داری می‌کند. در اینجا از  VirtualMethodInterceptor برای تزریق کد استفاده شده.
  • InterceptionBehavior : این کلاس Behavior مورد نظر را به کلاس تزریق می‌کند.
  • AddintionalInterface  : کلاس target را مجبور به پیاده سازی اینترفیس دریافتی از پارامتر می‌کند.  اگر کلاس behavior، متد  GetRequiredInterfaces  اینترفیس INotifyPropertyChanged را برمی گرداند، نیازی نیست از AddintionalInterface در پارامتر متد فوق استفاده کنید. 

نکته :
کلاس VirtualMethodInterceptor فقط اعضای virtual را تحت تاثیر قرار می‌دهد.
اکنون نحوه‌ی ساخت یک نمونه از کلاس Foo به شکل زیر است:
var foo = container.Resolve<Foo>();
(foo as INotifyPropertyChanged).PropertyChanged += FooPropertyChanged;
private void FooPropertyChanged (object sender, PropertyChangedEventArgs e)
 {
      // Do some things.......
 }

نکته‌ی تکمیلی
طبق مستندات MSDN، کلاس VirtualMethodInterceptor  یک کلاس جدید مشتق شده از کلاس target (در اینجا Foo) می‌سازد. بنابراین اگر کلاس‌های شما دارای Data annotation و یا در کلاس‌های Mapper یک ORM استفاده شده‌اند (مانند کلاس‌های لایه Domain)، بجای  VirtualMethodInterceptor  از TransparentProxyInterceptor استفاده کنید.
سرعت اجرای VirtualMethodInterceptor سریعتر است ؛ اما به یاد داشته که برای استفاده از  TransparentProxyInterceptor  باید کلاس target از کلاس MarshalByRefObject ارث بری کند.
نظرات مطالب
فعال سازی عملیات CRUD در Kendo UI Grid

دیتای زیر را در نظر بگیرید:

[
{
    OrderId: 1,
    OrderName: 'order 1'
    OrderItems: [
        {
            ProductId: 1,
            ProductName: "sample name"
        },
        {
            ProductId: 2,
            ProductName: "sample name 2"
        }
},
{
    OrderId: 2,
    OrderName: 'order 2'
    OrderItems: [
        {
            ProductId: 55,
            ProductName: "sample name 55"
        },
        {
            ProductId: 18,
            ProductName: "sample name18"
        }
}
]

من چطور میتونم مدلی تعریف کنم که بتوان مدل های  nested  این مدل رو پوشش بده:

var model = kendo.data.Model.define({
    id: "OrderId",
    fields: {
        OrderId: {
                type: "number",
                editable: false
        },
 
        OrderName: {
                type: "string",
                editable: false
        },
 
        OrderItems: {
            ??????????????
        }
    }
});

آیا امکان تعریف مدلی به این شکل وجود دارد که بتوان در حین عملیات  CRUD  مدل های  nested  را هم تغییر داد؟ 

مطالب
آموزش فایرباگ - #6 - HTML Panel - Side Panels
در قسمت قبل توضیحاتی در مورد تب HTML ارائه کردیم.
در این قسمت توضیحات کاملی در مورد پنل‌های جانبیِ داخل پنل HTML می‌دهیم.



Side Panels

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




1 - Style
در این تب استایل هایی که در حال حاظر بروی تگ انتخاب شده اعمال شده اند ، نمایش داده می‌شود.
در صورتی که موس را بروی مقادیر استایل هایی که جلوه‌ی بصری دارند بگیرید ، یک پاپ‌آپ کوچک نمایان می‌شود که مقدار را نمایش می‌دهد.


 Options Menu

هر تب یا پنل در فایرباگ دارای یک سری تنظیمات است که Options Menu نام دارد. تب Style هم دارای یک سری تنظیمات است که دانشتن آنها بسیار به شما کمک خواهد کرد.
این منو با کلیک کردن بروی فلش تب () یا راست کلیک کردن بروی تب ظاهر می‌شود.


  • Only Show Applied Styles
    در صورت انتخاب ، فقط استایل هایی که اعمال شده اند نمایش داده می‌شوند. (استایل‌های Overwrite شده نمایش داده نمی‌شوند.)
    (این گزینه قابلیت خوبی است ، اما چندبار برای بنده پیش آمده که این مورد به اشتباه استایلی که اعمال شده بود را هم Overwrite شده در نظر گرفته بود. پس در هین طراحی استایل و کار با CSS اگر احیانا یکی از استایل هایتان وجود نداشت و از وجود آن اطمینان داشتید ، غیرفعال کردن این گزینه را امتحان کنید.)

  • Show User Agent CSS
    با فعال کردن این گزینه ، استایل هایی که توسط مرورگر اعمال شده اند هم نمایش داده می‌شوند.

  • Expand Shorthand Properties
    با فعال کردن این گزینه ، استایل هایی که بصورت کوتاه شده تعریف شده اند را بصورت گسترده و باز شده نمایش می‌دهد.
    برای مثال ، دستور margin را بصورت margin-top , margin-right , margin-bottom , margin-left نمایش می‌دهد.

  • سه گزینه ی Colors As Hex ، Colors As RGB و Colors As HSL تعیین کننده‌ی فرمت نمایش رنگ‌ها هستند.

  • سه گزینه ی :active ، :hover و :focus هم برای تعیین کلاس کاذب برای تگ جاری کاربرد دارند.
    برای مثال شما می‌خواهید استایلی که یک لینک زمان موس برویش قرار دارد را بررسی کنید ، لینک را در NodeView انتخاب می‌کنید و سپس از گزینه‌ی :hover را فعال می‌کنید.


Panel

  • Element styles
    استایل هایی که بصورت inline (در خود تگ) تعریف شده اند هم در این قسمت نمایش داده می‌شود و نام rule آن element.style است.



  • Source Links
    در بالا-راست هر بخش ، یک لینک قرار دارد که لینک فایل استایلی است که در همان قسمت وجود دارد و عددی که در پرانتز قرار دارد ، شماره خط استایل در همان فایل است.
    اگر نام فایل با نام صفحه‌ی جاری برابر باشد ، به معنی وجود استایل در تگ <style> در صفحه‌ی جاری است و شماره‌ی بعد از # هم ایندکس تگ <style> است.
    (با کلیک بروی لینک فایل ، فایل در خط مورد نظر در پنل CSS نمایش داده می‌شود.)



  • Inherited rules
    rule‌های به ارث رسیده هم در قسمت‌های جداگانه به همراه استایل‌های به ارث رسیده نمایش داده می‌شود و تگی والد که استایل‌ها از آن به ارث رسیده اند هم در قسمت عنوان همان استایل‌ها نمایش قرار داده شده است. (با کلیک بروی آن ، در قسمت Nodeview انتخاب می‌شود.)



  • User agent rules
    استایل هایی که توسط مرورگر اعمال شده اند (User agent rules) ، با عبارت <System> در زیر لینک منبع استایل ، مشخص شده اند.



  • Overwritten styles
    استایل‌های overwrite شده ، با یک خط برویشان مشخص شده اند.



  • Inline editing
    استایل‌های نمایش داده شده در این پنل را براحتی و با کلیک کردن بروی نام یا مقدار هر یک از دستورات می‌توانید تغییر دهید.
    برای نوشتن دستورات و مقادیر آن‌ها می‌توانید از پیشنهاد‌های فایرباگ هم کمک بگیرید و با دکمه‌های Arrow Up و Arrow Down هم بین مقادیر مجاز حرکت کنید.
    دستورات یا مقادیر نا صحیح در هین تایپ ، با رنگ قرمز و مقادیر صحیح با رنگ سبز مشخص می‌شوند.
    (این امکان خیلی مفید است ، برای مثال می‌خواهید فونت‌های مختلف را برای یک استایل امتحان کنید ، دستور font-family را می‌نویسید و بعد از زدن Enter ، با دکمه های Arrow Up و Arrow Down در لحظه نتیجه‌ی اعمال فونت‌های مختلف و دردسترس را مشاهده می‌کنید و بهترین را بر می‌گزینید.
    یا برای یافتن بهترین مقدار margin ، بعد از دستور margin ، زدن کلید Enter ، وارد کردن یک عدد برای شروع ، می‌توان باز هم با دکمه های Arrow Up و Arrow Down به سرعت تغییر را در صفحه مشاهده کرد.)

  • Rendered font highlighted
    برای دستور font ، فایرباگ هوشمندانه عمل کرده و فونتی که در حال استفاده است را پررنگ می‌کند.
    این امکان برای یافتن خطاهای متداول هنگام تعریف فونت‌های غیر سیستمی ، بسیار مفید است.




 Context Menu

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


 گزینه Context توضیحات 
Copy Rule Declaration CSS selector  CSS Rule فعلی را به همراه استایل هایش در clipboard کپی می‌کند
Copy Style Declaration CSS selector  استایل‌های CSS Rule فعلی را در clipboard کپی می‌کند 
Copy Location source link  آدرس فایل تعریف CSS Rule را در clipboard کپی می‌کند
Open in New Tab source link   آدرس فایل تعریف CSS Rule را در تب جدید باز می‌کند
Edit Element Style... everywhere  امکان تعریف استایل‌های درون تگ (inline) را محیا می‌کند
Add Rule... everywhere  یک Rule جدید ایجاد می‌کند
CSS Rule هایی که در حال حاظر وجود دارد را هم پیشنهاد می‌دهد
Delete "<rule name>" CSS selector  CSS Rule فعلی را حذف می‌کند
New Property... CSS rule  یک استایل جدید به CSS Rule فعلی اضافه می‌کند
Edit "<property name>"... CSS property  Property فعلی را به حالت ویرایش می‌برد
راه دیگر ویرایش Property ، کلیک بروی آن است
Delete "<property name>" CSS property  Property فعلی را حذف می‌کند
Disable "<property name>" CSS property  Property فعلی را غیر فعال می‌کند
را سریع‌تر ، کلیک کردن در ناحیه‌ی پشت Property ، بروی علامت قرمز رنگ است
Refresh everywhere  محتویات پنل را بروز می‌کند
Inspect in DOM Panel
CSS rule  CSS Rule فعلی را در پنل DOM برای بررسی باز می‌کند
Inspect in CSS Panel CSS rule   CSS Rule فعلی را در پنل CSS برای بررسی باز می‌کند
<Default Editor Name> CSS rule  فایل تعریف استایل‌ها را در ادیتور تعریف شده باز می‌کند
(این گزینه در صورت تعریف ادیتور در تنظیمات FireBug نمایش داده خواهد شد)


2 - Computed

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


Style Tracing
برای ردیابی استایل‌ها ، استایل‌ها به ترتیب اعمال شدنشان مرتب شده اند و اولین مقدار ، مقداری است که اعمال شده است.
مقادیر Overwrite بصورت خط کشیده شده و استایل‌های Overwrite شده بصورت خاکستری-کمرنگ نمایش داده می‌شوند.
هر استایل هم مانند تب Style ، یک لینک به منبع خود دارد.


Options Menu

  • Show User Agent CSS
    در صورت انتخاب ، فقط استایل هایی که اعمال شده اند نمایش داده می‌شوند.

  • Sort alphabetically
    در صورت انتخاب ، استایل‌ها به ترتیب الفبا ، و درصورت عدم انتخاب بصورت گروه بندی نمایش داده می‌شوند.

  • Show Mozilla Specific Styles
    در صورت انتخاب ، استایل‌های مخصوص Mozilla را نمایش می‌دهد. (استایل هایی با پیشوند -moz-)

  • سه گزینه‌ی Colors As Hex ، Colors As RGB و Colors As HSL تعیین کننده‌ی فرمت نمایش رنگ‌ها هستند.


Context Menu

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

 گزینه Context توضیحات 
Expand All Styles everywhere  CSS Rule فعلی را به همراه استایل هایش در clipboard کپی می‌کند
Collapse All Styles everywhere  استایل‌های CSS Rule فعلی را در clipboard کپی می‌کند 
Inspect in DOM panel styles   آدرس فایل تعریف CSS Rule را در تب جدید باز می‌کند
Copy Location
style source link  امکان تعریف استایل‌های درون تگ (inline) را محیا می‌کند
Open in New Tab style source link  یک Rule جدید ایجاد می‌کند
CSS Rule هایی که در حال حاظر وجود دارد را هم پیشنهاد می‌دهد
Inspect in CSS panel style source link  CSS Rule فعلی را حذف می‌کند
<Default Editor Name> style source link  فایل تعریف استایل‌ها را در ادیتور تعریف شده باز می‌کند
(این گزینه در صورت تعریف ادیتور در تنظیمات FireBug نمایش داده خواهد شد)


3 - Layout

در این تب ، مقادیر Box Model بصورت بصری نمایش می‌دهد. می‌توان با کلیک کردن بروی هریک از مقادیر ، آن را ویرایش کرد. (این تغییر بصورت inline در تگ اعمال می‌شود.)
با حرکت موس بروی قسمت‌های مختلف ، می‌توان همان قسمت‌ها را در صفحه بصورت خط کشی شده مشاهده کرد.
(البته ظاهرا در ورژن 1.10.4 که بنده استفاده می‌کنم ، عملیات ویرایش مقادیر به درستی انجام نمی‌شود.)



 Options Menu

  • Show Rulers and Guides
    در صورت انتخاب ، خط‌های راهنما را هنگام حرکت موس بروی اجزای Box Model در صفحه نمایش می‌دهد.




4 - DOM

این پنل اطلاعات DOM تگ جاری را نمایش می‌دهد.
این پنل تمام قابلیت‌های پنل DOM اصلی را دارا می‌باشد.
(در مقالات آینده با تب DOM آشنا خواهیم شد.)