نظرات مطالب
MVVM و فراخوانی متدهای اشیاء View از طریق ViewModel
سلام استاد عزیز
من یک برنامه نویس آماتور بودم که طی سه سال آشنایی با وبلاگ شما سطح خود را بالا بردم . طی این سالها به ندرت اتفاق افتاده که روزی مطالب شما رو نخونده باشم. بعضی از مطالب رو هم چندین و چند بار خوندم.NH رو از شما یاد گرفتم و در کل بهترین استاد کامپیوتر زندگی من هستید. من حاضرم برای استفاده از مطالب شما هزینه پردازم. بسته شدن این وبلاگ یعنی مرگ برنامه نویسان نیمه حرفه ای و حرفه ای.
نظرات نظرسنجی‌ها
علت ترک آخرین محیط کاری که در آن مشغول بودید
اکثر شرکت‌های که کار کردم 2 مشکل بزرگ دارن:
1: مشکل مدیریتی
2: مشکل مدیرفنی
خیلی از شرکت‌ها چون در این 2 موضوع دارای مشکل هستن قاعدتا کدهای کثیف و زیبایی ندارن و همچنین عدم برنامه ریزی صحیح در طول یک یا چند سال باعث میشه تعدد برنامه نویس زیاد بشه و نشه یک پروژه خوب رو پیاده سازی کرد.
نظرات مطالب
EF Code First #15
نمونه‌ای از پیاده سازی اتصال به اوراکل 11g در Entity Framework 6 بوسیله پروایدر تجاری شرکت devart:

  1. ابتدا نسخه آزمایشی dotconnect for oracle 8.2 professional را از این آدرس دریافت و آن را نصب می‌کنیم.
  2. نصب آخرین نسخه Entity Framework از طریق پاور شل نیوگت.
  3. افزودن Devart.Data و Devart.Data.Oracle و Devart.Data.Oracle.Entity به Solution.
  4. حذف تگ defaultConnectionFactory در entityFramework.
  5. افزودن تگ زیر در قسمت providers همانند کد زیر:
<provider invariantName="Devart.Data.Oracle" type="Devart.Data.Oracle.Entity.OracleEntityProviderServices, Devart.Data.Oracle.Entity, Version=8.2.100.6, Culture=neutral, PublicKeyToken=09af7300eec23701" />
تکمیلی: اصول کلی دسترسی به اوراکل به شرح بالاست، ولی نکته مهم مقداردهی به خصیصه Version=X.X.X.X با توجه به نسخه اسمبلی Devart.Data.Oracle.Entity می‌باشد.
dotConnect for Oracle 
dotConnect for Oracle 
مطالب
قابلیت Templated Razor Delegate
Razor دارای قابلیتی با نام Templated Razor Delegates است. همانطور که از نام آن مشخص است، یعنی Razor Template هایی که Delegate هستند. در ادامه این قابلیت را با ذکر چند مثال توضیح خواهیم داد.
مثال اول:
می‌خواهیم تعدادی تگ li را در خروجی رندر کنیم، این کار را می‌توانیم با استفاده از Razor helpers نیز به این صورت انجام دهیم:
@helper ListItem(string content) {
 <li>@content</li>
}
<ul>
 @foreach(var item in Model) {
 @ListItem(item)
 }
</ul>
همین کار را می‌توانیم توسط Templated Razor Delegate به صورت زیر نیز انجام دهیم:
@{
 Func<dynamic, HelperResult> ListItem = @<li>@item</li>;
}
<ul>
 @foreach(var item in Model) {
 @ListItem(item)
 }
</ul>
برای اینکار از نوع Func استفاده خواهیم کرد. این Delegate یک پارامتر را می‌پذیرد. این پارامتر می‌تواند از هر نوعی باشد. در اینجا از نوع dynamic استفاده کرده‌ایم. خروجی این Delegate نیز یک HelperResult است. همانطور که مشاهده می‌کنید آن را برابر با الگویی که قرار است رندر شود تعیین کرده‌ایم. در اینجا از یک پارامتر ویژه با نام item استفاده شده است. نوع این پارامتر dynamic است؛ یعنی همان مقداری که برای پارامتر ورودی Func انتخاب کردیم. در نتیجه پارامتر ورودی یعنی رشته item جایگزین item@ درون Delegate خواهد شد.
در واقع دو روش فوق خروجی یکسانی را تولید میکنند. برای حالت‌هایی مانند کار با آرایه‌ها و یا Enumerations بهتر است از روش دوم استفاده کنید؛ از این جهت که نیاز به کد کمتری دارد و نگهداری آن خیلی از روش اول ساده‌تر است.

مثال دوم:
اجازه دهید یک مثال دیگر را بررسی کنیم. به طور مثال معمولاً در یک فایل Layout برای بررسی کردن وجود یک section از کدهای زیر استفاده می‌کنیم:
<header>  
    @if (IsSectionDefined("Header"))  
    {  
        @RenderSection("Header")  
    }  
    else  
    {  
        <div>Default Content for Header Section</div>  
    }  
</header>
روش فوق به درستی کار خواهد کرد اما می‌توان آن را با یک خط کد، درون ویو نیز نوشت. در واقع می‌توانیم با استفاده از Templated Razor Delegate یک متد الحاقی برای کلاس ViewPage بنویسیم؛ به طوریکه یک محتوای پیش‌فرض را برای حالتی که section خاصی وجود ندارد، نمایش دهد:
public static HelperResult RenderSection(this WebViewPage page, string name,  
    Func<dynamic, HelperResult> defaultContent)  
{  
    if (page.IsSectionDefined(name))  
    {  
        return page.RenderSection(name);  
    }  
    return defaultContent(null);  
}
بنابراین درون ویو می‌توانیم از متد الحاقی فوق به این صورت استفاده کرد:
<header>  
   @this.RenderSection("Header", @<div>Default Content for Header Section</div>)  
</header>
نکته: جهت بوجود نیامدن تداخل با نمونه اصلی RenderSection درون ویو، از کلمه this استفاده کرده‌ایم.

مثال سوم: شبیه‌سازی کنترل Repeater:
یکی از ویژگی‌های جذاب WebForm کنترل Repeater است. توسط این کنترل به سادگی می‌توانستیم یکسری داده را نمایش دهیم؛ این کنترل در واقع یک کنترل DataBound و همچنین یک Templated Control است. یعنی در نهایت کنترل کاملی بر روی Markup آن خواهید داشت. برای نمایش هر آیتم خاص داخل لیست می‌توانستید از ItemTemplate استفاده کنید. همچنین می‌توانستید از AlternatingItemtemplate استفاده کنید. یا اگر می‌خواستید هر آیتم را با چیزی از یکدیگر جدا کنید، می‌توانستید از SeparatorTemplate استفاده کنید. در این مثال می‌خواهیم همین کنترل را در MVC شبیه‌سازی کنیم.
به طور مثال ویوی Index ما یک مدل از نوع IEnumerable<string> را دارد: 
@model IEnumerable<string>  
@{  
    ViewBag.Title = "Test";  
}
و اکشن متد ما نیز به این صورت اطلاعات را به ویوی فوق پاس میدهد: 
public ActionResult Index()  
{  
    var names = new string[]  
    {  
        "Vahid Nasiri",  
        "Masoud Pakdel",  
        ...  
     };  
  
    return View(names);  
}
 اکنون در ویوی Index می‌خواهیم هر کدام از اسامی فوق را نمایش دهیم. اینکار را می‌توانیم درون ویو با یک حلقه‌ی foreach و بررسی زوج با فرد بودن ردیف‌ها انجام دهیم اما کد زیادی را باید درون ویو بنویسیم. اینکار را می‌توانیم درون یک متد الحاقی نیز انجام دهیم. بنابراین یک متد الحاقی برای HtmlHelper به صورت زیر خواهیم نوشت: 
public static HelperResult Repeater<T>(this HtmlHelper html,  
    IEnumerable<T> items,  
    Func<T, HelperResult> itemTemplate,  
    Func<T, HelperResult> alternatingitemTemplate = null,  
    Func<T, HelperResult> seperatorTemplate = null)  
{  
    return new HelperResult(writer =>  
    {  
        if (!items.Any())  
        {  
            return;  
        }  
        if (alternatingitemTemplate == null)  
        {  
            alternatingitemTemplate = itemTemplate;  
        }  
        var lastItem = items.Last();  
        int ii = 0;  
        foreach (var item in items)  
        {  
           var func = ii % 2 == 0 ? itemTemplate : alternatingitemTemplate;  
           func(item).WriteTo(writer);  
           if (seperatorTemplate != null && !item.Equals(lastItem))  
           {  
               seperatorTemplate(item).WriteTo(writer);  
           }  
           ii++;  
        }  
    });  
}
توضیح کدهای فوق:
خوب، همانطور که ملاحظه می‌کنید متد را به صورت Generic تعریف کرده‌ایم، تا بتواند با انواع نوع‌ها به خوبی کار کند. زیرا ممکن است لیستی از اعداد را داشته باشیم. از آنجائیکه این متد را برای کلاس HtmlHelper می‌نویسیم، پارامتر اول آن را از این نوع می‌گیریم. پارامتر دوم آن، آیتم‌هایی است که می‌خواهیم نمایش دهیم. پارامتر‌های بعدی نیز به ترتیب برای ItemTemplate، AlternatingItemtemplate و SeperatorItemTemplate تعریف شده‌اند و از نوع Delegate با پارامتر ورودی T و خروجی HelperResult هستند. در داخل متدمان یک HelperResult را برمیگردانیم. این کلاس یک Action را از نوع TextWriter از ورودی می‌پذیرد. اینکار را با ارائه یک Lambda Expression با نام writer انجام می‌دهیم. در داخل این Delegate به تمام منطقی که برای نمایش یک آیتم نیاز هست دسترسی داریم. 
ابتدا بررسی کرده‌ایم که آیا آیتم برای نمایش وجود دارد یا خیر. سپس اگر AlternatingItemtemplate برابر با null بود همان ItemTemplate را در خروجی نمایش خواهیم داد. مورد بعدی دسترسی به آخرین آیتم در Collection است. زیرا بعد از هر آیتم باید یک SeperatorItemTemplate را در خروجی نمایش دهیم. سپس توسط یک حلقه درون آیتم‌ها پیمایش میکنیم و ItemTemplate و  AlternatingItemtemplate را توسط متغیر func از یکدیگر تشخیص می‌دهیم و در نهایت درون ویو به این صورت از متد الحاقی فوق استفاده می‌کنیم: 
@Html.Repeater(Model, @<div>@item</div>, @<p>@item</p>, @<hr/>)
متد الحاقی فوق قابلیت کار با انواع ورودی‌ها را دارد به طور مثال مدل زیر را در نظر بگیرید:
public class Product
{
        public int Id { set; get; }
        public string Name { set; get; }
}
می‌خواهیم اطلاعات مدل فوق را در ویوی مربوط درون یک جدول نمایش دهیم، می‌توانیم به این صورت توسط متد الحاقی تعریف شده اینکار را به این صورت انجام دهیم:
<table>
    <tr>
        <td>Id</td>
        <td>Name</td>
    </tr>
    @Html.Repeater(Model, @<tr><td>@item.Id</td><td>@item.Name</td></tr>)
</table>

مطالب
چگونه از CodePlex به عنوان مخزنی جهت ذخیره سازی کدهای سایت یا وبلاگ خود استفاده کنیم؟

به شخصه اعتقادی ندارم که جهت مدیریت کار رایگانی که انجام می‌شود از امکانات غیر رایگان استفاده کرد. تابحال برای ذخیره سازی کدهای منتشر شده در این وبلاگ از persiangig تا googlepages مرحوم تا رپیدشیر تا ... استفاده کرده‌ام. نه امکان لیست کردن سریع آن‌ها موجود است و نه مشخص است که چه تعدادی از آن‌ها هنوز وجود خارجی داشته و از سرورهای یاد شده پاک نشده‌اند. اخیرا تعدادی وبلاگ برنامه نویسی را یافته‌ام که از سایت CodePlex به عنوان مخزنی برای ذخیره سازی کدها و مثال‌های منتشر شده در وبلاگ خود استفاده می‌کنند. این کار چند مزیت دارد:
- رایگان است (فضا، پهنای باند، اسکریپت و غیره)
- به صورت تضمینی تا 10 سال دیگر هم پابرجا است.
- درب آن به روی کاربران ایرانی باز است (برخلاف مثلا سایت googlecodes یا رفتار اخیر سورس فورج و غیره، سایت CodePlex در این چندسال رویه ثابتی داشته است)
- امکان مشاهده‌ی لیست تمامی کدهای منتشر شده‌ موجود است.
- امکان ثبت توضیحات کنار هر کد منتشر شده نیز وجود دارد.
- امکان دریافت یکجای آن‌ها با توجه به استفاده از ابزارهای سورس کنترل مهیا است.
- امکان دریافت بهینه‌ی موارد جدید هم برای کاربران وجود دارد. کاربری که یکبار با استفاده از ابزارهای سورس کنترل، کدهای موجود را دریافت کرده، در بار بعدی دریافت اطلاعات، تنها موارد تغییر کرده یا جدید را دریافت خواهد کرد و نه تمام اطلاعات کل مخزن را از ابتدا تا به امروز.
- امکان مشاهده‌ی آمار دریافت‌ها، مراجعات، سایت‌هایی که به شما لینک داده‌اند و غیره فراهم است.
- امکان دعوت کردن از افراد دیگر نیز جهت به روز رسانی مخزن کد تدارک دیده شده است.
- کلیه اعضای CodePlex بدون نیاز به عضویت در گروه مخزن کد شما، می‌توانند جهت تکمیل یا اصلاح کار شما patch یا وصله ارسال کنند.
و ...

اما برای استفاده از این امکانات نیاز است حداقل اطلاعاتی را در مورد کار با ابزارهای سورس کنترل داشت، که خلاصه‌ی مختصر و مفید آن‌را در ادامه ملاحظه خواهید نمود:
0 - دریافت و نصب برنامه‌ی TortoiseSVN
1- ثبت نام در سایت CodePlex
رایگان است.

2- ایجاد یک پروژه‌ی جدید


که به همراه وارد کردن مشخصات اولیه آن است:


تنها نکته‌ی مهم آن انتخاب سورس کنترل Team foundation server و سپس Subversion است چون می‌خواهیم با استفاده از TortoiseSVN کار به روز رسانی اطلاعات را انجام دهیم.

3- انتخاب مجوز برای پروژه در برگه‌ی License پروژه ایجاد شده

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

4- checkout کردن سورس کنترل
ابتدا به برگه‌ی source code پروژه مراجعه کرده و بر روی لینک subversion در کنار صفحه کلیک کنید.

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


در صفحه‌ی باز شده آدرس svn مربوط به پروژه خود را وارد نموده و بر روی Ok کلیک کنید:



در صفحه‌ی بعدی باید نام کاربری و کلمه‌ی عبور مرتبط با حساب کاربری سایت کدپلکس خود را وارد نمائید. همچنین بهتر است گزینه‌ی به خاطر سپاری آن‌را نیز برای سهولت کار در دفعات بعدی انتخاب کنید:



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



5- اضافه کردن فایل‌های دلخواه به مخزن کد
برای اضافه کردن کدهای مورد نظر خود، آن‌ها را به پوشه‌ی SiteRepository فوق کپی کرده و سپس بر روی آن‌ها کلیک راست نموده و گزینه‌ی Add مربوط به TortoiseSVN را انتخاب کنید:



به این صورت تنها فایل‌های مورد نظر جهت اضافه شدن به مخزن کد علامتگذاری خواهند شد (ایجاد پوشه و قرار دادن فایل‌ها درون آ‌ن‌ها نیز به همین ترتیب است):



اکنون برای تکمیل فرایند، جایی درون پوشه کلیک راست کرده و گزینه‌ی SVN Commit را انتخاب کنید:



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



پس از مدتی کار هماهنگ سازی اطلاعات با مخزن کد صورت خواهد گرفت:



همچنین آیکون فایل‌های مورد نظر نیز بر روی کامپیوتر شما به صورت زیر تغییر خواهند کرد:



6- ارائه نهایی پروژه
فراموش نکنید که پس از ایجاد یک پروژه‌ی جدید، انتخاب مجوز و ارسال فایل‌های مورد نظر، باید بر روی دکمه‌ی publish this project در بالای صفحه کلیک کرد. در غیراینصورت پروژه‌ی شما در روز بعد به صورت خودکار از سایت CodePlex حذف می‌گردد:




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


در دفعات آتی، تنها تکرار مرحله 5 یعنی کپی کردن فایل‌های مورد نظر به پوشه‌ی SiteRepository، سپس Add و در نهایت Commit آن‌ها کفایت می‌کند و نیازی به تکرار سایر مراحل نیست. عملیات هماهنگ سازی با مخزن کد هم بسیار بهینه است و تنها فایل‌هایی که اخیرا اضافه شده و هنوز ارسال نشده‌اند، Commit خواهند شد.
کاربران نهایی هم یا از طریق اینترفیس تحت وب سایت می‌توانند از فایل‌های شما استفاده کنند و یا روش دیگری هم برای این منظور وجود دارد (همان Checkout کردن یاد شده و سپس هر بار انتخاب گزینه‌ی SVN update بجای Commit جهت دریافت فایل‌های جدید و نه کل مخزن کد به صورت یکجا).

مطالب دوره‌ها
اصول طراحی یک سیستم افزونه پذیر به کمک StructureMap
قصد داریم سیستمی را طراحی کنیم که افزونه‌های خود را در زمان اجرا از مسیری خاص خوانده و سپس وهله‌های آن‌ها‌را جهت استفاده در دسترس قرار دهد. برنامه‌ای که در اینجا مورد بررسی قرار می‌گیرد، یک برنامه‌ی WinForms ساده است؛ به نام WinFormsWithPluginSupport. اما اصول کلی مطرح شده، در تمام فناوری‌های دیگر دات نتی نیز کاربرد دارد و یکسان است.


تهیه قرارداد

یک پروژه‌ی Class library به نام PluginsBase را به Solution جاری اضافه کنید. به آن اینترفیس قرار داد پلاگین‌های برنامه خود را اضافه نمائید. برای مثال:
namespace PluginsBase
{
    public interface IPlugin
    {
        string Name { get; }
        void Run();
    }
}
هر پلاگین دارای یک نام یا توضیح خاص خود خواهد بود به همراه متدی برای اجرای منطق مرتبط با آن.


تهیه سه پلاگین جدید

به Solution جاری سه پروژه‌ی مجزای Class library با نام‌های plugin1 تا 3 را اضافه کنید. در ادامه به هر پلاگین، ارجاعی را به اسمبلی PluginsBase، برای دریافت قرارداد پیاده سازی منطق پلاگین، اضافه نمائید. هدف این است که اینترفیس IPlugin، در این اسمبلی‌ها قابل دسترسی شود.
هر پلاگین هم دارای برای مثال کدهایی مانند کد ذیل خواهد بود که در آن صرفا نام آن‌ها به 2 و 3 تنظیم می‌شود.
using PluginsBase;

namespace Plugin1
{
    public class Plugin1Main : IPlugin
    {
        public string Name
        {
            get { return "Test 1"; }
        }

        public void Run()
        {
            // todo: ...
        }
    }
}


کپی خودکار پلاگین‌ها به پوشه‌ی مخصوص آن‌ها

به پروژه‌ی WinFormsWithPluginSupport مراجعه کنید. در پوشه‌ی bin\debug آن یک پوشه‌ی جدید به نام Plugins ایجاد نمائید. بدیهی است هربار که پلاگین‌های برنامه تغییر کنند نیاز است اسمبلی‌های نهایی آن‌ها را به این پوشه کپی نمائیم. اما راه بهتری نیز وجود دارد. به خواص هر کدام از پروژه‌های پلاگین مراجعه کرده و برگه‌ی Build events را باز کنید.


در اینجا قسمت post-build event را به نحو ذیل تغییر دهید:
 Copy "$(ProjectDir)$(OutDir)$(TargetName).*" "$(SolutionDir)WinFormsWithPluginSupport\bin\debug\Plugins"
این کار را برای هر سه پلاگین تکرار کنید.
به این ترتیب هربار که پلاگین جاری کامپایل شود، پس از آن به صورت خودکار به پوشه‌ی plugins تعیین شده، کپی می‌شود و دیگر نیازی به کپی دستی نخواهد بود.
تنظیم فوق، تنها اسمبلی اصلی پروژه را به پوشه‌ی bin\debug\plugins کپی می‌کند. اگر می‌خواهید تمام فایل‌ها کپی شوند، از تنظیم ذیل استفاده کنید:
 Copy "$(ProjectDir)$(OutDir)*.*" "$(SolutionDir)WinFormsWithPluginSupport\bin\debug\Plugins"


اضافه کردن وابستگی‌های اصلی پروژه‌ی WinForms

در ادامه بسته‌ی نیوگت StructureMap را به پروژه‌ی WinForms از طریق دستور ذیل اضافه کنید:
 PM> install-package structuremap
همچنین این پروژه تنها نیاز دارد ارجاع مستقیمی را به اسمبلی PluginsBase ابتدای مطلب داشته باشد. از آن، جهت تنظیمات اولیه یافتن افزونه‌ها استفاده می‌کنیم.


تعریف محل ثبت پلاگین‌ها

روش‌های متفاوتی برای کار با StructureMap وجود دارد. یکی از آن‌ها تعریف کلاسی است مشتق شده از کلاس Registry آن به نحو ذیل:
using System.IO;
using System.Windows.Forms;
using PluginsBase;
using StructureMap.Configuration.DSL;
using StructureMap.Graph;

namespace WinFormsWithPluginSupport.Core
{
    public class PluginsRegistry : Registry
    {
        public PluginsRegistry()
        {
            this.Scan(scanner =>
            {
                scanner.AssembliesFromPath(
                    path: Path.Combine(Application.StartupPath, "plugins"),
                    // یک اسمبلی نباید دوبار بارگذاری شود
                    assemblyFilter: assembly =>
                    {
                        return !assembly.FullName.Equals(typeof(IPlugin).Assembly.FullName);
                    });
                scanner.AddAllTypesOf<IPlugin>().NameBy(item => item.FullName);
            });
        }
    }
}
در اینجا مشخص کرده‌ایم که اسمبلی‌های پوشه plugins را که یک سطح پایین‌تر از پوشه‌ی اجرایی برنامه قرار می‌گیرند، خوانده و در این بین آن‌هایی را که پیاده سازی از اینترفیس IPlugin دارند، در دسترس قرار دهد.

یک نکته‌ی مهم
در قسمت assemblyFilter تعیین کرده‌ایم که اسمبلی تکراری PluginBase بارگذاری نشود. چون این اسمبلی هم اکنون به برنامه‌ی WinForms ارجاع دارد. رعایت این نکته جهت رفع تداخلات آتی بسیار مهم است. همچنین این فایل در پوشه‌ی Plugins نیز نباید حضور داشته باشد وگرنه شاهد بارگذاری افزونه‌ها نخواهید بود.


سپس نیاز به وهله سازی Container آن و معرفی این کلاس PluginsRegistry می‌باشد:
using System;
using System.Threading;
using StructureMap;

namespace WinFormsWithPluginSupport
{
    public static class IocConfig
    {
        private static readonly Lazy<Container> _containerBuilder =
            new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);

        public static IContainer Container
        {
            get { return _containerBuilder.Value; }
        }

        private static Container defaultContainer()
        {
            return new Container(x =>
            {
                x.AddRegistry<PluginsRegistry>();
            });
        }
    }
}


تنظیمات ابتدایی WinForms برای دسترسی به امکانات StructureMap

به فرم اصلی برنامه مراجعه کرده و به سازنده‌ی آن IContainer را اضافه کنید. از این اینترفیس جهت دسترسی به پلاگین‌های برنامه استفاده خواهیم کرد.
using System.Windows.Forms;
using StructureMap;

namespace WinFormsWithPluginSupport
{
    public partial class FrmMain : Form
    {
        private readonly IContainer _container;

        public FrmMain(IContainer container)
        {
            _container = container;
            InitializeComponent();
        }
    }
}
اکنون برنامه دیگر کامپایل نخواهد شد؛ چون در فایل Program.cs وهله سازی مستقیمی از FrmMain وجود دارد. این وهله سازی را اکنون به StructureMap محول می‌کنیم تا مشکل برطرف شود:
using System;
using System.Windows.Forms;

namespace WinFormsWithPluginSupport
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(IocConfig.Container.GetInstance<FrmMain>());
        }
    }
}
زمانیکه از متد IocConfig.Container.GetInstance استفاده می‌شود، تا هر تعداد سطحی که تعریف شده، سازنده‌های کلاس‌های مرتبط وهله سازی می‌شوند. در اینجا نیاز است سازنده‌ی کلاس FrmMain وهله سازی شود. چون IContainer اینترفیس اصلی خود StructureMap است، آن‌را شناخته و به صورت خودکار وهله سازی می‌کند. اگر اینترفیس دیگری را ذکر کنید، نیاز است مطابق معمول آن‌را در کلاس IocConfig و متد defaultContainer آن معرفی نمائید.


بارگذاری و اجرای افزونه‌ها

دو دکمه‌ی Run و ReLoad را به فرم اصلی برنامه با کدهای ذیل اضافه کنید:
using System.Linq;
using System.Windows.Forms;
using PluginsBase;
using StructureMap;
using WinFormsWithPluginSupport.Core;

namespace WinFormsWithPluginSupport
{
    public partial class FrmMain : Form
    {
        private readonly IContainer _container;

        public FrmMain(IContainer container)
        {
            _container = container;
            InitializeComponent();
        }

        private void BtnRun_Click(object sender, System.EventArgs e)
        {
            var plugins = _container.GetAllInstances<IPlugin>().ToList();
            foreach (var plugin in plugins)
            {
                plugin.Run();
            }
        }

        private void BtnReload_Click(object sender, System.EventArgs e)
        {
            _container.EjectAllInstancesOf<IPlugin>();
            _container.Configure(x =>
                x.AddRegistry<PluginsRegistry>()
            );
        }
    }
}
در اینجا توسط متد container.GetAllInstances می‌توان به تمامی وهله‌های پلاگین‌های بارگذاری شده، دسترسی یافت و سپس آن‌ها را اجرا کرد.
همچنین در متد ReLoad نحوه‌ی بارگذاری مجدد این پلاگین‌ها را در صورت نیاز مشاهده می‌کنید.
اگر برنامه را اجرا کردید و پلاگینی بارگذاری نشد، به دنبال اسمبلی‌های تکراری بگردید. برای مثال PluginsBase نباید هم در پوشه‌ی اصلی اجرایی برنامه حضور داشته باشد و هم در پوشه‌ی پلاگین‌ها.


کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید
WinFormsWithPluginSupport.zip
 
مطالب
شروع به کار با AngularJS 2.0 و TypeScript - قسمت دوم - معرفی کامپوننت‌ها
در قسمت قبل، پیشنیازهای کار با AngularJS 2.0 مرور و دریافت شدند. اگر مطالب آن‌را پیگیری کرده باشید، هم اکنون باید در پوشه‌ی node_modules واقع در ریشه‌ی پروژه، تمام اسکریپت‌های لازم جهت شروع به کار با AngularJS 2.0 موجود باشند.


تعاریف مداخل فایل index.html یک سایت AngularJS 2.0

پروژه‌ای که در اینجا در حال استفاده است یک برنامه‌ی ASP.NET MVC 5.x است؛ اما الزامی هم به استفاده‌ی از آن وجود ندارد. یا یک فایل index.html را به ریشه‌ی پروژه اضافه کنید و یا فایل Views\Shared\_Layout.cshtml را به نحو ذیل تغییر دهید:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>@ViewBag.Title - My ASP.NET Application</title>
 
    <link href="~/node_modules/bootstrap/dist/css/bootstrap.css" rel="stylesheet" />
    <link href="~/app/app.component.css" rel="stylesheet"/>
    <link href="~/Content/Site.css" rel="stylesheet" type="text/css" />
 
    <!-- 1. Load libraries -->
    <!-- IE required polyfills, in this exact order -->
    <script src="~/node_modules/angular2/es6/dev/src/testing/shims_for_IE.js"></script>
    <script src="~/node_modules/es6-shim/es6-shim.min.js"></script>
    <script src="~/node_modules/systemjs/dist/system-polyfills.js"></script>
 
    <script src="~/node_modules/angular2/bundles/angular2-polyfills.js"></script>
    <script src="~/node_modules/systemjs/dist/system.src.js"></script>
    <script src="~/node_modules/rxjs/bundles/Rx.js"></script>
    <script src="~/node_modules/angular2/bundles/angular2.dev.js"></script>
 
    <!-- Required for http -->
    <script src="~/node_modules/angular2/bundles/http.dev.js"></script>
 
    <!-- Required for routing -->
    <script src="~/node_modules/angular2/bundles/router.dev.js"></script> 
 
    <!-- 2. Configure SystemJS -->
    <script>
        System.config({
            packages: {
                app: {
                    format: 'register',
                    defaultExtension: 'js'
                }
            }
        });
        System.import('app/main')
              .then(null, console.error.bind(console));
    </script>
</head>
<body>
    <div>
        @RenderBody()
        <pm-app>Loading App...</pm-app>
    </div>
 
    @RenderSection("Scripts", required: false)
</body>
</html>
در اینجا ابتدا تعاریف مداخل بوت استرپ و css‌های سفارشی برنامه را مشاهده می‌کنید.
سپس کتابخانه‌های جاوا اسکریپتی مورد نیاز جهت کار با AngularJS 2.0 به ترتیبی که ذکر شده‌، باید تعریف شوند.
ذکر /~ در ابتدای آدرس‌ها، مختص به ASP.NET MVC است. اگر از آن استفاده نمی‌کنید، نیازی به ذکر آن هم نیست.
در ادامه تعاریف System.JS ذکر شده‌است. System.JS کار بارگذاری ماژول‌های برنامه را به عهده دارد. به این ترتیب دیگر نیازی نیست تا به ازای هر قسمت جدید برنامه، مدخلی را در اینجا اضافه کرد و کار بارگذاری آن‌ها خودکار خواهد بود. فرمت register ایی که در اینجا ذکر شده‌است، تا ماژول‌های استاندارد با فرمت ES 6 را نیز پشتیبانی می‌کند. همچنین با ذکر و تنظیم پسوند پیش فرض به js، دیگر نیازی نخواهد بود تا در حین تعریف importها در قسمت‌های مختلف برنامه، پسوند فایل‌ها را به صورت صریح ذکر کرد. مبحث improtها مرتبط است به مفاهیم ماژول‌ها در ES 6 و همچنین TypeScript.
سطر System.import کار بارگذاری اولین ماژول برنامه را از پوشه‌ی app قرار گرفته در ریشه‌ی سایت انجام می‌دهد. این ماژول main نام دارد.


نوشتن اولین کامپوننت AngularJS 2.0

برنامه‌های AngularJS 2.0 متشکل هستند از تعدادی کامپوننت و سرویس:


 و هر کامپوننت تشکیل شده‌است از:


- یک قالب یا Template: با استفاده از HTML تعریف می‌شود و کار تشکیل View و نحوه‌ی رندر کامپوننت را مشخص می‌کند. در این Viewها با استفاده از امکانات binding و directives موجود در AngularJS 2.0 کار دسترسی به داده‌ها صورت می‌گیرد.
یک کلاس: کار این کلاس که توسط TypeScript تهیه می‌شود، فراهم آوردن کدهای مرتبط با قالب است. برای مثال این کلاس حاوی تعدادی خاصیت خواهد بود که از اطلاعات آن‌ها در View مرتبط استفاده می‌شود. همچنین این کلاس می‌تواند حاوی متدهای مورد نیاز در View نیز باشد؛ برای مثال متدی که کار نمایش یا مخفی سازی یک تصویر را با کلیک بر روی دکمه‌ای انجام می‌دهد.
- متادیتا: متادیتا (یا decorator در اینجا) به AngularJS 2.0 اعلام می‌کند که این کلاس تعریف شده، صرفا یک کلاس ساده نیست و باید به آن به صورت یک کامپوننت نگاه شود.

در ذیل، کدهای یک کامپوننت نمونه‌ی AngularJS 2.0 را مشاهده می‌کنید:
import { Component } from 'angular2/core';
 
@Component({
    selector: 'pm-app',
    template:`
    <div><h1>{{pageTitle}}</h1>
        <div>My First Component</div>
    </div>
    `
})
export class AppComponent {
    pageTitle: string = "DNT AngularJS 2.0 APP";
}
در انتهای کدها، یک کلاس را مشاهده می‌کنید که کار تعریف خواص و متدهای مورد نیاز توسط View را انجام می‌دهد.
بلافاصله در بالای این کلاس، متد decorator ایی را به نام Component مشاهده می‌کنید. این متادیتا است که به AngularJS 2.0 اعلام می‌کند، کلاس AppComponent تعریف شده، یک کامپوننت است و نه تنها یک کلاس ساده.
در متد Component تعریف شده، قالب یا template نحوه‌ی رندر این کامپوننت را مشاهده می‌کنید.
در ابتدای این ماژول نیز کار import تعاریف مرتبط با متد ویژه‌ی Component، از هسته‌ی AngularJS 2.0 انجام شده‌است تا کامپایلر TypeScript بتواند این فایل ts را کامپایل کند.


مروری بر نحوه‌ی تعریف class در TypeScript

مرور جامع کلاس‌ها در TypeScript را در مطلب «مبانی TypeScript؛ کلاس‌ها» می‌توانید مطالعه کنید. در اینجا جهت یادآوری، خلاصه‌ای از آن‌را که نیاز داریم، بررسی خواهیم کرد:
- جهت تعریف یک کلاس، ابتدا واژه‌ی کلیدی class به همراه نام کلاس ذکر می‌شوند.
- در AngularJS 2.0 مرسوم است که نام کلاس را به صورت نام ویژگی مدنظر به همراه پسوند Component ذکر کنیم؛ مانند AppComponent مثال فوق. این نام pascal case است و با حروف بزرگ شروع می‌شود.
- همچنین مرسوم است در برنامه‌های AngularJS 2.0، کامپوننت ریشه‌ی سایت نیز AppComponent نامیده شود.
- در مثال فوق، واژه‌ی کلیدی export را نیز پیش از واژه‌ی کلیدی class مشاهده می‌کنید. به این ترتیب این کلاس خارج از ماژولی که در آن تعریف می‌شود، قابل دسترسی خواهد بود. اکنون این کلاس و فایل، تبدیل به ماژولی خواهند شد که توسط module loader معرفی شده‌ی در ابتدای بحث یا همان System.JS به صورت خودکار بارگذاری می‌شود و دیگر نیازی به تعریف مدخل script متناظر با آن در فایل index.html نخواهد بود.
- در بدنه‌ی کلاس، کار تعریف متدها و خواص مورد نیاز View صورت می‌گیرند. برای نمونه در اینجا تنها یک خاصیت «عنوان صفحه» تعریف شده‌است. در جاوا اسکریپت مرسوم است که نام خواص را camel case شروع شده با حروف کوچک تعریف کنیم. سپس نوع این خاصیت به صورت رشته‌ای تعریف شده‌است و در آخر مقدار پیش فرض این خاصیت ذکر گردیده‌است.
البته باید دقت داشت که الزامی به ذکر نوع خاصیت، در TypeScript وجود ندارد. همینقدر که مقدار پیش فرض این خاصیت رشته‌ای است، بر اساس ویژگی به نام Type inference در TypeScript، نوع این خاصیت نیز رشته‌ای درنظر گرفته خواهد شد و دیگر نمی‌توان برای مثال یک عدد را به آن انتساب داد.
- سطح دسترسی خواص تعریف شده‌ی در یک کلاس TypeScript به صورت پیش فرض public است. بنابراین در اینجا نیازی به ذکر صریح آن نبوده‌است.


مروری بر متادیتا یا decorator یک کلاس در AngularJS 2.0

خوب، تا اینجا کلاس AppComponent تعریف و همچنین export شد تا توسط system.js به صورت خودکار بارگذاری شود. اما این کلاس به خودی خود صرفا یک کلاس TypeScript ایی است و توسط AngularJS شناسایی نمی‌شود. برای معرفی این کلاس به صورت یک کامپوننت، از یک تزئین کننده یا decorator ویژه به نام Component استفاده می‌شود که بلافاصله در بالای تعریف کلاس قرار می‌گیرد؛ چیزی شبیه به data annotations یا attributes در زبان #C.
یک decorator متدی است که اطلاعاتی اضافی را به یک کلاس، اعضاء و متدهای آن و یا حتی آرگومان‌های آن متدها، الصاق می‌کند. این ویژگی قرار است به صورت استاندارد در ES 2016 یا نگارش بعدی جاوا اسکریپت حضور داشته باشد و در حال حاضر توسط TypeScript پشتیبانی شده و در نهایت به کدهای ES 5 قابل اجرای در تمام مرورگرها ترجمه می‌شود.
یک decorator همیشه با @ شروع می‌شود و AngularJS 2.0 به همراه تعدادی decorator توکار است؛ مانند Component. از آنجائیکه decorator یک متد است، همیشه به همراه یک جفت پرانتز () ذکر می‌شود و در انتهای آن نیازی به ذکر سمی‌کالن نیست. در اینجا تزئین کننده‌ی Component یک شیء را می‌پذیر که به همراه تعدادی خاصیت است. به همین جهت پارامتر آن به صورت {} ذکر شده‌است.
خاصیت selector یک کامپوننت مشخص می‌کند که نام directive متناظر با این کامپوننت چیست:
 selector: 'pm-app',
 از این نام، به صورت یک المان جدید و سفارشی HTML جهت تعریف این کامپوننت استفاده خواهیم کرد. برای مثال اگر به کدهای ابتدای بحث دقت کنید، نام pm-app به صورت ذیل و به شکل یک تگ جدید HTML استفاده شده‌است:
    <div>
        @RenderBody()
        <pm-app>Loading App...</pm-app>
    </div>
همچنین یک کامپوننت همواره به همراه یک قالب است که نحوه‌ی رندر آن‌را مشخص می‌کند:
  template:`
 <div><h1>{{pageTitle}}</h1>
<div>My First Component</div>
 </div>
 `
 در اینجا از back tick مربوط به ES 6.0 که توسط TypeScript نیز پشتیبانی می‌شود، جهت تعریف یک رشته‌ی چندسطری جاوا اسکریپتی، استفاده شده‌است.
همچنین {{}} به معنای تعریف data binding است. به این ترتیب مقداری که قرار است به صورت تگ h1 رندر شود، از خاصیت pageTitle کلاس مزین شده‌ی توسط این ویژگی یا decorator تامین خواهد شد؛ یعنی مقدار پیش فرض خاصیت pageTitle در کلاس AppComponent.


import اطلاعات مورد نیاز جهت کامپایل یک فایل TypeScript

تا اینجا مفاهیم موجود در کلاس AppComponent را به همراه متادیتای آن بررسی کردیم. اما این متادیتای جدید کامپوننت، به صورت پیش فرض ناشناخته‌است:


همانطور که مشاهده می‌کنید، در اینجا ذیل کامپوننت، خط قرمزی جهت یادآوری عدم تعریف آن، کشیده شده‌است. در TypeScript و یا ES 6، پیش از استفاده از یک کلاس یا متد خارجی، نیاز است به module loader اعلام کنیم تا آن‌را باید از کجا تامین کند. اینکار توسط عبارت import انجام می‌شود که شبیه به عبارت using در زبان سی‌شارپ است. عبارت import جزئی از استاندارد ES 6 است و همچنین در TypeScript نیز پشتیبانی می‌شود. به این ترتیب امکان دسترسی به ویژگی‌های export شده‌ی از سایر ماژول‌ها را در ماژول فعلی (فایل فعلی در حال کار) خواهیم یافت. نمونه‌ی آن‌را با ذکر واژه‌ی کلیدی export پیش از کلاس AppComponent پیشتر ملاحظه کردید.
این ماژول‌های خارجی می‌توانند سایر ماژول‌ها و فایل‌های ts نوشته شده‌ی توسط خودمان و یا حتی اجزای AngularJS 2.0 باشند. طراحی AngularJS 2.0 نیز ماژولار است و از ماژول‌هایی مانند angular2/core، angular2/animation، angular2/http و angular2/router تشکیل شده‌است.
برای نمونه متادیتای کامپوننت، در ماژول angular2/core قرار دارد. به همین جهت نیاز است در ابتدای ماژول فعلی آن‌را import کرد:
import { Component } from 'angular2/core';
کار با ذکر واژه‌ی کلیدی import شروع می‌شود. سپس جزئی را که نیاز داریم داخل {} ذکر کرده و در آخر مسیر یافتن آن‌را مشخص می‌کنیم.


ساخت کامپوننت ریشه‌ی یک برنامه‌ی AngularJS 2.0

در ابتدای بحث که تعاریف مداخل مورد نیاز جهت اجرای یک برنامه‌ی AngularJS 2.0 ذکر شدند، عنوان شد که system.js به دنبال ماژول آغازین app/main می‌گردد.
بنابراین در ریشه‌ی پروژه، پوشه‌ی جدیدی را به نام app ایجاد کنید.
سپس یک فایل TypeScript جدید را به نام app.component.ts به این پوشه اضافه کنید. قالب این نوع فایل‌ها در add new item و با جستجو typescript در دسترس است و یا حتی یک فایل متنی ساده را هم با پسوند ts ایجاد کنید، کافی است.


نامگذاری این فایل‌ها هم مرسوم است به صورت ذکر نام ویژگی به همراه یک دات و سپس ذکر کامپوننت صورت گیرد. در اینجا چون قصد داریم کامپوننت ریشه‌ی برنامه را ایجاد کنیم، نام ویژگی آن app خواهد بود و نام کامل فایل به این ترتیب app.component.ts می‌شود.
سپس محتوای این فایل را به دقیقا معادل کدهای قسمت «نوشتن اولین کامپوننت AngularJS 2.0» ابتدای بحث تغییر دهید (همان import، متادیتا و کلاس AppComponent).

تا اینجا کامپوننت ریشه‌ی برنامه ایجاد شد. اما چگونه باید از آن استفاده کنیم و چگونه AngularJS 2.0 آن‌را شناسایی می‌کند؟ به این فرآیند آغازین شناسایی و بارگذاری ماژول‌ها، اصطلاحا bootstrapping می‌گویند. تنها صفحه‌ی واقعی موجود در یک برنامه‌ی تک صفحه‌ای وب، همان فایل index.html است و سایر صفحات و محتوای آن‌ها به محتوای این صفحه‌ی آغازین اضافه یا کم خواهند شد.
<div>
    @RenderBody()
    <pm-app>Loading App...</pm-app>
</div>
در اینجا برای نمایش اولین کامپوننت تهیه شده، نام تگ selector آن که توسط متادیتای کلاس AppComponent تعریف شد، در body فایل index.html به نحو فوق به صورت یک تگ سفارشی جدید اضافه می‌شود. به آن directive نیز می‌گویند.
خوب، اکنون module loader یا system.js چگونه این pm-app یا کامپوننت ریشه‌ی برنامه را شناسایی می‌کند؟
 System.import('app/main')
این شناسایی توسط سطر System.import تعریف شده‌ی در فایل index.html انجام می‌شود. در اینجا system.js، در پوشه‌ی app واقع در ریشه‌ی سایت، به دنبال ماژول راه اندازی به نام main می‌گردد. یعنی نیاز است فایل TypeScript جدیدی را به نام main.ts به ریشه‌ی پوشه‌ی app اضافه کنیم. محتوای این فایل ویژه‌ی بوت استرپ AngularJS 2.0 به صورت ذیل است:
/// <reference path="../node_modules/angular2/typings/browser.d.ts" />
 
import { bootstrap } from "angular2/platform/browser";
 
// Our main component
import { AppComponent } from "./app.component";
 
bootstrap(AppComponent);
این فایل ویژه را نیز مانند کلاس AppComponent که پیشتر بررسی کردیم، نیاز است از انتها به ابتدا بررسی کرد.
در انتهای این فایل متد bootstrap مربوط به AngularJS 2.0 را مشاهده می‌کنید. کار آن بارگذاری اولین ماژول و کامپوننت برنامه یا همان AppComponent است.
در ادامه نیاز است AppComponent را به این ماژول معرفی کرد؛ در غیراینصورت کامپایل نخواهد شد. برای این منظور سطر import این کلاس را از فایل app.component، مشاهده می‌کنید. در اینجا نیازی به ذکر پسوند ts. فایل app.component نیست.
سپس نیاز است محل تعریف متد بوت استرپ را نیز مشخص کنیم. این متد در ماژول angular2/platform/browser قرار دارد که به عنوان اولین import این فایل ذکر شده‌است.
سطر اول، مربوط است به تعریف فایل‌های d.ts. مربوط به TypeScript جهت شناسایی نوع‌های مرتبط با AngularJS 2.0. اگر اینکار صورت نگیرد، خطاهای ذیل را در حین کامپایل فایل‌های TypeScript دریافت خواهید کرد:
 node_modules\angular2\src\core\application_ref.d.ts(171,81): error TS2304: Build: Cannot find name 'Promise'.
node_modules\angular2\src\core\change_detection\differs\default_keyvalue_differ.d.ts(23,15): error TS2304: Build: Cannot find name 'Map'.
تهیه فایل main.ts تنها یکبار صورت می‌گیرد و دیگر با آن کاری نخواهیم داشت.

تا اینجا پوشه‌ی app واقع در ریشه‌ی سایت، یک چنین شکلی را پیدا می‌کند:



و اکنون اگر برنامه را اجرا کنیم (فشردن دکمه‌ی F5)، خروجی آن در مرورگر به صورت ذیل خواهد بود:

با توجه به اینکه در حال کار با یک برنامه‌ی جاوا اسکریپتی هستیم، باز نگه داشتن developer tools مرورگر، جهت مشاهده‌ی خطاهای احتمالی ضروری است.

در اینجا اگر خطایی وجود داشته باشد، یا اطلاعات اضافی مدنظر باشد، در console لاگ خواهند شد. برای مثال در اینجا عنوان شده‌است که برنامه در حالت توسعه در حال اجرا است. بهتر است برای ارائه‌ی نهایی، متد enableProdMode را در فایل index.html فراخوانی کنید.

همچنین در اینجا می‌توان HTML نهایی تزریق شده‌ی به صفحه را بهتر مشاهده کرد:



کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: MVC5Angular2.part2.zip
برای اجرای آن، ابتدا به فایل project.json مراجعه کرده و یکبار آن‌را ذخیره کنید تا وابستگی‌های اسکریپتی پروژه از اینترنت دریافت شوند (این موارد در قسمت قبل مرور شدند). سپس یکبار هم پروژه را Build کنید تا تمام فایل‌های ts آن کامپایل شوند و در آخر، اجرای نهایی پروژه.


خلاصه‌ی بحث

یک برنامه‌ی AngularJS 2.0 متشکل است از تعدادی کامپوننت. بنابراین کلاسی را ایجاد خواهیم کرد تا کدهای پشتیبانی کننده‌ی View این کامپوننت را تولید کند. سپس این کلاس را با متادیتایی مزین کرده و توسط آن تگ سفارشی ویژه‌ی این کامپوننت و تگ‌های HTML تشکیل دهنده‌ی این کامپوننت را به AngularJS 2.0 معرفی می‌کنیم. در اینجا در صورت نیاز وابستگی‌های تعریف این متادیتا را توسط واژه‌ی کلیدی import دریافت می‌کنیم. نام این کلاس بهتر است Pascal case بوده و با حروف بزرگ شروع شود و همچنین به صورت نام ویژگی ختم شده‌ی به کلمه‌ی Component باشد. در اینجا حتما نیاز است این کلاس export شود تا توسط module loader قابل استفاده و بارگذاری گردد. اگر View این کامپوننت نیاز به دریافت اطلاعاتی دارد، این اطلاعات به صورت خواصی در کلاس کامپوننت تعریف می‌شوند. این خواص تعریف شده‌ی با سطح دسترسی عمومی، مرسوم است به صورت camel case تعریف شوند و حروف اول آن‌ها کوچک باشد.
اشتراک‌ها
لیست استان ها / شهرستان ها و شهر های ایران به همراه مختصات سال 97

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

برای یکی از نرم افزارها نیاز بود  لیست به روز (سال 96-97) شهر‌های ایران رو به تفکیک استان/شهرستان/شهر به همراه مختصات آنها برای نمایش روی نقشه به صورت اطلاعات پایه در نرم افزار داشته باشیم... این شد که این فایل رو با توجه به دیتای سایت آمار کشور و چند سایت دیگر جمع آوری کردیم. 

در نهایت این لیست رو به فرمت json به اشتراک گذاشتم تا دوستان هم اگر نیازی داشتند استفاده کنند.

لیست استان ها / شهرستان ها و شهر های ایران به همراه مختصات سال 97
اشتراک‌ها
چه زمانی باید ریفکتور کنیم؟

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

قانون 3

  1. وقتی کاری برای بار اول انجام می‌شود، فقط انجام می‌دهیم
  2. وقتی کار مشابهی را برای بار دوم انجام می‌دهیم، یواشکی! انجام می‌دهیم
  3. اگر برای برای سوم شد حتما باید ریفکتور کنیم و کدها را اصلاح کنیم! 
چه زمانی باید ریفکتور کنیم؟
مطالب
چگونگی رسیدگی به Null property در AutoMapper

AutoMapper کتابخانه‌ای برای نگاشت اطلاعات یک شیء به شی‌ءایی دیگر به صورت خودکار می‌باشد.

در این مقاله چگونگی رسیدگی به Null property را در AutoMapper   بررسی خواهیم کرد. فرض کنید شیء منبع دارای یک خاصیت Null  است و می‌خواهید به وسیله Automaper شیء منبع را به مقصد نگاشت نمایید. اما می‌خواهید در صورت Null بودن شیء مبدا، یک مقدار پیش فرض برای شیء مقصد در نظر گرفته شود .

برای نمونه کلاسuser   را که در آن از کلاس Address یک خاصیت تعریف شده، در نظر بگیرید. اگر مقدار آدرس در شیء منبع خالی بود شاید شما بخواهید مقدار آن را به صورت empty string و یا با یک مقدار پیش فرض در مقصد مقدار دهی کنید.

همانند مثال زیر : 

public class UserSource
{
  public Address Address{get;set;}
}
 
public class UserDestination
{
  public string Address{get;set;}
}
ابتدا نگاشت‌ها را تعریف می‌کنیم:
AutoMapper.Mapper.CreateMap<UserSource, UserDestination>()
          .ForMember(dest => dest.Address
          , opt => opt.NullSubstitute("Address not found")
          );
کد بالا نشان دهنده تبدیل Address به Address است ولی دارای متد اختیاری NullSubstitute می‌باشد و بیانگر این است که اگر آدرس شیء منبع Null بود، مقدار پیش فرضی را برای شیء مقصد در نظر بگیرد. در انتها  می‌توان نگاشت را در برنامه متناسب با نیاز خود انجام داد:
var model = AutoMapper.Mapper.Map<UserSource, UserDestination>(user);
var models = AutoMapper.Mapper.Map<IEnumerable<UserSource>, IEnumerable<UserDestination>>(users);