اشتراک‌ها
14 قابلیت بهبود یافته برتر در VB 14

1. 50 درصد سریعتر شدن زمان کامپایل. نسخه فعلی کامپایلر VB در ++C نوشته شده است, ولی تیم VB کامپایلر جدید VB.NET 14 را کاملا در VB.NET بازنویسی کرده اند.نتایج دو مقایسه :

   * زمان Build یک Solution بزرگ(Huge) با 1.3 میلیون خط کد, از 68 ثانیه رسید به 41 ثانیه.

   * زمان Load یک Cold Solution (یک پروژه Win Store) از 6.7 ثانیه رسید به 4.6 ثانیه.

2. به اشتراک گذاری یک پروژه(یک سری ار سورس کد ها) بین چند تا Application

3. نمایش Expression‌های Lambda و LINQ در پنجره Watch و Immediate در زمان Debug

4. قرار دادن کامنت در لا به لای عبارات LINQ

5. استفاده از اپراتور جدید ?. جهت بررسی Null بودن برای تسریع در حین کد نویسی

و ...

14 قابلیت بهبود یافته برتر در VB 14
مطالب
آشنایی با Refactoring - قسمت 2

قسمت دوم آشنایی با Refactoring به معرفی روش «استخراج متدها» اختصاص دارد. این نوع Refactoring بسیار ساده بوده و مزایای بسیاری را به همراه دارد؛ منجمله:
- بالا بردن خوانایی کد؛ از این جهت که منطق طولانی یک متد به متدهای کوچکتری با نام‌های مفهوم شکسته می‌شود.
- به این ترتیب نیاز به مستند سازی کدها نیز بسیار کاهش خواهد یافت. بنابراین در یک متد، هر جایی که نیاز به نوشتن کامنت وجود داشت، یعنی باید همینجا آن‌ قسمت را جدا کرده و در متد دیگری که نام آن، همان خلاصه کامنت مورد نظر است، قرار داد.
- این نوع جدا سازی منطق‌های پیاده سازی قسمت‌های مختلف یک متد، در آینده نگهداری کد نهایی را نیز ساده‌تر کرده و انجام تغییرات بر روی آن را نیز تسهیل می‌بخشد؛ زیرا اینبار بجای هراس از دستکاری یک متد طولانی، با چند متد کوچک و مشخص سروکار داریم.

برای نمونه به مثال زیر دقت کنید:
using System.Collections.Generic;

namespace Refactoring.Day2.ExtractMethod.Before
{
public class Receipt
{
private IList<decimal> Discounts { get; set; }
private IList<decimal> ItemTotals { get; set; }

public decimal CalculateGrandTotal()
{
// Calculate SubTotal
decimal subTotal = 0m;
foreach (decimal itemTotal in ItemTotals)
subTotal += itemTotal;

// Calculate Discounts
if (Discounts.Count > 0)
{
foreach (decimal discount in Discounts)
subTotal -= discount;
}

// Calculate Tax
decimal tax = subTotal * 0.065m;
subTotal += tax;

return subTotal;
}
}
}

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

using System.Collections.Generic;

namespace Refactoring.Day2.ExtractMethod.After
{
public class Receipt
{
private IList<decimal> Discounts { get; set; }
private IList<decimal> ItemTotals { get; set; }

public decimal CalculateGrandTotal()
{
decimal subTotal = CalculateSubTotal();
subTotal = CalculateDiscounts(subTotal);
subTotal = CalculateTax(subTotal);
return subTotal;
}

private decimal CalculateTax(decimal subTotal)
{
decimal tax = subTotal * 0.065m;
subTotal += tax;
return subTotal;
}

private decimal CalculateDiscounts(decimal subTotal)
{
if (Discounts.Count > 0)
{
foreach (decimal discount in Discounts)
subTotal -= discount;
}
return subTotal;
}

private decimal CalculateSubTotal()
{
decimal subTotal = 0m;
foreach (decimal itemTotal in ItemTotals)
subTotal += itemTotal;
return subTotal;
}
}
}

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

ابزارهای کمکی جهت پیاده سازی روش «استخراج متدها»:

- ابزار Refactoring توکار ویژوال استودیو پس از انتخاب یک قطعه کد و سپس کلیک راست و انتخاب گزینه‌ی Refactor->Extract method، این عملیات را به خوبی می‌تواند مدیریت کند و در وقت شما صرفه جویی خواهد کرد.
- افزونه‌های ReSharper و همچنین CodeRush نیز چنین قابلیتی را ارائه می‌دهند؛ البته توانمندی‌های آن‌ها از ابزار توکار یاد شده بیشتر است. برای مثال اگر در میانه کد شما جایی return وجود داشته باشد، گزینه‌ی Extract method ویژوال استودیو کار نخواهد کرد. اما سایر ابزارهای یاده شده به خوبی از پس این موارد و سایر موارد پیشرفته‌تر بر می‌آیند.

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


و ... «لطفا» این نوع پیاده سازی‌ها را خارج از فایل code behind هر نوع برنامه‌ی winform/wpf/asp.net و غیره قرار دهید. تا حد امکان سعی کنید این مکان‌ها، استفاده کننده‌ی «نهایی» منطق‌های پیاده سازی شده توسط کلاس‌های دیگر باشند؛ نه اینکه خودشان محل اصلی قرارگیری و ابتدای تعریف منطق‌های مورد نیاز قسمت‌های مختلف همان فرم مورد نظر باشند. «لطفا» یک فرم درست نکنید با 3000 سطر کد که در قسمت code behind آن قرار گرفته‌اند. code behind را محل «نهایی» ارائه کار قرار دهید؛ نه نقطه‌ی آغاز تعریف منطق‌های پیاده سازی کار. این برنامه نویسی چندلایه که از آن صحبت می‌شود، فقط مرتبط با کار با بانک‌های اطلاعاتی نیست. در همین مثال، کدهای فرم برنامه، باید نقطه‌ی نهایی نمایش عملیات محاسبه مالیات باشند؛ نه اینکه همانجا دوستانه یک قسمت مالیات حساب شود، یک قسمت تخفیف، یک قسمت جمع بزند، همانجا هم نمایش بدهد! بعد از یک هفته می‌بینید که code behind فرم در حال انفجار است! شده 3000 سطر! بعد هم سؤال می‌پرسید که چرا اینقدر میل به «بازنویسی» سیستم این اطراف زیاد است! برنامه نویس حاضر است کل کار را از صفر بنویسد، بجای اینکه با این شاهکار بخواهد سرو کله بزند! هر چند یکی از روش‌های برخورد با این نوع کدها جهت کاهش هراس نگهداری آن‌ها، شروع به Refactoring است.

نظرات مطالب
تعامل MATLAB (متلب) با دات نت - قسمت اول
در این مورد در قسمت بعد توضیح خواهم داد. بله احتیاج است. اگر تصمیم به نوشتن کدها و کامپایل کردن اونها دارید. خیر، اگر نویسنده کدها یک پکیج از پروژه متلب در اختیار شما قرار بدهد.
مطالب
انتقال SVN به یک سیستم جدید

در راستای مهاجرت به ویندوز 7، کار نصب و راه اندازی SVN و کلاینت‌های آن باید مجددا انجام می‌شد. اگر برای بار اول است که به مبحث SVN برخورد می‌کنید، مطالعه این جزوه توصیه می‌شود. مطالب ذیل برای افرادی مفید است که قصد انتقال سیستم SVN موجود خود را به مکان و یا سیستم عامل دیگری در اسرع وقت دارند.

الف) دریافت و نصب Visual SVN server
یا می‌توان SVN خالص را از سایت آن دریافت کرد و یا جهت سهولت کار و همچنین دسترسی به یک کنسول مدیریتی می‌توان برنامه‌ی رایگان Visual SVN server را از آدرس زیر دریافت و نصب کرد:

پس از نصب، ابتدا باید یا کاربر جدیدی را جهت استفاده از منابع آن تعریف کرد و یا از نحوه‌ی اعتبار سنجی یکپارچه با ویندوز هم می‌توان استفاده کرد که من از این روش دوم استفاده می‌کنم (شکل زیر، کلیک راست بر روی نود اصلی visual SVN server و سپس انتخاب خواص و مراجعه به برگه‌ی اعتبار سنجی آن):



ب)دریافت و نصب TortoiseSVN

نصب آن نکته‌ی خاصی ندارد. اما یک سری نکته‌ی ریز پس از نصب آن بهتر است رعایت شود که در ادامه ذکر می‌شود:

ج) دریافت و نصب برنامه‌ی WinMerge
برنامه‌ی Diff پیش فرض TortoiseSVN آنچنان قوی نیست. به همین جهت می‌توان برنامه‌ی WinMerge را با آن یکپارچه کرد. برای این منظور ابتدا آن‌را دریافت نمائید:

اگر پس از نصب TortoiseSVN آن‌را نصب کنید، در حین نصب پیشنهاد یکپارچه سازی با TortoiseSVN را نیز می‌دهد. اگر ابتدا WinMerge را نصب کرده‌اید و سپس TortoiseSVN بر روی سیستم شما نصب شده، فقط کافی است مطابق شکل زیر ابتدا به قسمت Diff viewers آن مراجعه کرده و سپس با انتخاب گزینه‌ی external ، دستور خط فرمان زیر را وارد نمائید:

C:\Program Files (x86)\WinMerge\WinMergeU.exe -e -x -ub -dl %bname -dr %yname %base %mine



بدیهی است مسیر WinMergeU.exe مطابق مسیر نصب در سیستم شما باید تنظیم شود.

د) تنظیم مسیر تحت نظر قرار گرفتن سیستم
TortoiseSVN به صورت پیش فرض کل سیستم را جهت مشاهده‌ی تغییرات تحت نظر قرار می‌دهد که گاهی باعث کاهش کارآیی آن خواهد شد. برای رفع این مشکل می‌توان مسیرهایی را که پروژه‌های شما در آن قرار دارند را به آن معرفی نمود تا بار کلی سیستم کاهش یابد.



همانطور که در شکل نیز ملاحظه می‌کنید، Include path مقدار دهی شده است.

ه) مشخص سازی پسوندهایی که بهتر است از آن‌ها صرفنظر شود
به برگه‌ی general تنظیمات TortoiseSVN مراجعه کرده و در قسمت global ignore pattern آن، موارد زیر را وارد نمائید:



این موارد شامل پروژه‌های دات نت، دلفی ، VC و امثال آن است و همچنین یک سری فایل بایناری که عموما با پروژه‌های برنامه نویسی نیازی به ثبت نگارش آن‌ها نیست.

*.dcu *.~* dcu temp *.exe *.zip *.bkm *.ddp *.cfg *.dof *.dsk *.ini *.hlp *.gid *.bmp *.png *.gif ~* *.log bin debug release *.map *.chm *.bkf Thumbs.db *.mdb .obj *.elf *.stat *.ddp *.bpl *.map *.GID *.hlp *.opt *.dll *.raw *.BIN *.obj *.pdb *.scc Debug Release *.xml obj *.~* *.backup *.INI *.ArmLog *.KeyLog *.NanoLog *.Stats *.PreARM *.old *.drc *.*~ *.doc *.pdf *.bmp *.jpg *.MRW *.NEF *.ORF *.psd *.X3F __history *.local *.identcache *.bak Thumbs.db *.ldb *.dex *.rar DllDcu *.lck CVS cvs *.txt *.TXT *.jdbg *.HLP *.KWF *.xls *.cnt *.dsm *.dti *.tmp *.lnk *.cbk *.mes *.suo *.ncb *.user _ReSharper.* [Bb]in obj [Dd]ebug [Rr]elease *.aps *.eto


در همین برگه، اگر هنوز از VS2003 استفاده می‌کنید، تیک مربوط به استفاده از _svn بجای .svn را قرار دهید تا VS.Net با پوشه‌های مدیریتی ذکر شده مشکل پیدا نکند.

و) نصب افزونه‌های SVN سازگار با VS.Net
یا می‌توان از افزونه‌ی Visual SVN استفاده کرد (که رایگان نیست) و یا AnkhSVN که رایگان و سورس باز است.
ولی در کل یک مورد را بیشتر نصب نکنید. علت هم کند شدن VS.Net است به دلیل فعالیت‌های پشت صحنه‌ی هر کدام از این افزونه‌ها که زیاده روی در تعداد آن‌ها گاها باعث کرش هم می‌شود. بنابراین همان یک مورد کافی است.

ز) Import مخزن‌های قبلی
تا اینجا مقدمات کار فراهم شد. اکنون نوبت به import مخزن‌های بجا مانده از سیستم قبلی است. برای اینکار مطابق شکل زیر، گزینه‌ی import existing repositories را انتخاب کرده و مسیر مخزن‌های قبلی خود را باید معرفی نمود (به ازای هر کدام یکبار باید این عملیات صورت گیرد).



پس از انجام این مراحل یکبار باید سیستم reboot شود و اکنون همه چیز مثل قبل خواهد شد!


نکته:
اگر مسیر ریشه مخزن‌های جدید با مسیر آن‌ها در سیستم قبلی متفاوت است، هنگام commit کارهای خود با خطای زیر متوقف خواهید شد:
Commit failed (details follow): Unable to open an ra_local session to URL
Unable to open repository 'file:///C:/Repositories/tracking/trunk'




اشکالی ندارد! برای رفع آن باید از گزینه‌ی relocate مربوط به TortoiseSVN استفاده کرد.
بر روی پوشه کاری پروژه خود کلیک راست کرده، انتخاب گزینه‌ی TortoiseSVN و سپس انتخاب گزینه‌ی Relocate آن باید صورت گیرد. در اینجا می‌توان مسیر جدید ریشه اصلی مخزن را در سیستم جدید معرفی کرد.



نظرات مطالب
مباحث تکمیلی مدل‌های خود ارجاع دهنده در EF Code first
نیازی نیست زیاد پیچیده فکر کنید. یک لیست ساده رو واکشی کنید (مثلا یک کامنت که سه زیر مجموعه مرتبط دارد با یک select ساده)، اتصال نهایی آن‌ها در سمت کلاینت خودکار است. به عبارتی شکل دهی تو در تو در اینجا در سمت کلاینت انجام می‌شود و نه سمت سرور. سمت سرور آن، یک select معمولی از رکوردهای مورد نظر بیشتر نیست.
var specifiedCommentId = 4; //عدد 4 در اینجا شماره رکورد کامنتی است که دارای یک سری زیر کامنت است
var list = ctx.BlogComments
                 .Include(x => x.Reply)
                 .Where(x => x.Id >= specifiedCommentId && (x.ReplyId == x.Reply.Id || x.Reply == null))
                 .ToList() // fills the childs list too
                 .Where(x => x.Reply == null) // for TreeViewHelper                        
                 .ToList();
بازخوردهای پروژه‌ها
کامنت گذاری مرتبط با پست های سایت
سلام، ابتدا تشکر می‌کنم بابت ارائه این پروژه خوب وخوش ساخت
خواستم خواهش کنم اگر برای شما مقدور هست در بین کد‌ها با کامنت گذاری به مطالب مرتبط با سایت ارجاع دهید (طبق گفته آقای نصیری برای فهمیدن این پروژه نیاز هست که روی مطالب سایت مسلط بود).

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


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


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

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


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


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

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


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



برای آزمایش بیشتر، فایل csproj نهایی را از اینجا می‌توانید دریافت کنید:
DualTargetFrameworks.zip
 
مطالب
تنظیمات امنیتی SMTP Server متعلق به IIS 6.0 جهت قرارگیری بر روی اینترنت

فرض کنید یک سرور را بر روی اینترنت قرار داده‌اید و از SMTP Server متعلق به IIS قصد دارید جهت ارسال ایمیل توسط برنامه‌های خود استفاده نمائید. در این حالت مواردی را باید رعایت نمود تا این سرور تبدیل به سرور رایگان ارسال spam توسط "دشمنان" نشود.



1- پورت پیش فرض را عوض کنید
پورت پیش فرض اتصال به SMTP Server مساوی 25 است. از آنجائیکه به سادگی در برنامه‌های خود می‌توان این پورت را نیز تنظیم نمود، بهتر است به عنوان اولین قدم، این پورت را تغییر داد. یک شماره پورت دلخواه خالی را یافته و بجای 25 قرار دهید. برای این منظور مسیر زیر را طی کنید:
بر روی Default SMTP Virtual Server در کنسول IIS کلیک راست کرده و گزینه خواص را انتخاب کنید. در برگه General روی دکمه Advanced کلیک کرده و در صفحه باز شده سطر مربوط به پورت 25 را یافته، بر روی دکمه Edit کلیک نموده و آن‌را به عددی دیگر تغییر دهید.



2- دسترسی عموم را به سرور قطع کنید!
متاسفانه تنظیمات پیش فرض SMTP Server متعلق به IIS در جهت قطع دسترسی "دشمنان" کاملا نادرست بوده و بر مبنای ایده حداقل دسترسی صورت نگرفته‌ است. اگر سرور را به این حال رها کنید فقط "دشمنان" را خوشحال کرده‌اید.
برای قطع دسترسی دشمنان سه مرحله باید صورت گیرد:
الف) در برگه Access مربوط به تنظیمات SMTP server ، روی دکمه relay کلیک کرده، ابتدا تیک مربوط به Allow all computers which successfully authenticated to relay‌ را بردارید (این مورد در یک شبکه داخلی حائز اهمیت می‌شود و سایر کامپیوترها را منع می‌کند). سپس در قسمت بالای صفحه گزینه only the list below را انتخاب کرده و IP آن‌را مساوی 127.0.0.1 وارد کنید (یعنی فقط این کامپیوتر مجاز است که از این سرویس جهت ارسال ایمیل استفاده کند؛ نه دشمنان خارجی).



ب) مورد الف را درباره‌ی قسمت مرتبط با دکمه connections نیز تکرار کنید. (پیش فرض آن تمام عالم است!)



ج) در همین برگه‌ی Access بر روی دکمه Authentication کلیک کرده و فقط تیک مربوط به integrated windows authentication را قرار دهید. (همیشه تحت ویندوز این روش authentication یکی از امن‌ترین‌ها است. همچنین در حالت قرارگیری سرور بر روی اینترنت سخت گیرانه‌ترین حالت ممکن را در اینجا انتخاب کرده‌ایم.)



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

پس از اعمال این تنظیمات بر روی دکمه apply کلیک کنید، تا تنظیمات اعمال شوند. یکبار نیز میل سرور را استاپ و استارت کنید.

3- تنظیمات ویژه برنامه‌ها برای ارسال ایمیل:
در این حالت برنامه‌های دات نت شما نیاز به چهار تنظیم اضافه‌تر پیش از فراخوانی تابع Send دارند:

MailMessage message = new MailMessage("from@site.com", strTo, subject, body)
{
IsBodyHtml = true,
BodyEncoding = Encoding.UTF8
};

SmtpClient client = new SmtpClient("127.0.0.1",portNumber);//portNumber is new
client.UseDefaultCredentials = false; //new
client.DeliveryMethod = SmtpDeliveryMethod.Network; //new
client.Credentials = new NetworkCredential("mail_user", "pass"); //new
client.Send(message);

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

4- تمامی رخ‌دادهای میل سرور را ثبت کنید.
برای این منظور در برگه general ، تیک مربوط به enable logging را فعال کنید. سپس بر روی دکمه خواص کنار آن کلیک کرده و در صفحه باز شده به برگه extended properties مراجعه نموده و تمامی موارد را تیک بزنید. به ازای هر یک روز فعالیت سرور، یک فایل متنی در مسیر C:\WINDOWS\System32\LogFiles تشکیل خواهد شد.


سؤال چگونه تشخیص دهم که میل سرور من هک شده است یا خیر؟!
اگر موارد فوق را رعایت نکنید، در قسمت current sessions کنسول IIS می‌توانید "دشمنان" را مشاهده کنید! همچنین مصرف CPU پروسه inetinfo.exe عملکرد سرور را مختل کرده، بعلاوه در مسیر C:\Inetpub\mailroot\Queue احتمالا چند هزار ایمیل درصف قرار گرفته شده برای ارسال را می‌توانید مشاهده کنید! (همینطور در مسیر C:\Inetpub\mailroot\Badmail نیز این تعرض قابل مشاهده است)
اگر این موارد را مشاهده کردید، ابتدا سرور را استاپ کنید، سپس محتویات پوشه‌های یاد شده را تخلیه کرده و از مرحله یک فوق شروع به اعمال تنظیمات نمائید.


مطالب دوره‌ها
نگاهی به SignalR Clients
در قسمت قبل موفق به ایجاد اولین Hub خود شدیم. در ادامه، برای تکمیل برنامه نیاز است تا کلاینتی را نیز برای آن تهیه کنیم.
مصرف کنندگان یک Hub می‌توانند انواع و اقسام برنامه‌های کلاینت مانند jQuery Clients و یا حتی یک برنامه کنسول ساده باشند و همچنین Hubهای دیگر نیز قابلیت استفاده از این امکانات Hubهای موجود را دارند. تیم SignalR امکان استفاده از Hubهای آن‌را در برنامه‌های دات نت 4 به بعد، برنامه‌های WinRT، ویندوز فون 8، سیلورلایت 5، jQuery و همچنین برنامه‌های CPP نیز مهیا کرده‌اند. به علاوه گروه‌های مختلف نیز با توجه به سورس باز بودن این مجموعه، کلاینت‌های iOS Native، iOS via Mono و Android via Mono را نیز به این لیست اضافه کرده‌اند.


بررسی کلاینت‌های jQuery

با توجه به پروتکل مبتنی بر JSON سیگنال‌آر، استفاده از آن در کتابخانه‌های جاوا اسکریپتی همانند jQuery نیز به سادگی مهیا است. برای نصب آن نیاز است در کنسول پاور شل نوگت، دستور زیر را صادر کنید:
 PM> Install-Package Microsoft.AspNet.SignalR.JS
برای نمونه به solution پروژه قبل، یک برنامه وب خالی دیگر را اضافه کرده و سپس دستور فوق را بر روی آن اجرا نمائید. در این حالت فقط باید دقت داشت که فرامین بر روی کدام پروژه اجرا می‌شوند:


با استفاده از افزونه SignalR jQuery، به دو طریق می‌توان به یک Hub اتصال برقرار کرد:
الف) استفاده از فایل proxy تولید شده آن (این فایل، در زمان اجرای برنامه تولید می‌شود و یا امکان استفاده از آن به کمک ابزارهای کمکی نیز وجود دارد)
نمونه‌ای از آن‌را در قسمت قبل ملاحظه کردید؛ همان فایل تولید شده در مسیر /signalr/hubs برنامه. به نوعی به آن Service contract نیز گفته می‌شود (ارائه متادیتا و قراردادهای کار با یک سرویس Hub). این فایل همانطور که عنوان شد به صورت پویا در زمان اجرای برنامه ایجاد می‌شود.
امکان تولید آن توسط برنامه کمکی signalr.exe نیز وجود دارد؛ برای دریافت آن می‌توان از طریق NuGet اقدام کرد (بسته Microsoft.AspNet.SignalR.Utils) که نهایتا در پوشه packages قرار خواهد گرفت. نحوه استفاده از آن نیز به صورت زیر است:
 Signalr.exe ghp http://localhost/
در این دستور ghp مخفف generate hub proxy است و نهایتا فایلی را به نام server.js تولید می‌کند.

ب) بدون استفاده از فایل proxy و به کمک روش late binding (انقیاد دیر هنگام)

برای کار با یک Hub از طریق jQuery مراحل ذیل باید طی شوند:
1) ارجاعی به Hub باید مشخص شود.
2) روال‌های رخدادگردان تنظیم گردند.
3) اتصال به Hub برقرار گردد.
4) متدی فراخوانی شود.

در اینجا باید دقت داشت که امکانات Hub به صورت خواص
 $.connection
در سمت کلاینت جی‌کوئری، در دسترس خواهند بود. برای مثال:
 $.connection.chatHub
و نام‌های بکارگرفته شده در اینجا مطابق روش‌های متداول نام گذاری در جاوا اسکریپت، camel case هستند.

خوب، تا اینجا فرض بر این است که یک پروژه خالی ASP.NET را آغاز و سپس فرمان نصب Microsoft.AspNet.SignalR.JS را نیز همانطور که عنوان شد، صادر کرده‌اید. در ادامه یک فایل ساده html را به نام chat.htm، به این پروژه جدید اضافه کنید (برای استفاده از کتابخانه جاوا اسکریپتی SignalR الزامی به استفاده از صفحات کامل پروژه‌های وب نیست).
<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script>
    <script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
</head>
<body>
    <div>
        <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" />
        <ul id="messages">
        </ul>
    </div>
    <script type="text/javascript">
        $(function () {
            var chat;
            $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ می‌کند
            chat = $.connection.chat; //این نام مستعار پیشتر توسط ویژگی نام هاب تنظیم شده است
            chat.client.hello = function (message) {
                //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است
                //به این ترتیب سرور می‌تواند کلاینت را فراخوانی کند
                $("#messages").append("<li>" + message + "</li>");
            };
            $.connection.hub.start(/*{ transport: 'longPolling' }*/); // فاز اولیه ارتباط را آغاز می‌کند

            $("#send").click(function () {
                // Hub's `SendMessage` should be camel case here
                chat.server.sendMessage($("#txtMsg").val());
            });
        });
    </script>
</body>
</html>
کدهای آن‌را به نحو فوق تغییر دهید.
توضیحات:
همانطور که ملاحظه می‌کنید ابتدا ارجاعاتی به jquery و jquery.signalR-1.0.1.min.js اضافه شده‌اند. سپس نیاز است مسیر دقیق فایل پروکسی هاب خود را نیز مشخص کنیم. اینکار با تعریف مسیر signalr/hubs انجام شده است.
<script src="http://localhost:1072/signalr/hubs" type="text/javascript"></script>
در ادامه توسط تنظیم connection.hub.logging سبب خواهیم شد تا اطلاعات بیشتری در javascript console مرورگر لاگ شود.
سپس ارجاعی به هاب تعریف شده، تعریف گردیده است. اگر از قسمت قبل به خاطر داشته باشید، توسط ویژگی HubName، نام chat را برگزیدیم. بنابراین connection.chat ذکر شده دقیقا به این هاب اشاره می‌کند.
سپس سطر chat.client.hello مقدار دهی شده است. متد hello، متدی dynamic و تعریف شده در سمت هاب برنامه است. به این ترتیب می‌توان به پیام‌های رسیده از طرف سرور گوش فرا داد. در اینجا، این پیام‌ها، به li ایی با id مساوی messages اضافه می‌شوند.
سپس توسط فراخوانی متد connection.hub.start، فاز negotiation شروع می‌شود. در اینجا حتی می‌توان نوع transport را نیز صریحا انتخاب کرد که نمونه‌ای از آن را به صورت کامنت شده جهت آشنایی با نحوه تعریف آن مشاهده می‌کنید. مقادیر قابل استفاده در آن به شرح زیر هستند:
 - webSockets
- forverFrame
- serverSentEvents
- longPolling
سپس به رویدادهای کلیک دکمه send گوش فرا داده و در این حین، اطلاعات TextBox ایی با id مساوی txtMsg را به متد SendMessage هاب خود ارسال می‌کنیم. همانطور که پیشتر نیز عنوان شد، در سمت کلاینت، تعریف متد SendMessage باید camel case باشد.

اکنون به صورت جداگانه یکبار برنامه hub را در مرورگر باز کنید. سپس بر روی فایل chat.htm کلیک راست کرده و گزینه مشاهده آن را در مرورگر نیز انتخاب نمائید (گزینه View in browser منوی کلیک راست).

خوب! پروژه کار نمی‌کند! برای اینکه مشکلات را بهتر بتوانید مشاهده کنید نیاز است به JavaScript Console مرورگر خود مراجعه نمائید. برای مثال در مرورگر کروم دکمه F12 را فشرده و برگه Console آن‌را باز کنید. در اینجا اعلام می‌کند که فاز negotiation قابل انجام نیست؛ چون مسیر پیش فرضی را که انتخاب کرده است، همین مسیر پروژه دومی است که اضافه کرده‌ایم (کلاینت ما در پروژه دوم قرار دارد و نه در همان پروژه اول هاب).
برای اینکه مسیر دقیق hub را در این حالت مشخص کنیم، سطر زیر را به ابتدای کدهای جاوا اسکریپتی فوق اضافه نمائید:
 $.connection.hub.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
اکنون اگر مجدا سعی کنید، باز هم برنامه کار نمی‌کند و پیام می‌دهد که امکان دسترسی به این سرویس از خارج از دومین آن میسر نیست. برای اینکه این مجوز را صادر کنیم نیاز است تنظیمات مسیریابی پروژه هاب را به نحو ذیل ویرایش نمائیم:
using System;
using System.Web;
using System.Web.Routing;
using Microsoft.AspNet.SignalR;

namespace SignalR02
{
    public class Global : HttpApplication
    {
        protected void Application_Start(object sender, EventArgs e)
        {
            // Register the default hubs route: ~/signalr
            RouteTable.Routes.MapHubs(new HubConfiguration
            {
                EnableCrossDomain = true
            });
        }
    }
}
با تنظیم EnableCrossDomain به true اینبار فاز‌های آغاز ارتباط با سرور برقرار می‌شوند:
 SignalR: Auto detected cross domain url. jquery.signalR-1.0.1.min.js:10
SignalR: Negotiating with 'http://localhost:1072/signalr/negotiate'. jquery.signalR-1.0.1.min.js:10
SignalR: SignalR: Initializing long polling connection with server. jquery.signalR-1.0.1.min.js:10
SignalR: Attempting to connect to 'http://localhost:1072/signalr/connect?transport=longPolling&connectionToken…NRh72omzsPkKqhKw2&connectionData=%5B%7B%22name%22%3A%22chat%22%7D%5D&tid=3' using longPolling. jquery.signalR-1.0.1.min.js:10
SignalR: Longpolling connected jquery.signalR-1.0.1.min.js:10
مطابق این لاگ‌ها ابتدا فاز negotiation انجام می‌شود. سپس حالت long polling را به صورت خودکار انتخاب می‌کند.

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

یک نکته:
اگر از ویندوز 8 (یعنی IIS8) و VS 2012 استفاده می‌کنید، برای استفاده از حالت Web socket، ابتدا فایل وب کانفیگ برنامه را باز کرده و در قسمت httpRunTime، مقدار ویژگی targetFramework را بر روی 4.5 تنظیم کنید. اینبار اگر مراحل negotiation را بررسی کنید در همان مرحله اول برقراری اتصال، از روش Web socket استفاده گردیده است.


تمرین 1
به پروژه ساده و ابتدایی فوق یک تکست باکس دیگر به نام Room را اضافه کنید؛ به همراه دکمه join. سپس نکات قسمت قبل را در مورد الحاق به یک گروه و سپس ارسال پیام به اعضای گروه را پیاده سازی نمائید. (تمام نکات آن با مطلب فوق پوشش داده شده است و در اینجا باید صرفا فراخوانی متدهای عمومی دیگری در سمت هاب، صورت گیرد)

تمرین 2
در انتهای قسمت دوم به نحوه ارسال پیام از یک هاب به هابی دیگر اشاره شد. این MonitorHub را ایجاد کرده و همچنین یک کلاینت جاوا اسکریپتی را نیز برای آن تهیه کنید تا بتوان اتصال و قطع اتصال کلیه کاربران سیستم را مانیتور و مشاهده کرد.


پیاده سازی کلاینت jQuery بدون استفاده از کلاس Proxy

در مثال قبل، از پروکسی پویای مهیای در آدرس signalr/hubs استفاده کردیم. در اینجا قصد داریم، بدون استفاده از آن نیز کار برپایی کلاینت را بررسی کنیم.
بنابراین یک فایل جدید html را مثلا به نام chat_np.html به پروژه دوم برنامه اضافه کنید. سپس محتویات آن‌را به نحو زیر تغییر دهید:
<!DOCTYPE>
<html>
<head>
    <title></title>
    <script src="Scripts/jquery-1.6.4.min.js" type="text/javascript"></script>
    <script src="Scripts/jquery.signalR-1.0.1.min.js" type="text/javascript"></script>
</head>
<body>
    <div>
        <input id="txtMsg" type="text" /><input id="send" type="button" value="send msg" />
        <ul id="messages">
        </ul>
    </div>
    <script type="text/javascript">
        $(function () {                        
            $.connection.hub.logging = true; //اطلاعات بیشتری را در جاوا اسکریپت کنسول مرورگر لاگ می‌کند

            var connection = $.hubConnection();
            connection.url = 'http://localhost:1072/signalr'; //چون در یک پروژه دیگر قرار داریم
            var proxy = connection.createHubProxy('chat');

            proxy.on('hello', function (message) {
                //متدی که در اینجا تعریف شده دقیقا مطابق نام متد پویایی است که در هاب تعریف شده است
                //به این ترتیب سرور می‌تواند کلاینت را فراخوانی کند
                $("#messages").append("<li>" + message + "</li>");
            });

            $("#send").click(function () {
                // Hub's `SendMessage` should be camel case here
                proxy.invoke('sendMessage', $("#txtMsg").val());
            });

            connection.start();
        });
    </script>
</body>
</html>
در اینجا سطر مرتبط با تعریف مسیر اسکریپت‌های پویای signalr/hubs را دیگر در ابتدای فایل مشاهده نمی‌کنید. کار تشکیل proxy اینبار از طریق کدنویسی صورت گرفته است. پس از ایجاد پروکسی، برای گوش فرا دادن به متدهای فراخوانی شده از طرف سرور از متد proxy.on و نام متد فراخوانی شده سمت سرور استفاده می‌کنیم و یا برای ارسال اطلاعات به سرور از متد proxy.invoke به همراه نام متد سمت سرور استفاده خواهد شد.


کلاینت‌های دات نتی SignalR
تا کنون Solution ما حاوی یک پروژه Hub و یک پروژه وب کلاینت جی‌کوئری است. به همین Solution، یک پروژه کلاینت کنسول ویندوزی را نیز اضافه کنید.
سپس در خط فرمان پاور شل نوگت دستور زیر را صادر نمائید تا فایل‌های مورد نیاز به پروژه کنسول اضافه شوند:
 PM> Install-Package Microsoft.AspNet.SignalR.Client
در اینجا نیز باید دقت داشت تا دستور بر روی default project صحیحی اجرا شود (حالت پیش فرض، اولین پروژه موجود در solution است).
پس از نصب آن اگر به پوشه packages مراجعه کنید، نگارش‌های مختلف آن‌را مخصوص سیلورلایت، دات نت‌های 4 و 4.5، WinRT و ویندوز فون8 نیز می‌توانید در پوشه Microsoft.AspNet.SignalR.Client ملاحظه نمائید. البته در ابتدای نصب، انتخاب نگارش مناسب، بر اساس نوع پروژه جاری به صورت خودکار صورت می‌گیرد.
مدل برنامه نویسی آن نیز بسیار شبیه است به حالت عدم استفاده از پروکسی در حین استفاده از jQuery که در قسمت قبل بررسی گردید و شامل این مراحل است:
1) یک وهله از شیء HubConnection را ایجاد کنید.
2) پروکسی مورد نیاز را جهت اتصال به Hub از طریق متد CreateProxy تهیه کنید.
3) رویدادگردان‌ها را همانند نمونه کدهای جاوا اسکریپتی قسمت قبل، توسط متد On تعریف کنید.
4) به کمک متد Start، اتصال را آغاز نمائید.
5) متدها را به کمک متد Invoke فراخوانی نمائید.

using System;
using Microsoft.AspNet.SignalR.Client.Hubs;

namespace SignalR02.WinClient
{
    class Program
    {
        static void Main(string[] args)
        {
            var hubConnection = new HubConnection(url: "http://localhost:1072/signalr");
            var chat = hubConnection.CreateHubProxy(hubName: "chat");

            chat.On<string>("hello", msg => {
                Console.WriteLine(msg);
            });

            hubConnection.Start().Wait();

            chat.Invoke<string>("sendMessage", "Hello!");

            Console.WriteLine("Press a key to terminate the client...");
            Console.Read();
        }
    }
}
نمونه‌ای از این پیاده سازی را در کدهای فوق ملاحظه می‌کنید که از لحاظ طراحی آنچنان تفاوتی با نمونه ذهنی جاوا اسکریپتی ندارد.

نکته مهم
کلیه فراخوانی‌هایی که در اینجا ملاحظه می‌کنید غیرهمزمان هستند.
به همین جهت پس از متد Start، متد Wait ذکر شده‌است تا در این برنامه ساده، پس از برقراری کامل اتصال، کار invoke صورت گیرد و یا زمانیکه callback تعریف شده توسط متد chat.On فراخوانی می‌شود نیز این فراخوانی غیرهمزمان است و خصوصا اگر نیاز است رابط کاربری برنامه را در این بین به روز کنید باید به نکات به روز رسانی رابط کاربری از طریق یک ترد دیگر دقت داشت.