مطالب
مدیریت سفارشی سطوح دسترسی کاربران در MVC
فرض کنید در پروژه‌ی جاری خودتون قصد دارید یک سیستم مدیریت سطوح دسترسی کاربران رو با انعطاف بالا پیاده سازی کنید . مثلا سیستم شما دارای صفحات مختلفی هستش که هر گروه کاربری اجازه دسترسی به هر صفحه رو نداره ... هدف اینه که شما این گروه‌های کاربری و سطوح دسترسی برای هر گروه رو برای سیستم مشخص کنید .
مثلا فقط کاربرانی که دسترسی admin دارن بتونن به صفحات مدیریتی دسترسی داشته باشن و ....
برای این منظور در دات نت کلاسی با نام Role Provider وجود داره که در ادامه‌ی این مبحث به کار با اون میپردازیم.
مثلا فرض کنید قرار بر اینه که سطوح دسترسی رو از بانک اطلاعاتی استخراج کنیم . کلاس مذکور در فضای نام System.Web.Security قرار گرفته . برای شروع ما نیاز داریم یک نمونه از این کلاس رو پیاده سازی کرده و چند تابع از اون رو بازنویسی کنیم . 

پیاده سازی کلاس به این صورته :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;

namespace Myproject.Security
{
    public class CustomRoleProvider : RoleProvider
    {

    }
}
 خب در مرحله‌ی بعد دو تابع از این کلاس رو بازنویسی میکنیم . اول تابع GetRolesForUser که در این مقاله وظیفه‌ی استخراج لیست مجوز‌ها برای هر کاربر رو از بانک اطلاعاتی داره که به شکل زیر پیاده سازی میشه .
public override string[] GetRolesForUser(string username)
        {
            using (DatabaseEntities db = new DatabaseEntities())
            {
                User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase));
 
                var roles = from ur in user.UserRoles
                            from r in db.Roles
                            where ur.RoleId == r.Id
                            select r.Name;
                if (roles != null)
                    return roles.ToArray();
                else
                    return new string[] {}; ;
            }
        }
 همونطور که میبینید در تابع بالا از کلاس CustomRoleProvider ما عملیات استخراج لیست مجوز‌های دسترسی مربوط به هر کاربر رو از بانک اطلاعاتی انجام دادیم . توجه داشته باشین که شما میتونید این واکشی رو از هر محیط دیگه ای جز بانک اطلاعاتی هم بسته به نوع کارتون انجام بدید .

تابع بعد IsUserInRole نام داره که با بازنویسی اون مشخص میکنیم که ایا یک کاربر دارای مجوز لازم برای دسترسی هست یا نه . اون رو به شکل زیر بازنویسی میکنیم.
public override bool IsUserInRole(string username, string roleName)
        {
            using (DatabaseEntities db = new DatabaseEntities())
            {
                User user = db.Users.FirstOrDefault(u => u.UserName.Equals(username, StringComparison.CurrentCultureIgnoreCase));
 
                var roles = from ur in user.UserRoles
                            from r in db.Roles
                            where ur.RoleId == r.Id
                            select r.Name;
                if (user != null)
                    return roles.Any(r => r.Equals(roleName, StringComparison.CurrentCultureIgnoreCase));
                else
                    return false;
            }
        }
 همونطور که شاهد هستید در تابع بالا بعد از واکشی لیست مجوز‌های ثبت شده برای هر کاربر بررسی انجام میشه که ایا اولا کاربر یک کاربر ثبت شده هست و ثانیا اینکه ایا درخواستی که ارسال کرده برای دسترسی به یک بخش مجوز اون رو داره یا خیر ... 

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

public override bool IsUserInRole(string username, string roleName)
        {
  return this.GetRolesForUser(username).Contains(roleName);
        }

خب میرسیم به بخش معرفی این Provider در web.config که به صورت زیر انجام میشه ...
<system.web>
    ...
    <rolemanager cacherolesincookie="true" defaultprovider="CustomRoleProvider" enabled="true">
        <providers>
            <clear />
            <add name="CustomRoleProvider" type="Myproject.Security.CustomRoleProvider" />
        </providers>
    </rolemanager>
    ...
</system.web>

 توجه داشته باشید که مجوز‌های هر کاربر با معرفی بالا بعد از یک بار واکشی در کوکی ذخیره میشه و دیگه هر بار، بار اضافه برای واکشی از بانک اطلاعاتی به برنامه تحمیل نمیشه ...

حالا به این صورت میتونیم مثلا یک Controller رو محافظت  کنیم در برابر درخواست از جانب کاربرانی که سطح دسترسی به اون رو ندارند .
using System;
using System.Web.Mvc;
 
namespace MyProject.Areas.Admin.Controllers
{
    [Authorize(Roles = "Administrators")]
    public class HomeController : Controller
    {
        //
        // GET: /Admin/Home/
 
        public ActionResult Index()
        {
            return View();
        }
 
    }
}
 توجه داشته باشید که کنترل مجوز برای بررسی  وجود مجوز در بخش‌های کوچکتر هم مانند اکشن‌ها و ... میتونه در نظر گرفته بشه .
مطالب
مفاهیم برنامه نویسی ـ مروری بر فیلدها، متدها و ساخت اشیاء
شکستن یک مسئله بزرگ به تعدادی مسئله کوچک‌تر راهکار موثری برای حل آن است. این امر در برنامه نویسی نیز که هدف آن چیزی جز حل یک مسئله نیست همواره مورد توجه بوده است. به همین دلیل روش هایی که به کمک آن‌ها بتوان یک برنامه بزرگ را به قطعات کوچکتری تقسیم کرد تا هر قطعه کد مسئول انجام کار خاصی باشد پیشتر به زبان‌های برنامه نویسی اضافه شده اند. یکی از این ساختار‌ها تابع (Function) نام دارد. برنامه ای که از توابع برای تقسیم کدهای برنامه استفاده می‌کند یک برنامه ساخت‌یافته می‌گوییم.
در مطلب پیشین به پیرامون خود نگاه کردیم و اشیاء گوناگونی را مشاهده کردیم که در حقیقت دنیای ما را تشکیل داده اند و فعالیت‌های روزمره ما با استفاده از آن‌ها صورت می‌گیرد. ایده ای به ذهنمان رسید. اشیاء و مفاهیم مرتبط به آن می‌تواند روش بهتر و موثرتری برای تقسیم کدهای برنامه باشد. مثلاً اگر کل کدهای برنامه که مسئول حل یکی از مسئله‌های کوچک یاد شده است را یکجا بسته بندی کنیم و اصولی که از اشیاء واقعی پیرامون خود آموختیم را در مورد آن رعایت کنیم به برنامه بسیار با کیفیت‌تری از نظر خوانایی، راحتی در توسعه، اشکال زدایی ساده‌تر و بسیاری موارد دیگر خواهیم رسید.
توسعه دهندگان زبان‌های برنامه نویسی که با ما در این مورد هم عقیده بوده اند دست به کار شده و دستورات و ساختار‌های لازم برای پیاده کردن این ایده را در زبان برنامه نویسی قرار دادند و آن را زبان برنامه نویسی شیء گرا نامیدند. حتی جهت برخورداری از قابلیت استفاده مجدد از کد و موارد دیگر به جای آنکه کدها را در بسته هایی به عنوان یک شیء خاص قرار دهیم آن‌ها را در بسته هایی به عنوان قالب یا نقشه ساخت اشیاء خاصی که در ذهن داریم قرار می‌دهیم. یعنی مفهوم کلاس یا رده که پیشتر اشاره شد. به این ترتیب یک بار می‌نویسیم و بارها استفاده می‌کنیم. مانند همان مثال بازیکن در بخش نخست. هر زمان که لازم باشد با استفاده از دستورات مربوطه از روی کدهای کلاس که نقشه یا قالب ساخت اشیاء هستند شیء مورد نظر را ساخته و در جهت حل مسئله مورد نظر به کار می‌بریم.
حال برای آنکه به طور عملی بتوانیم از ایده شیء گرایی در برنامه هایمان استفاده کنیم و مسائل بزرگ را حل کنیم لازم است ابتدا مقداری با جزییات و دستورات زبان در این مورد آشنا شویم.
تذکر: دقت کنید برای آنکه از ایده شیء گرایی در برنامه‌ها حداکثر استفاده را ببریم مفاهیمی در مهندسی نرم افزار به آن اضافه شده است که ممکن است در دنیای واقعی نیازی به طرح آن‌ها نباشد. پس لطفاً تلاش نکنید با دیدن هر مفهوم تازه بلافاصله سعی در تطبیق آن با محیط اطراف کنید. هر چند بسیاری از آن‌ها به طور ضمنی در اشیاء پیرامون ما نیز وجود دارند.
زبان برنامه نویسی مورد استفاده برای بیان مفاهیم برنامه نویسی در این سری مقالات زبان سی شارپ است. اما درک برنامه‌های نوشته شده برای علاقه مندان به زبان‌های دیگری مانند وی بی دات نت نیز دشوار نیست. چراکه اکثر دستورات مشابه است و تبدیل Syntax نیز به راحتی با اندکی جستجو میسر می‌باشد. لازم به یادآوری است زبان سی شارپ به بزرگی یا کوچکی حروف حساس است.

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

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

public class Rectangle
{
   public double Width;
   public double Height;
 
   public double Area()
   {
      return Width*Height;
   }
 
   public double Perimeter()
   {
      return 2*(Width + Height);
   }
}

در این قطعه برنامه نکات زیر قابل توجه است:
  • کلاس با کلمه کلیدی class تعریف می‌شود.
  • همان طور که مشاهده می‌کنید تعریف کلاس با کلمه public آغاز شده است. این کلمه محدوده دسترسی به کلاس را تعیین می‌کند. در اینجا از کلمه public استفاده کردیم تا بخش‌های دیگر برنامه امکان استفاده از این کلاس را داشته باشند.
  • پس از کلمه کلیدی class نوبت به نام کلاس می‌رسد. اگرچه انتخاب نام مورد نظر امری اختیاری است اما در آینده حتماً اصول و قراردادهای نام‌گذاری در دات‌نت را مطالعه نمایید. در حال حاضر حداقل به خاطر داشته باشید تا انتخاب نامی مناسب که گویای کاربرد کلاس باشد بسیار مهم است.
  • باقیمانده کد، بدنه کلاس را تشکیل می‌دهد. جاییکه ویژگی ها، رفتار‌ها و ... یا به طور کلی اعضای کلاس تعریف می‌شوند.
نکته: کلماتی مانند public که پیش از تعریف کلاس یا اعضای آن قرار می‌گیرند Modifier یا پیراینده نام دارند. که البته به نظر من ترجمه این گونه واژه‌ها از کارهای شیطان است. بنابراین از این پس بهتر است همان Modifier را به خاطر داشته باشید. از آنجا که public مدیفایری است که سطح دسترسی را تعیین می‌کند به آن یک Access Modifier می‌گویند. در یک بخش از این سری مقالات تمامی مدیفایرها بررسی خواهند شد.

ایجاد شیء از یک کلاس و نحوه دسترسی به شیء ایجاد شده

شیء و کلاس چیزهای متفاوتی هستند. یک کلاس نوع یک شیء را تعریف می‌کند. اما یک شیء یک موجودیت عینی و واقعی بر اساس یک کلاس است. در اصطلاح از شیء به عنوان یک نمونه (Instance) یا وهله ای از کلاس مربوطه یاد می‌کنیم. همچنین به عمل ساخت شیء نمونه سازی یا وهله سازی گوییم.
برای ایجاد شیء از کلمه کلیدی new و به دنبال آن نام کلاسی که قصد داریم بر اساس آن یک شیء بسازیم استفاده می‌کنیم. همان طور که اشاره شد کلاس یک نوع را تعریف می‌کند. پس از آن می‌توان همانند سایر انواع مانند int, string, … برای تعریف متغیر استفاده نمود. به مثال زیر توجه کنید.
Rectangle rectangle = new Rectangle();
در این مثال rectangle که با حرف کوچک شروع شده و می‌توانست هر نام دلخواه دیگری باشد ارجاعی به شیء ساخته شده را به دست می‌دهد. وقتی نمونه ای از یک کلاس ایجاد می‌شود یک ارجاع به شیء تازه ساخته شده برای برنامه نویس برگشت داده می‌شود. در این مثال rectangle یک ارجاع به شیء تازه ساخته شده است یعنی به آن اشاره می‌کند اما خودش شامل داده‌های آن شیء نیست. تصور کنید این ارجاع تنها دستگیره ای برای شیء ساخته شده است که دسترسی به آن را برای برنامه نویس میسر می‌کند. درک این مطلب از این جهت دارای اهمیت است که بدانید می‌شود یک دستگیره یا ارجاع دیگر بسازید بدون آنکه شیء جدیدی تولید کنید.
Rectangle rectangle;
البته توصیه نمی‌کنم چنین ارجاعی را تعریف کنید چرا که به هیچ شیء خاصی اشاره نمی‌کند. و تلاش برای استفاده از آن منجر به بروز خطای معروفی در برنامه خواهد شد. به هر حال یک ارجاع می‌توان ساخت چه با ایجاد یک شیء جدید و یا با نسبت دادن یک شیء موجود به آن.
Rectangle rectangle1 = new Rectangle();
Rectangle rectangle2 = rectangle1;
در این کد دو ارجاع یا دستگیره ایجاد شده است که هر دو به یک شیء اشاره می‌کنند. بنابراین ما با استفاده از هر دو ارجاع می‌توانیم به همان شیء واحد دسترسی پیدا کنیم و اگر مثلاً با rectangle1 در شیء مورد نظر تغییری بدهیم و سپس با rectangle2 شیء را مورد بررسی قرار دهیم تغییرات داده شده قابل مشاهده خواهد بود چون هر دو ارجاع به یک شیء اشاره می‌کنند. به همین دلیل کلاس‌ها را به عنوان نوع ارجاعی می‌شناسیم در مقایسه با انواع داده دیگری که اصطلاحاً نوع مقداری هستند.
حالا می‌توان شیء ساخته شده را با استفاده از ارجاعی که به آن داریم به کار برد.
Rectangle rectangle = new Rectangle();
rectangle.Width = 10.5;
rectangle.Height = 10;
double a = rectangle.Area();
ابتدا عرض و ارتفاع شیء چهارضلعی را مقدار دهی کرده و سپس مساحت را دریافت کرده ایم. از نقطه برای دسترسی به اعضای یک شیء استفاده می‌شود.

فیلدها

اگر به تعریف کلاس دقت کنید مشخص است که دو متغییر Width و Height را با سطح دسترسی عمومی تعریف کرده ایم.
به متغیرهایی از هر نوع که مستقیماً درون کلاس تعریف شوند (و نه مثلاً داخل یک تابع درون کلاس) فیلد می‌گوییم. فیلدها از اعضای کلاس دربردارنده آن‌ها محسوب می‌شوند.
تعریف فیلدها مستقیماً در بدنه کلاس با یک Access Modifier شروع می‌شود و به دنبال آن نوع فیلد و سپس نام دلخواه برای فیلد می‌آید.
تذکر: نامگذاری مناسب یکی از مهمترین اصولی است که یک برنامه نویس باید همواره به آن توجه کافی داشته باشد و به شدت در بالا رفتن کیفیت برنامه موثر است. به خاطر داشته باشید تنها اجرا شدن و کار کردن یک برنامه کافی نیست. رعایت بسیاری از اصول مهندسی نرم افزار که ممکن است نقش مستقیمی در کارکرد برنامه نداشته باشند موجب سهولت در نگهداری و توسعه برنامه شده و به همان اندازه کارکرد صحیح برنامه مهم هستند. بنابراین مجدداً شما را دعوت به خواندن مقاله یاد شده بالا در مورد اصول نامگذاری صحیح می‌کنم. هر مفهوم تازه ای که می‌آموزید می‌توانید به اصول نامگذاری همان مورد در مقاله پیش گفته مراجعه نمایید. همچنین افزونه هایی برای Visual Studio وجود دارد که شما را در زمینه نامگذاری صحیح و بسیاری موارد دیگر هدایت می‌کنند که یکی از مهمترین آن‌ها Resharper نام دارد.
مثال:
// public field (Generally not recommended.)
public double Width;
همان طور که در این قطعه کد به عنوان توضیح درج شده است استفاده از فیلدهایی با دسترسی عمومی توصیه نمی‌شود. علت آن واضح است. چون هیچ کنترلی برای مقداری که برای آن در نظر گرفته می‌شود نداریم. به عنوان مثال امکان دارد یک مقدار منفی برای عرض یا ارتفاع شیء درج شود حال آنکه می‌دانیم عرض یا ارتفاع منفی معنا ندارد. در قسمت بعدی این سری مقالات این مشکل را بررسی و حل خواهیم نمود.
فیلد‌ها معمولاً با سطح دسترسی خصوصی و برای نگهداری از داده‌هایی که مورد نیاز بیش از یک متد (یا تابع) درون کلاس است و آن داده‌ها باید پس از خاتمه کار یک متد همچنان باقی بمانند استفاده می‌شود. بدیهی است در غیر اینصورت به جای تعریف فیلد می‌توان از متغیرهای محلی (متغیری که درون خود تابع تعریف می‌شود) استفاده نمود.
همان طور که پیشتر اشاره شد برای دسترسی به یک فیلد ابتدا یک نقطه پس از نام شیء درج کرده و سپس نام فیلد مورد نظر را می‌نویسیم.
Rectangle rectangle = new Rectangle();
rectangle.Width = 10.5;
در هنگام تعریف یک فیلد در صورت نیاز می‌توان برای آن یک مقدار اولیه را در نظر گرفت. مانند مثال زیر:
public class Rectangle
{
   public double Width = 5;
   // ...
}

متدها

متدها قطعه کدهایی شامل یک سری دستور هستند. این مجموعه دستورات با فراخوانی متد و تعیین آرگومان‌های مورد نیاز اجرا می‌شوند. در زبان سی شارپ به نوعی تمام دستورات در داخل متدها اجرا می‌شوند. در این زبان تمامی توابع در داخل کلاس‌ها تعریف می‌شوند و بنابراین همه متد هستند.
متدها نیز مانند فیلد‌ها در داخل کلاس تعریف می‌شوند. ابتدا یک Access Modifier سطح دسترسی را تعیین می‌نماید. سپس به ترتیب نوع خروجی، نام متد و لیست پارامترهای آن در صورت وجود درج می‌شود. به مجموعه بخش‌های یاد شده امضای متد می‌گویند.
پارامترهای یک متد داخل یک جفت پرانتز قرار می‌گیرند و با کاما (,) از هم جدا می‌شوند. یک جفت پرانتز خالی نشان دهنده آن است که متد نیاز به هیچ پارامتری ندارد.
بار دیگر به بخش تعریف متدهای کلاسی که ایجاد کردیم توجه نمایید.
public class Rectangle
{
   // ...
 
   public double Area()
   {
      return Width*Height;
   }
 
   public double Perimeter()
   {
      return 2*(Width + Height);
   }
}
در این کلاس دو متد به نام‌های  Area و Perimeter به ترتیب برای محاسبه مساحت و محیط چهارضلعی تعریف شده است. همانطور که پیشتر اشاره شد متدها برای پیاده سازی رفتار اشیاء یا همان کارکردهای آن‌ها استفاده می‌شوند. در این مثال شیء ما قادر است مساحت و محیط خود را محاسبه نماید. چه شیء خوش رفتاری!
همچنین توجه نمایید این شیء برای محاسبه مساحت و محیط خود نگاهی به ویژگی‌های خود یعنی عرض و ارتفاعش که در فیلدهای آن نگهداری می‌کنیم می‌اندازد.
فراخوانی متد یک شیء همانند دسترسی به فیلد آن است. ابتدا نام شیء سپس یک نقطه و به دنبال آن نام متد مورد نظر به همراه پرانترها. آرگومان‌های مورد نیاز در صورت وجود داخل پرانتزها قرار می‌گیرند و با کاما از هم جدا می‌شوند. که البته در این مثال متد ما نیازی به آرگومان ندارد. به همین دلیل برای فراخوانی آن تنها یک جفت پرانتز خالی قرار می‌دهیم.
در این بخش به دو مفهوم پارامتر و آرگومان اشاره شد. تفاورت آن‌ها چیست؟
در هنگام تعریف یک متد نام و نوع پارامترهای مورد نیاز را تعیین و درج می‌نماییم. حال وقتی قصد فراخوانی متد را داریم باید مقادیر واقعی که آرگومان نامیده می‌شود را برای هر یک از پارامترهای تعریف شده فراهم نماییم. نوع آرگومان باید با نوع پارامتر تعریف شده تطبیق داشته باشد. اما اگر یک متغیر را به عنوان آرگومان در هنگام فراخوانی متد استفاده می‌کنیم نیازی به یکسان بودن نام آن متغیر و نام پارامتر تعریف شده نیست.
متدها می‌توانند یک مقدار را به کدی که آن متد را فراخوانی کرده است بازگشت دهند.
Rectangle rectangle = new Rectangle();
rectangle.Width = 10.5;
rectangle.Height = 10;
double p = rectangle.Perimeter();
در این مثال مشاهده می‌کنید که پس از فراخوانی متد Perimeter مقدار بازگشتی آن در متغیری به نام p قرار گرفته است. اگر نوع خروجی یک متد که در هنگام تعریف آن پیش از نام متد قرار می‌گیرد void یا پوچ نباشد، متد می‌تواند مقدار مورد نظر را با استفاده از کلمه کلیدی return بازگشت دهد. کلمه return و به دنبال آن مقداری که از نظر نوع باید با نوع خروجی تعیین شده تطبیق داشته باشد، مقدار درج شده را به کد فراخوان متد بازگشت می‌دهد.
نکته: کلمه return علاوه بر بازگشت مقدار مورد نظر سبب پایان اجرای متد نیز می‌شود. حتی در صورتی که نوع خروجی یک متد void تعریف شده باشد استفاده از کلمه return بدون اینکه مقداری به دنبال آن بیاید می‌تواند برای پایان اجرای متد، در صورت نیاز و مثلاً برقراری شرطی خاص مفید باشد. بدون کلمه return متد زمانی پایان می‌یابد که به پایان قطعه کد بدنه خود برسد. توجه نمایید که در صورتی که نوع خروجی متد چیزی به جز void است استفاده از کلمه return به همراه مقدار مربوطه الزامی است.
مقدار خروجی یک متد را می‌توان هر جایی که مقداری از همان نوع مناسب است مستقیماً به کار برد. همچنین می‌توان آن را در یک متغیر قرار داد و سپس از آن استفاده نمود.
به عنوان مثال کلاس ساده زیر را در نظر بگیرید که متدی دارد برای جمع دو عدد.
public class SimpleMath
{
   public int AddTwoNumbers(int number1, int number2)
   {
      return number1 + number2;
   }
}
و حال دو روش استفاده از این متد:
SimpleMath obj = new SimpleMath();

Console.WriteLine(obj.AddTwoNumbers(1, 2));

int result = obj.AddTwoNumbers(1, 2);
Console.WriteLine(result);
در روش اول مستقیماً خروجی متد مورد استفاده قرار گرفته است و در روش دوم ابتدا مقدار خروجی در یک متغیر قرار گرفته است و سپس از مقدار درون متغیر استفاده شده است. استفاده از متغیر برای نگهداری مقدار خروجی اجباری نبوده و تنها جهت بالا بردن خوانایی برنامه یا حفظ مقدار خروجی تابع برای استفاده‌های بعدی به کار می‌رود.

در بخش‌های بعدی بحث ما در مورد سایر اعضای کلاس و برخی جزییات پیرامون اعضای پیش گفته خواهد بود.
 
مطالب
پنج دلیل برای توسعه‌ی وب با ASP.NET Core

یک:  ASP.NET Core مستقل از Platform است

آینده‌ی محتوم نرم‌افزار، توسعه به شیوه‌های مستقل از Platform است. شاید این دلیل به تنهایی برای مهاجرت به ASP.NET Core کافی باشد. امروزه نرم‌افزارهایی که مبتنی بر یک Platform خاص نیستند، نسبت به سایر نرم‌افزارها مزیت رقابتی‌تری دارند. نرم‌افزارهای Cross Platform یا مستقل از Platform، بر روی هر سیستم عاملی اجرا می‌شوند. برای اجرای آنها در کامپیوترهای شخصی یا Server کافیست معماری پردازنده‌ی مرکزی x86 باشد و سیستم عامل نیز یکی از انواع ویندوز، لینوکس یا مک.
دلیل مستقل بودن ASP.NET Core از Platform، مبتنی بودن آن بر NET Core. است. این نسخه از دات‌نت را می‌توان برای سیستم‌‌عامل‌های مختلف از http://dot.net دانلود و نصب کرد.
برای اجرای نرم‌افزارهایی که مبتنی بر NET Core. هستند نیاز به بازنویسی کدها یا استفاده از زبان‌های برنامه‌نویسی جداگانه‌ای نیست. این خاصیت همچنین برای libraryهای استانداردی که از این نسخه از دات‌نت استفاده می‌کنند نیز صادق است.

دو:  Open Source است

یکی از انتقادهایی که سال‌ها به مایکروسافت می‌شد، ناشناخته بودن سورس نرم‌افزارهای این شرکت و انحصار طلبی‌هایش بود. اما در سال‌های اخیر مایکروسافت نشان داده‌است که این جنبه از کاراکترش را به تدریج اصلاح کرده‌است. به طوری که اسکات هانسلمن، یکی از کارمندان این شرکت و وبلاگ‌نویس مشهور در این مورد گفته است:
دلیل آمدن من به مایکروسافت این بود که می‌خواستیم هر چقدر می‌توانیم کارها را Open Source کنیم و یک جامعه‌ی کاربری برای دات‌نت و اوپن سورس بسازیم و حالا به NET Core 1.0. رسیده‌ایم.
زمانی شایعه‌ی لو رفتن بخشی از سورس کد ویندوز ۹۵، در صدر اخبار تکنولوژی بود و این یک شکست برای مایکروسافت محسوب می‌شد. اما امروزه ASP.NET Core با لایسنس MIT عرضه شده است که یکی از آزادترین مجوزهای اوپن سورس است. نرم‌افزارهایی که با این مجوز عرضه می‌شوند، آزادی تغییر کد، ادغام با مجوزهای دیگر، عرضه به عنوان محصول دیگر، استفاده تجاری و ... را به همه‌ی توسعه‌دهندگان می‌دهد.

سه: جدا بودن از Web Server

این نسخه‌ی از APS.NET، کاملاً از وب‌سرور که نرم‌افزارها را هاست می‌کند، جدا (decouple) شده است. اگرچه همچنان استفاده از IIS بر روی ویندوز منطقی به نظر می‌رسد اما مایکروسافت یک پروژه‌ی اوپن سورس دیگری را به نام Kestrel نیز منتشر کرده است.
وب‌سرور Kestrel مبتنی بر پروژه libuv است و libuv در اصل برای هاست کردن Node.js تولید شده بود و تأکید آن بر روی انجام عملیات I/O به صورت asynchronous است.
نکته جالب این است که یک برنامه‌ی مبتنی بر ASP.NET Core، در واقع یک Console Application است که در متد Main آن وب‌سرور فراخوانی می‌شود:
using System;
using Microsoft.AspNetCore.Hosting;
namespace aspnetcoreapp
{
public class Program
{
  public static void Main(string[] args)
  {
   var host = new WebHostBuilder()
                  .UseKestrel()
                  .UseStartup<Startup>()
                  .Build();
   host.Run();
  }
}
}

چهار: تزریق وابستگی (Dependency Injection) تو کار

تزریق وابستگی‌ها برای ایجاد وابستگی سست (loosely coupling) بین اشیاء مرتبط و وابسته به یکدیگر است. به جای نمونه‌سازی مستقیم اشیاء مرتبط، یا استفاده از ارجاع‌های ایستا به آن اشیاء، زمانی که یک کلاس به آنها احتیاج داشته باشد، با روش‌های خاصی از طریق DI به اشیاء مورد نیاز دسترسی پیدا می‌کند. در این استراتژی، ماژول‌های سطح بالا نباید به ماژول‌های سطح پایین وابسته باشند، بلکه هر دو باید به abstraction (معمولا Interface ها) وابسته باشند.
وقتی یک سیستم برای استفاده‌ی از DI طراحی شده‌است و کلاس‌های زیادی دارد که وابستگی‌هایش را از طریق constructor یا property‌ها درخواست می‌کند، بهتر است یک کلاس مخصوص ایجاد آن کلاس‌ها و وابستگی‌هایشان داشته باشد. به این کلاس‌ها container، یا Inversion of Control (IoC) container یا Dependency Injection (DI) container گفته می‌شود.
با این روش، بدون نیاز به hard code کردن instance سازی از کلاس‌ها، می‌توان گراف‌های پیچیده وابستگی را در اختیار یک کلاس قرار داد.
طراحی ASP.NET Core از پایه طوری است که حداکثر استفاده را از Dependency Injection می‌کند. یک container ساده توکار با نام IServiceProvider وجود دارد که به صورت پیش‌فرض constructor injection را پشتیبانی می‌کند.
در ASP.NET Core با مفهومی به نام service سر و کار خواهید داشت که در واقع به type هایی گفته می‌شود که از طریق DI در اختیار شما قرار می‌گیرند. سرویس‌ها در متد ConfigureServices کلاس Startup برنامه شما تعریف می‌شوند. این service‌ها می‌توانند Entity Framework Core یا ASP.NET Core MVC باشند یا سرویس‌هایی که توسط شما تعریف شده‌اند. مثال:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices (IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
  options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>()
  .AddEntityFrameworkStores<ApplicationDbContext>()
  .AddDefaultTokenProviders();
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}


پنج: یکپارچگی با framework‌های مدرن سمت کلاینت

فرآیند build در برنامه‌های تحت وب مدرن معمولاً این وظایف را انجام می‌دهد:
  • bundle و minify کردن فایل‌های جاوا اسکریپت و همینطور CSS
  • اجرای ابزارهایی برای bundle و minify کردن قبل از هر build
  • کامپایل کردن فایل‌های LESS و SASS در CSS
  • کامپیال کردن فایل‌های CoffeeScript و TypeScript در JavaScript
برای اجرای چنین فرآیندهایی از ابزاری به نام task runner استفاده می‌شود که Visual Studio از دو ابزار task runner مبتنی برا جاوا اسکریپت به نام‌های Gulp و Grunt بهره می‌برد. از این ابزارها با استفاده از ASP.NET Core Web Application template می‌توان در ASP.NET Core استفاده کرد.
همچنین امکان استفاده از Bower که یک package manager (مانند NuGet) برای وب است، وجود دارد. معمولاً از Bower برای نصب فایل‌های CSS ، فونت‌ها، framework‌های سمت کلاینت و کتابخانه‌های جاوا اسکریپت استفاده می‌شود. اگرچه بسیاری از package‌ها در NuGet هم وجود دارند، اما تمرکز بیشتر NuGet بر روی کتابخانه‌های دات‌نتی است.
نصب و استفاده‌ی سایر library‌های سمت کلاینت مانند Bootstrap ، Knockout.js ، Angular JS  و زبان TypeScript نیز در Visual Studio و هماهنگی آن با ASP.NET Core نیز بسیار ساده است.
پس همین حالا دست به کار شوید و با نصب -حداقل - Microsoft Visual Studio 2015 Update 3 بر روی ویندوز یا Visual Studio Code  بر روی هر سیستم عاملی از برنامه‌نویسی با ASP.NET Core لذت ببرید!
منابع :
مطالب دوره‌ها
کار با AutoMapper زمانیکه نوع منبع داده مورد استفاده مشخص نیست
در سناریوهای متداول نگاشت اشیاء، مشخص است که نوع ViewModel برنامه چیست و معادل Model آن کدام است. اما حالت‌هایی مانند کار با anonymous objects و یا data reader و data table و امثال آن نیز وجود دارند که در این حالت‌ها، نوع منبع داده‌ی مورد استفاده، شیء مشخصی نیست که بتوان آن‌را در قسمت CreateMap مشخص کرد. برای مدیریت یک چنین حالت‌هایی، متد DynamicMap طراحی شده‌است.

مثال اول: تبدیل یک DataTable به لیست جنریک معادل

فرض کنید یک DataTable را با ساختار و داده‌های ذیل در اختیار داریم:
var dataTable = new DataTable("SalaryList");
dataTable.Columns.Add("User", typeof (string));
dataTable.Columns.Add("Month", typeof (int));
dataTable.Columns.Add("Salary", typeof (decimal));
 
var rnd = new Random();
for (var i = 0; i < 200; i++)
  dataTable.Rows.Add("User " + i, rnd.Next(1, 12), rnd.Next(400, 2000));
نوع این DataTable کاملا پویا است و می‌تواند هربار در قسمت‌های مختلف برنامه تعریف متفاوتی داشته باشد.
در ادامه معادل کلاس ساختار ستون‌های این DataTable را به صورت ذیل تهیه می‌کنیم.
public class SalaryList
{
  public string User { set; get; }
  public int Month { set; get; }
  public decimal Salary { set; get; }
}
اکنون می‌خواهیم اطلاعات DataTable را به لیستی جنریک از SalaryList نگاشت کنیم. برای اینکار تنها کافی است از متد DaynamicMap استفاده نمائیم:
var salaryList = AutoMapper.Mapper.DynamicMap<IDataReader, List<SalaryList>>(dataTable.CreateDataReader());
منبع داده را از نوع IDataReader بر اساس متد CreateDataReader مشخص کرده‌ایم. به این ترتیب AutoMapper قادر خواهد بود تا اطلاعات این DataTable را به صورت خودکار پیمایش کند. سپس مقصد را نیز لیست جنریکی از کلاس SalaryList تعیین کرده‌ایم. مابقی کار را متد DynamicMap انجام می‌دهد.
کار با AutoMapper نسبت به راه حل‌های Reflection متداول بسیار سریعتر است. زیرا AutoMapper از مباحث Fast reflection به صورت توکار استفاده می‌کند.


مثال دوم: تبدیل لیستی از اشیاء anonymous به لیستی جنریک

در اینجا قصد داریم یک شیء anonymous را به شیء معادل SalaryList آن نگاشت کنیم. این‌کار را نیز می‌توان توسط متد DynamicMap انجام داد:
var anonymousObject = new
{
  User = "User 1",
  Month = 1,
  Salary = 100000
};
var salary = Mapper.DynamicMap<SalaryList>(anonymousObject);
و یا نمونه‌ی دیگر آن تبدیل یک لیست anonymous به معادل جنریک آن است که به نحو ذیل قابل انجام است:
var anonymousList = new[]
{
  new
  {
   User = "User 1",
   Month = 1,
   Salary = 100000
  },
  new
  {
   User = "User 2",
   Month = 1,
   Salary = 300000
  }
};
var salaryList = anonymousList.Select(item => Mapper.DynamicMap<SalaryList>(item)).ToList();
این نکته در مورد حاصل کوئری‌های LINQ یا IQueryable‌ها نیز صادق است.


مثال سوم: نگاشت پویا به یک اینترفیس

فرض کنید یک چنین اینترفیسی، در برنامه تعریف شده‌است و همچنین دارای هیچ نوع پیاده سازی هم در برنامه نیست:
public interface ICustomerService
{
  string Code { get; set; }
  string Name { get; set; }
}
اکنون قصد داریم یک شیء anonymous را به آن نگاشت کنیم:
var anonymousObject = new
{
  Code = "111",
  Name = "Test 1"
};
var result = Mapper.DynamicMap<ICustomerService>(anonymousObject);
در این حالت خاص، AutoMapper با استفاده از یک Dynamic Proxy به نام LinFu (که با اسمبلی آن Merge شده‌است)، پیاده سازی پویایی را از اینترفیس مشخص شده تهیه کرده و سپس کار نگاشت را انجام می‌دهد.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: 
AM_Sample05.zip
مطالب
ویرایش قالب پیش فرض Add View در ASP.NET MVC برای سازگار سازی آن با Twitter bootstrap
همانطور که در مطلب «اعمال کلاس‌های ویژه اعتبارسنجی Twitter bootstrap به فرم‌های ASP.NET MVC» ملاحظه کردید، برای سازگار سازی یک فرم جدید ایجاد شده ASP.NET MVC با پیش فرض‌های Twitter bootstrap، حداقل 8 مرحله باید طی شود و ... چقدر خوب می‌شد اگر این‌کارها به صورت خودکار توسط VS.NET بجای قالب پیش فرض ایجاد فرم آن، تولید می‌شد. در ادامه قصد داریم این سفارشی سازی را انجام دهیم.


مراحل کلی سفارشی سازی قالب‌های Scaffolding پیش فرض ASP.NET MVC

قالب‌های Scaffolding پیش فرض ASP.NET در مسیر Microsoft Visual Studio X\Common7\IDE\ItemTemplates\CSharp\Web\MVC X\CodeTemplates قرار دارند. برای نمونه اگر بخواهیم پیش فرض‌های تولید فرم‌های MVC4 را تغییر دهیم، باید به پوشه MVC 4\CodeTemplates\AddView\CSHTML مراجعه و فایل Create.tt را ویرایش کنیم.
اینکار هرچند عملی است اما آنچنان جالب نیست؛ از این جهت که تاثیری کلی و سراسری خواهد داشت.
برای اعمال محلی این تغییرات فقط به یک پروژه خاص، تنها کافی است همین مسیر CodeTemplates\AddView\CSHTML به همراه تمام فایل‌های tt آن، در پوشه جاری پروژه مدنظر ما کپی شود. به این ترتیب ابتدا به این پوشه محلی مراجعه خواهد شد.
روش دوم کپی کردن این فایل‌ها، استفاده از بسته نیوگت ذیل است:
 PM> Install-Package Mvc4CodeTemplatesCSharp


سفارشی سازی فایل Create.tt پیش فرض ASP.NET MVC جهت سازگار سازی آن با Twitter bootstrap

در اینجا قصد داریم همان 8 مرحله مطلب «اعمال کلاس‌های ویژه اعتبارسنجی Twitter bootstrap به فرم‌های ASP.NET MVC» را به فایل Create.tt که اکنون در پوشه CodeTemplates\AddView\CSHTML\Create.tt ریشه پروژه جاری قرار دارد، اعمال کنیم.
الف) ابتدا نام این فایل را به CreateBootstrapForm.tt تغییر می‌دهیم. از این لحاظ که این نام جدید در drop down مرتبط با scaffold template صفحه Add view ظاهر خواهد شد. به علاوه نیازی نیست تا این فایل tt در همان لحظه اجرا شود، بنابراین به خواص آن در VS.NET مراجعه کرده و مقدار گزینه custom tool آن‌را خالی می‌کنیم (مانند سایر فایل‌های tt اضافه شده).
ب) قسمت ابتدایی فایل CreateBootstrapForm.tt را که همان کپی مطابق اصل فایل Create.tt است، به نحو ذیل تغییر می‌دهیم:
<#
    if (!mvcHost.IsContentPage) {
#>
<script src="~/Scripts/jquery-1.9.1.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

<#
    }
}
#>
@using (Html.BeginForm()) {
    @Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" })

    <fieldset class="form-horizontal">
        <legend><#= mvcHost.ViewDataType.Name #></legend>

<#
foreach (ModelProperty property in GetModelProperties(mvcHost.ViewDataType)) {
    if (!property.IsPrimaryKey && !property.IsReadOnly && property.Scaffold) {
#>
        <div class="control-group">
<#
        if (property.IsForeignKey) {
#>
            @Html.LabelFor(model => model.<#= property.Name #>, "<#= property.AssociationName #>",new {@class="control-label"})
<#
        } else {
#>
            @Html.LabelFor(model => model.<#= property.Name #>,new {@class="control-label"})
<#
        }
#>
        
           <div class="controls">
<#
        if (property.IsForeignKey) {
#>
            @Html.DropDownList("<#= property.Name #>", String.Empty)
<#
        } else {
#>
            @Html.EditorFor(model => model.<#= property.Name #>)
<#
        }
#>
            @Html.ValidationMessageFor(model => model.<#= property.Name #>,null,new{@class="help-inline"})
</div>
        </div>

<#
    }
}
#>
<div class="form-actions">
            <button type="submit" class="btn btn-primary">ارسال</button>
            <button class="btn">لغو</button>
          </div>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>
<#
if(mvcHost.IsContentPage && mvcHost.ReferenceScriptLibraries) {
#>

@section JavaScript {    

}
که حاصل آن به صورت ذیل قابل استفاده و دسترسی خواهد بود:


دریافت فایل CreateBootstrapForm.tt اصلاح شده:
همانطور که عنوان شد، برای استفاده از آن فقط کافی است آن‌را در مسیر CodeTemplates\AddView\CSHTML\CreateBootstrapForm.tt ریشه پروژه جاری خود کپی کنید.
مطالب
طبقه بندی Bad Code Smell ها
نقل قول‌های زیادی، در مورد کیفیت کد وجود دارند. دستور العمل‌های فراوانی نیز در این راستا وجود دارند. یکی از ابزارهایی که برای نوشتن کدهایی با کیفیت مطلوب وجود دارد، مجموعه الگوهای بد کد نویسی است که به Code smell یا بوی بد کد مشهور هستند.  
بوی بد کد، نشانه‌هایی در کد هستند که حکایت از مشکلات عمیق‌تری دارند. بوی بد کد مساوی با باگ نیست. ولی خطر افزایش باگ‌ها و یا مشکلاتی را در آینده، به دنبال خواهند داشت. بوی بد کد معمولا حاصل رعایت نکردن یک سری اصول اولیه برنامه نویسی و یا طراحی شیء گرا هستند. 
برای بهبود کیفیت نرم افزار در دراز مدت نیاز است موارد بوی بد کد به دقت بررسی و رفع شوند. رفع شدن آنها ریسک انباشته شدن بوی بد کد را در پروژه کم خواهد کرد. یکی از فواید جلوگیری از انباشته شدن چنین الگوهای بدی در پروژه، بهبود فرآیند نگهداشت آن می‌باشد که موضوعی بسیار مهم برای چابکی یک تیم نرم افزاری است. 
هنگام مشاهده‌ی بوی بد، در بخشی از کدها، معمولا اولین اقدام، رفع آن است (Refactoring). در فرآیند رفع آن ممکن است الگوهای بد دیگری در کد یافت شوند که با آنها نیز به همین صورت برخورد خواهد شد. 
انوع بوهای بد کد به دسته‌های زیر طبقه بندی می‌شوند. 

کدهای متورم (Bloaters) 


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

1 - متدهای بلند (Long method): در این الگوی بد، متدها تعداد خطهای زیادی از کد را شامل می‌شوند. به طور معمول متدهایی با تعداد خطوط بیشتر از 10 خط، متدهای بلند محسوب می‌شوند. نکته قابل توجه این است که هیچ کس متدی را با تعداد خطوط زیاد طراحی نمی‌کند! معمولا به مرور زمان تعداد خط‌های یک متد افزایش می‌یابند. 
2 - کلاس‌های بزرگ (Large class): کلاسی که تعداد فیلدها، متدها و خطوط کد زیادی دارد. 
3 - وسواس استفاده از متغیرهای داده‌ای اولیه (Primitive obsession): این بوی بد معمولا به سه شکل بروز می‌کند. 
  • استفاده از متغیرهای اولیه بجای ساختارهای کوچک برای کارهای اولیه مانند Currency, DateTime, PhoneNumber 
  • استفاده از constant‌ها برای کد کردن اطلاعات مانند USER_ADMIN_ROLE = 1 
  • استفاده از constant‌های رشته‌ای به عنوان نام فیلدها در آرایه‌های داده 
4 - تعداد پارامترهای زیاد متد (Long parameter list): تعداد پارامترهای بیشتر از سه یا چهار عدد در یک متد. 
5 - توده داده (Data clumps): در بعضی موارد ممکن است از متغیرها به صورت دسته‌ای در مکان‌های مختلف کد استفاده شود. مانند استفاده از دسته‌ای از متغیرها برای نگه داشتن اطلاعات مربوط به اتصال پایگاه داده. این دسته‌ها باید به کلاس‌های حمل کننده داده خود تغییر کنند. 
 

بد استفاده کنندگان از شیء گرایی (Object orientation abusers)  


تکه کدهای این بخش در واقع بد استفاده کنندگاه یا ناقص استفاده کنندگان از اصول شیء گرایی هستند. در این دسته بندی موارد زیر وجود دارند:  

1 - گذاره‌های switch: وجود یک گذاره switch پیچیده یا دنباله‌ای از گذاره‌های if  
2 - درخواست رد شده (Refused request): در این حالت یک کلاس مجموعه محدودی از اعضای کلاس پدر خود را پیاده سازی می‌کند و باقی اعضای کلاس پدر یا بدون استفاده می‌مانند یا با استفاده از پرتاب کردن استثناء (Exception throwing) از کار انداخته می‌شوند. 
3 - فیلد موقتی (Temporary field): در این حالت متغیرها مقدار خود را در شرایط خاصی می‌گیرند و در بقیه شرایط خالی هستند. 
4 - کلاس هایی دقیقا مشابه در کارایی ولی متفاوت در مشخصات (Alternative Classes with Different Interfaces): دو کلاس دقیقا یک کار را انجام می‌دهند ولی نام اعضای آنها (متد و ...) متفاوت است. 

جلوگیری کنندگان از تغییر(Change preventers) 


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

1 - تغییر واگرا (Divergent change): این حالت زمانی اتفاق می‌افتد که برای اعمال یک تغییر به کلاس نیاز است متدهای زیادی را تغییر دهید. به طور مثال به ازای هر نوع محصولی که به محصولات شما اضافه می‌شود باید متدهای ذخیره، بازیابی، جستجو را تغییر دهید. 
2 - Shotgun Surgery: این حالت شباهت زیادی به تغییر واگرا دارد. تنها تفاوت آن این است که در این حالت شما به ازای هر تغییر نیاز است کلاس‌های زیادی را تغییر دهید. تغییر واگرا در بدنه یک کلاس اتفاق می‌افتد. 
3 - سلسله مراتب موازی ارث بری (Parallel inheritance hierarchy): این مورد یکی کمتر درک شده‌ترین موارد است. در این حالت زمانی که یک زیر کلاس برای یک کلاس ایجاد می‌کنید به ازای آن ناخودآگاه مجبور می‌شوید یک زیر کلاس برای کلاس دیگری ایجاد کنید. 

کدهای غیر ضروری (Dispensables) 


این دسته از کدها معمولا کدهایی هستند بی دلیل و بی استفاده. کدهایی که نبودنشان بهتر از بودنشان است! حذف کردن این کدها به خوانایی و قابلیت نگهداری کد خواهد افزود. بوهای بدی که در این دسته بندی قرار دارند به صورت زیر می‌باشند: 

1 - کامنت: یک متد، با مقادیر فراوانی از کامنت‌های توضیحی پر شده است. 
2 - کد تکراری: در این بوی بد، دو قطعه کد دقیقا مانند یکدیگر هستند. 
3 - کلاس داده (ِData class): کلاس‌هایی که تنها فیلدهای اطلاعاتی در آنها وجود دارند و متدهای خامی که جهت دریافت یا ذخیره اطلاعات در آنها استفاده می‌شوند. این کلاس‌های معمولا هیچ روال منطقی ای در خود ندارند. یکی از قدرت‌های شیء گرایی افزودن رفتار به کلاس‌ها در کنار اقلام اطلاعاتی موجود در آن است.  
4 - کلاس تنبل (Lazy class):  اگر کلاس کار چندانی که درخور نگهداری و توسعه باشد، انجام نمی‌دهد بهتر است از بین برود. 
5 - کد مرده (Dead code): متغیر، پارامتر، متد یا کلاسی که دیگر هیچ استفاده‌ای از آن متصور نیست و هیچ استفاده‌ای در حال حاضر از آن وجود ندارد. 
6 - کلی نگری بیش از اندازه (Speculative Generality): این الگو نیز کدهایی را شامل می‌شود که بلااستفاده هستند. ولی دلیل بلااستفاده بودن آن کلی نگری و دور اندیشی بدون دلیل است. معمولا کدهای تولیدی برای شرایط فعلی و پیش‌بینی آینده تولید می‌شوند. اگر این پیش‌بینی آینده به درستی و بر مبنای واقعیات انجام نشود، معمولا نتیجه کار، طراحی و پیاده سازی ای بی فایده و بلااستفاده خواهد بود. 

کدهایی بیش از اندازه وابسته به هم (Couplers) 


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

1- متد حسود (Feature envy): متدی که از اعضای یک شیء دیگر بیشتر از اعضای کلاس خود استفاده می‌کند! این اتفاق معمولا زمانی می‌افتد که فیلدهایی به یک "کلاس داده" منتقل می‌شوند. وقتی این اتفاق می‌افتد یکی از راه حل‌ها، انتقال روالهای استفاده کننده از فیلدها به "کلاس داده" مربوطه است.
2 - کلاس‌های بیش از اندازه صمیمی (Inappropriate Intimacy): کلاس‌ها از اعضای internal یکدیگر بیش از اندازه استفاده می‌کنند. کلاس‌های خوب کلاس‌هایی هستند که کمترین اطلاعی را از وضعیت داخلی یکدیگر دارند. 
3 - کتابخانه‌های ناقص (Incomplete Library Class): زمانیکه کتابخانه‌ای آماده می‌شود، بالاخره روزی می‌رسد که این کتابخانه نیازهای پروژه را رفع نمی‌کند و نیاز به توسعه خواهد داشت. ولی از آنجایی که کتابخانه‌ها به صورت فقط خواندنی در اختیار پروژه‌ها قرار می‌گیرند، در صورتیکه توسعه دهنده اصلی آن از توسعه کتابخانه سر باز بزند، مشکلاتی بوجود خواهد آمد. 
4 - زنجیره فراخوانی‌ها (Message chain): زمانیکه یک متد در بدنه خود پیامی به شیء دیگری می‌فرستد که آن شیء نیز به خودی خود پیامی به شیء دیگری می‌فرستد (و الی آخر) یک زنجیره فراخوانی بوجود آمده است. در این روش بیرونی‌ترین استفاده کننده از متد در واقع وابسته به یک زنجیره‌ای از فراخوانی‌ها است که تغییر در هر قدمی از آن باعث خرابی خواهد شد. 
5 - دلال (Middle man): اگر کلاسی تنها کاری که انجام می‌دهد انتقال فراخوانی به کلاس دیگری است، دیگر نیازی به این کلاس وجود نخواهد داشت.

اطلاع از الگوهای بد کد نویسی به همان اندازه اطلاع از الگوهای خوب کد نویسی در کیفیت محصول تولیدی اثر مثبت خواهند داشت. یادگیری طبقه بندی شده این الگوها کار را برای استفاده روزمره از آنها آسان‌تر خواهد کرد.
مطالب
نحوه استخراج آیکون‌های یک قلم در WPF
مطلب «نحوه نمایش تمام آیکون‌های تعریف شده در یک قلم در WPF» را در نظر بگیرید. سؤال: اگر در یک برنامه تنها به تعدادی از این آیکون‌ها یا گلیف‌ها نیاز بود آیا می‌توان این‌ها را به صورت مجزا استخراج و استفاده کرد؟
پاسخ: بلی. همان کلاس  FontFamily موجود در اسمبلی PresentationCore.dll، امکان تبدیل یک گلیف را به معادل هندسی آن نیز دارد. در ادامه کدهای آن‌را مرور خواهیم کرد:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Windows;
using System.Windows.Media;
using CrMap.Models;

namespace CrMap.ViewModels
{
    public class CrMapViewModel
    {
        public IList<Symbol> Symbols { set; get; }
        public int GridRows { set; get; }
        public int GridCols { set; get; }

        public CrMapViewModel()
        {
            fillDataSource();
        }

        private void fillDataSource()
        {
            Symbols = new List<Symbol>();
            GridCols = 15;

            var fontFamily = new FontFamily(new Uri("pack://application:,,,/"), "/Fonts/#whhglyphs");

            GlyphTypeface glyph = null;
            Typeface glyphTypeface = null;
            foreach (var typeface in fontFamily.GetTypefaces())
            {
                if (typeface.TryGetGlyphTypeface(out glyph) && (glyph != null))
                {
                    glyphTypeface = typeface;
                    break;
                }
            }

            if (glyph == null)
                throw new InvalidOperationException("Couldn't find a GlyphTypeface.");

            GridRows = (glyph.CharacterToGlyphMap.Count / GridCols) + 1;

            foreach (var item in glyph.CharacterToGlyphMap)
            {
                var index = item.Key;
                Symbols.Add(new Symbol
                {
                    Character = Convert.ToChar(index),
                    CharacterCode = string.Format("&#x{0:X}", index)
                });

                saveToFile(glyphTypeface, index);
            }
        }

        private static void saveToFile(Typeface glyphTypeface, int index)
        {
            var formattedText = new FormattedText(
                                        textToFormat: Convert.ToChar(index).ToString(),
                                        culture: new CultureInfo("en-us"),
                                        flowDirection: FlowDirection.LeftToRight,
                                        typeface: glyphTypeface,
                                        emSize: 20,
                                        foreground: Brushes.Black);
            var geometry = formattedText.BuildGeometry(new Point(0, 0));
            var path = geometry.GetFlattenedPathGeometry();
            File.WriteAllText(index + ".path", path.ToString());
        }
    }
}
در اینجا تنها متد saveToFile در مقایسه با قسمت قبل افزوده شده است.
شیء FormattedText دارای متدی است به نام BuildGeometry که اطلاعات یک گلیف را تبدیل به معادل هندسی آن می‌کند. سپس توسط GetFlattenedPathGeometry معادل Path آن‌را می‌توان بدست آورد. برای مثال اگر پس از اجرای این مثال، به فایل 48.path تولیدی آن مراجعه کنیم، چنین خروجی را می‌توان مشاهده کرد:
 F1M5,7.47150993347168L5,17.2566661834717 5.732421875,19.0242443084717 7.5,19.7566661834717
12.5,19.7566661834717 13.69140625,19.4441661834717 5,7.47150993347168z
M7.5,4.75666618347168L6.30859375,5.06916618347168 15,17.0418224334717
15,7.25666618347168 14.267578125,5.48908805847168 12.5,4.75666618347168
7.5,4.75666618347168z M7.5,2.25666618347168L12.5,2.25666618347168
14.4189453125,2.62287712097168 16.03515625,3.72150993347168 17.1337890625,5.33772134780884
17.5,7.25666618347168 17.5,17.2566661834717 17.1337890625,19.1756114959717
16.03515625,20.7918224334717 14.4189453125,21.8904552459717 12.5,22.2566661834717
7.5,22.2566661834717 5.5810546875,21.8904552459717 3.96484375,20.7918224334717
2.8662109375,19.1756114959717 2.5,17.2566661834717 2.5,7.25666618347168 2.8662109375,5.33772134780884
3.96484375,3.72150993347168 5.5810546875,2.62287712097168 7.5,2.25666618347168z
که برای استفاده از اطلاعات آن در WPF می‌توان نوشت:
<Path Stroke="DarkRed" Fill="Black" Data="F1M5,7.47150993347168L5,17.2566661834717
              5.732421875,19.0242443084717 7.5,19.7566661834717 12.5,19.7566661834717 13.69140625,19.4441661834717 
              5,7.47150993347168z M7.5,4.75666618347168L6.30859375,5.06916618347168 15,17.0418224334717 
              15,7.25666618347168 14.267578125,5.48908805847168 12.5,4.75666618347168 7.5,4.75666618347168z 
              M7.5,2.25666618347168L12.5,2.25666618347168 14.4189453125,2.62287712097168 
              16.03515625,3.72150993347168 17.1337890625,5.33772134780884 17.5,7.25666618347168 
              17.5,17.2566661834717 17.1337890625,19.1756114959717 16.03515625,20.7918224334717 
              14.4189453125,21.8904552459717 12.5,22.2566661834717 7.5,22.2566661834717 
              5.5810546875,21.8904552459717 3.96484375,20.7918224334717 2.8662109375,19.1756114959717 
              2.5,17.2566661834717 2.5,7.25666618347168 2.8662109375,5.33772134780884 
              3.96484375,3.72150993347168 
              5.5810546875,2.62287712097168 7.5,2.25666618347168z" />
مطالب
بررسی مفاهیم Covariant و Contravariant در زبان سی‌شارپ
یکی از مفاهیمی که بنظر پیچیده می‌آمد و هر دفعه موقع مطالعه از آن فرار می‌کردم، همین بحث COVARIANCE و CONTRAVARIANCE بود. در اینجا قصد دارم به زبان ساده این مفاهیم را شرح دهم.

Covariance 
A را در نظر بگیرید که قابل تبدیل به B باشد. در اینصورت X، دارای پارامتر کواریانس است اگر <X<A قابل تبدیل به <X<B باشد. بدون ذکر مثال شاید این تعریف خیلی ملموس نباشد. پس بهتر است با ذکر مثال به تشریح مفاهیم بپردازیم.
نکته: در اینجا منظور از قابل تبدیل بودن، قابل تبدیل بودن به صورت ضمنی (implicit) می‌باشد. برای مثال A از B ارث بری داشته باشد و یا A، تایپ B را پیاده سازی کند (در صورتی که B یک اینترفیس باشد). تبدیلات عددی، Boxing و تبدیلات کاستوم مجاز نیستند.
برای نمونه نوع <IFoo<T پارامتر کوواریانس T دارد، اگر کد زیر معتبر باشد:
IFoo<string> s = ...;
IFoo<object> b = s;
از C# 4.0، اینترفیسها و delegateها مجاز به استفاده از پارامتر کوواریانس T هستند؛ اما در مورد کلاس‌ها اینطور نیست. آرایه‌ها نیز مجاز هستند که در ادامه تشریح خواهند شد (اگر A قابل تبدیل به B باشد در اینصورت []A قابل تبدیل به []B خواهد بود. هر چند ممکن است به run-time exception منجر گردد که ظاهرا این پشتیبانی آرایه‌ها از پارامترهای کوواریانس دلایل تاریخی دارد!).

Variance is not automatic
برای حصول اطمینان از static type safety، پارامترها به صورت پیش فرض variant نمی‌باشند:
class Animal {}
class Bear : Animal {}
class Camel : Animal {}
public class Stack<T>
{
   int position;
   T[] data = new T[100];
   public void Push (T obj) => data[position++] = obj;
   public T Pop() => data[--position];
}
کد زیر کامپایل نخواهد شد:
Stack<Bear> bears = new Stack<Bear>();
Stack<Animal> animals = bears; // Compile-time error 
دلیل اینکه کد فوق کامپایل نمی‌شود، در کد زیر آورده شده است:
animals.Push (new Camel()); // Trying to add Camel to bears
اگر کامپایل انجام می‌شد، کد بالا در زمان اجرا خطا صادر می‌کرد؛ چرا که نوع واقعی animals، در واقع <Stack<Bear بوده و نمی‌توان به آن، شیء ای از جنس Camel اضافه کرد. عدم پشتیبانی از کوواریانس، به هرحال مانع از امکان استفاده مجدد (re-usability) خواهد شد. برای مثال فرض کنید می‌خواهیم متدی بنویسیم که وظیفه آن صادر کردن دستور شستن حیوانات موجود در پشته باشد:
public class ZooCleaner
{
  public static void Wash (Stack<Animal> animals) {...}
}
فراخوانی متد Wash با پارامتری از جنس <Stack<Bear در زمان کامپایل خطا خواهد داد (اعمال این محدودیت منطقی است. برای مثال ممکن است مثلا در بدنه متد Wash با استفاده از متد Pop کلاس Stack یک Animal برداشته شده و به Camel کست گردد که با توجه به نوع اصلی آن (Bear) خطای run-time صادر خواهد شد. اما به هرحال محدودیت ایجاد شده، جلوی خطاهایی که ممکن است در run-time اتفاق بیافتد را می‌گیرد). 
یک راه حل برای این موضوع، تعریف متد Wash به صورت جنریک و با constraint است:
class ZooCleaner
{
  public static void Wash<T> (Stack<T> animals) where T : Animal { ... }
}
با کد فوق می‌توان متد Wash را به صورت زیر فراخوانی نمود:
Stack<Bear> bears = new Stack<Bear>();
ZooCleaner.Wash(bears);
کامپایلر، ورژن جنریک متد Wash را کامپایل میکند. در این حالت میتوان با چک کردن نوع واقعی T و کست کردن به آن نوع، عملیات را بدون خطا انجام داد.
نکته: اگر reusable بودن مد نظر نبود، باید برای هر sub-type از Animal یک متد جداگانه Wash مینوشتیم (یکی برای Bear، یکی برای Camel،...).

راه حل دیگر این است که کلاس <Stack<T یک اینترفیس با پارامتر covariant پیاده سازی نماید که در ادامه به این مورد بازخواهیم گشت.

Arrays 
آرایه‌ها از covariance پشتیبانی می‌کنند. برای مثال:
Bear[] bears = new Bear[3];
Animal[] animals = bears; // OK
این مورد باعث ایجاد قابلیت استفاده مجدد می‌شود؛ به قیمت اینکه ممکن است چنین خطاهایی ایجاد شوند:
animals[0] = new Camel(); // Runtime error

Declaring a covariant type parameter  
از C# 4.0 و بالاتر، پارامترهای اینترفیسها و delegateها می‌توانند با استفاده از کلمه کلیدی out از covariance پشتیبانی کنند؛ یا به زبان ساده‌تر covariant گردند. در این صورت برخلاف آرایه‌ها از type safety اطمینان کامل خواهیم داشت.
برای نشان دادن این مورد، در کلاس <Stack<T اینترفیس زیر را پیاده سازی می‌کنیم:
public interface IPoppable<out T> { T Pop(); }
کلمه کلیدی out نشان می‌دهد که T فقط در موقعیت خروجی مورد استفاده واقع می‌گردد (برای مثال نوع برگشتی یک متد). این مورد سبب می‌شود تا پارامتر covariant باشد و کد زیر کامپایل گردد:
var bears = new Stack<Bear>();
bears.Push (new Bear());
// Bears implements IPoppable<Bear>. We can convert to IPoppable<Animal>:
IPoppable<Animal> animals = bears; // Legal
Animal a = animals.Pop();
در اینجا کامپایلر اجازه تبدیل bears را به animals می‌دهد. چرا که موردی که کامپایلر از آن جلوگیری می‌کرد (Push کردن Camel به Stack با اعضایی از جنس Bear) در اینجا نمی‌تواند رخ دهد. چرا که در اینجا پارامتر T فقط می‌تواند به عنوان خروجی استفاده گردد و امکان Push کردن وجود ندارد.

نکته: پارامترهای متدی که مزین به کلمه کلیدی out شده‌اند، واجد شرایط covariant بودن نمی‌باشند (به دلیل وجود محدودیتی در CLR).

با استفاده از کد زیر قابلیت استفاده مجددی که در ابتدا بحث کردیم فراهم می‌شود:
public class ZooCleaner
{
 public static void Wash (IPoppable<Animal> animals) { ... } //cast covariantly to solve the reusability problem 
}

نکته: Covariance (و contravariance) فقط در موارد تبدیل ارجاعی کار می‌کنند (نه تبدیل boxing). بنابراین اگر متدی داشته باشیم که دارای پارامتری از جنس IPoppa
<ble<object باشد، امکان فراخوانی آن متد با ورودی از جنس <IPoppable<string وجود دارد؛ اما پاس دادن متغیر از جنس <IPoppable<int امکانپذیر نمی‌باشد.

Contravariance   
در تعریف covaraince داشتیم:  A را در نظر بگیرید که قابل تبدیل به B باشد. در اینصورت X، دارای پارامتر کواریانس است اگر <X<A قابل تبدیل به <X<B باشد.  Contravariance 
زمانی است که تبدیل در جهت عکس صورت گیرد (تبدیل از <X<B به <X<A). این مورد فقط برای پارامترهای ورودی صحیح است و با کلمه کلیدی in تعیین می‌گردد. با استفاده از پیاده سازی اینترفیس:
public interface IPushable<in T> { void Push (T obj); }
می‌توانیم کد زیر را بنویسیم:
IPushable<Animal> animals = new Stack<Animal>();
IPushable<Bear> bears = animals; // Legal
bears.Push (new Bear());
هیچ عضوی از اینترفیس IPushable خروجی T را بر نمی‌گرداند و لذا با casting اشتباه، مواجه نخواهیم شد (برای نمونه از طریق این اینترفیس راهی برای Pop کردن نداریم).
توجه: کلاس <Stack<T هر دو اینترفیس <IPushable<T و <IPoppable<T را پیاده سازی کرده است (با وجود اینکه T هم out است و هم in). اما این مورد مشکلی ایجاد نمی‌کند. زیرا قبل از تبدیل، ارجاعی فقط به یکی از اینترفیسها صورت می‌گیرد (نه همزمان به هردو!). این مورد نشان می‌دهد که چرا class‌ها از پارامترهای variant پشتیبانی نمی‌کنند. 

برای مثال اینترفیس زیر را در نظر بگیرید:
public interface IComparer<in T>
{
// Returns a value indicating the relative ordering of a and b
  int Compare (T a, T b);
}
از آنجاییکه T در اینجا contravariant است می‌توان از <IComparer<object برای مقایسه دو string استفاده نمود:
var objectComparer = Comparer<object>.Default;
// objectComparer implements IComparer<object>
IComparer<string> stringComparer = objectComparer;
int result = stringComparer.Compare ("Hashem", "hashem");


برای مطالعه‌ی بیشتر
Covariant and Contravariant  
مطالب
پایان پروژه ASP.NET Ajax Control Toolkit !

بله! همانطور که حدس زده می‌شد بالاخره مایکروسافت تکلیف خودش را با کتابخانه‌های Ajax ایی تولید شده در طی این چند سال مشخص کرد و از این پس انتخاب اصلی جهت تولید برنامه‌های ASP.NET مبتنی بر Ajax ، تنها jQuery است.
اصل مطلب رو می‌تونید اینجا مطالعه کنید:



خلاصه آن:
  • ASP.NET AJAX در آینده نیز کاملا پشتیبانی می‌شود، اما شهروند درجه یک محسوب نخواهد شد؛ مانند استفاده از ScriptManager و UpdatePanel
  • Ajax Control Toolkit که اکنون به صورت سورس باز در سایت CodePlex نیز قابل دریافت است، منسوخ شده در نظر گرفته شده و تنها در آینده شاید هر از چندگاهی به رفع باگ‌های گزارش شده در آن پرداخته شود (اما دیگر به صورت فعال توسعه داده نخواهد شد).
  • Microsoft Ajax Library / ASP.NET Ajax Library هر چند تشابه اسمی با ASP.NET Ajax دارند اما جزئی از کتابخانه‌ی AJAX Control Toolkit بوده و این مورد نیز از این پس منسوخ شده در نظر گرفته خواهد شد (مانند استفاده از DataView یا Sys.require ).

نظرات مطالب
آشنایی با NHibernate - قسمت دهم
سلام،
خودتون هم می‌تونید اینکار را انجام بدید.
یک فایل در رپیدشیر آپلود کنید. سپس یک لینک به شما می‌دهد جهت ایجاد collectors account . این اکانت کالکتور را که ایجاد کردید، امکان remote upload هم دارد. (این نوع اکانت‌ها پولی نیست اما امکانات خوبی دارد و کار راه انداز است)