مطالب
اضافه شدن پشتیبانی از unsigned right shift به C# 11
به C# 11، عملگر جدیدی به شکل <<< و به معنای unsigned right shift اضافه شده‌است که ... در زبان جاوا از نگارش ابتدایی آن حضور داشته‌است. اما ... چرا از این لحاظ بین این دو زبان، تفاوت وجود داشته‌است؟


مفهوم عملگر شیفت در #C

عملگر شیفت به سمت راست و یا <<، عددی را به تعداد بیت مشخص شده (x >> count)، به سمت راست منتقل می‌کند و دو نوع دارد:
الف) شیفت به راست منطقی
برای مثال اگر عدد 12 را به صورت باینری نمایش دهیم، به صورت زیر خواهد بود:
00000000 00000000 00000000 00001100
و اگر آن‌را به اندازه‌ی یک بیت به سمت راست هدایت کنیم، که با 1 <<< 12  نمایش داده می‌شود:
00000000 00000000 00000000 00000110
به عدد 6 خواهیم رسید.
در این حالت همواره فرض می‌شود که عدد مدنظر، unsigned است.

ب) شیفت به راست ریاضی
شیفت به راست ریاضی، دقیقا مانند شیفت به راست منطقی است؛ مانند مثال زیر که عدد 1001  باینری را دو بیت به سمت راست منتقل می‌کند:
uint e = 0b_1001;
Console.WriteLine($"Before: {Convert.ToString(e, toBase: 2),4}");
// Before: 1001

uint f = e >> 2;
Console.WriteLine($"After:  {Convert.ToString(f, toBase: 2).PadLeft(4, '0'),4}");
// After:  0010
اما ... بجای اینکه همانند شیفت به راست منطقی، سمت چپ را با صفر پر کند، آن‌را با «با ارزش‌ترین بیت یا همان بیت علامت» پر می‌کند. یعنی در اینجا بیتی که بیانگر مثبت و منفی بودن عدد است، حفظ می‌شود. یعنی این نوع شیفت، با اعداد signed هم کار می‌کند.
برای مثال نمایش باینری عدد منفی 2,147,483,552-  به صورت زیر است:
10000000 00000000 00000000 01100000
و اگر آن‌را چهار بیت به سمت راست هدایت کنیم (یعنی 4 << 2,147,483,552 -)، به عدد 134,217,722- می‌رسیم که معادل عدد باینری زیر است:
11111000 00000000 00000000 00000110
به این ترتیب با شیفت به راست ریاضی، علامت عدد منفی حفظ شده‌است. مثالی دیگر:
int x = -8;
Console.WriteLine($"Before: {x,11}, hex: {x,8:x}, binary: {Convert.ToString(x, toBase: 2),32}");
// Before: -8, hex: fffffff8, binary: 11111111111111111111111111111000

int y = x >> 2;
Console.WriteLine($"After  >>: {y,11}, hex: {y,8:x}, binary: {Convert.ToString(y, toBase: 2),32}");
// After  >>: -2, hex: fffffffe, binary: 11111111111111111111111111111110

int z = x >>> 2;
Console.WriteLine($"After >>>: {z,11}, hex: {z,8:x}, binary: {Convert.ToString(z, toBase: 2).PadLeft(32, '0'),32}");
// After >>>:  1073741822, hex: 3ffffffe, binary: 00111111111111111111111111111110


سؤال: در زبان جاوا، عملگر <<< به معنای unsigned right shift است؛ اما چنین عملگری در زبان #C تا نگارش 11 آن وجود ندارد؛ چرا؟!

پاسخ: چون زبان جاوا، فاقد نوع‌های داده‌ای توکار unsigned integers است (^)؛ برخلاف #C از نگارش یک آن. حتی به ظاهر، این امکان در Java 8 اضافه شده‌است، اما در حقیقت با نوع‌های int و long، فقط مانند اینکه unsigned هم می‌توانند باشند، رفتار می‌کند (^). البته نوع char در زبان Java، تنها نوع unsigned واقعی است.
همانطور که عنوان شد، در زبان #C فقط کافی است بر روی unsigned types مانند ulong, uint, ushort، عملگر << را بکار برد تا به  unsigned right shift جاوا رسید (به همین جهت عملگر اضافه‌تری برای آن ارائه نشده بود). البته باید دقت داشت که در اینجا عملگر << کار پر کردن MSB یا «با ارزش‌ترین بیت یا همان بیت علامت» را هم با صفر انجام می‌دهد؛ حتی اگر MSB مقدار دهی شده باشد (چون این کاری است که << بر روی unsigned types انجام می‌دهد).
تا پیش از C# 11 اگر نیاز به کار بر روی signed types جهت رسیدن به نتیجه‌ی عملگر <<< وجود داشته باشد (انجام شیفت منطقی؛ یعنی صرفنظر کردن از نوع علامت عدد)، می‌توان از متدهای الحاقی زیر استفاده کرد که ابتدا آن‌ها را به نمونه‌های unsigned تبدیل می‌کند و کار شیفت را انجام می‌دهد و سپس نوع اصلی را بازیابی می‌کند:
public static int UnsignedRightShift(this int signed, int places)
{
    unchecked
    {
        var unsigned = (uint)signed;
        unsigned >>= places;
        return (int)unsigned;
    }
}

public static long UnsignedRightShift(this long signed, int places)
{
    unchecked
    {
        var unsigned = (ulong)signed;
        unsigned >>= places;
        return (long)unsigned;
    }
}
مطالب
ویژگی های آگهی استخدام مناسب همراه با نقدی بر یک آگهی استخدام
امروز فرصتی پیش آمد تا سری هم به بخش آگهی‌های این سایت بزنم. با یک آگهی استخدام (مربوط به شرکتی تحت عنوان فناوران سیستم دلفین) روبرو شدم که نظرم را جلب کرد. تقریبا مدت‌ها بود که با یک آگهی استخدام به این صورت روبرو نشده بودم. در رشته کامپیوتر و اغلب در بخش تولید و پشتیبانی نرم افزار کمتر با آگهی‌های استخدام این چنینی که کامل باشند(البته دارای ضعف هایی نیز بود) روبرو می‌شویم. تصمیم گرفتم در این پست به تشریح ویژگیهای یک آگهی استخدام خوب (همراه با نقدی بر این آگهی استخدام ) بپردازم. یک جمله معروف وجود دارد با این مضمون که، استخدام دو مرحله دارد:
  • جذب
  • انتخاب یا گزینش

مرحله جذب:

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

مرحله انتخاب:

این مرحله برای گزینش افراد است که همگان از  آن به عنوان مرحله مصاحبه یاد می‌کنند که مورد بحث ما نیست.

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

»ذکر نام شرکت

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

»ذکر آدرس و شماره تماس:

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

» ذکر شرایط استخدام

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

»ذکر شرایط تخصصی متقاضیان کار:

در بخش به صورت کامل شرایط تخصصی و مهارتی افراد مورد بررسی قرار گرفته بود. در یک نگاه خوب به نظر می‌رسید ولی اگر با دقت به این بخش نگاه کنید متوجه خواهید شد به دلیل عدم نگارش صحیح موارد مورد نیاز، کمتر کسی دارای این همه توانایی است آن هم به صورت تسلط کامل. استفاده از واژه تسلط کامل باید با احتیاط انجام شود. برای مثال تسلط کامل بر VB.NET , C#.NET Windows Application . بهتر بود از یا استفاده می‌شد. به این صورت " تسلط بر VB.NET یا C#.NET  در Windows Application"

» ذکر اولویت ها

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

»ذکر آدرس وب سایت شرکت:

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

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

»عدم توازن بین عنوان آگهی با محتوای آن :

در عنوان آگهی ذکر شده بود "برنامه نویس تحت وب" در حالی که در شرایط تخصصی، موردی به عنوان تسلط کامل بر برنامه نویس تحت ویندوز ذکر شده  آن هم با دو زبان Vb.Net و C#.Net

آیا منظور از تسلط کامل بر مدیریت بانک اطلاعاتی SqlServer همان DBA است؟ اگر پاسخ مثبت است در این صورت چند نفر واجد شرایط را می‌توان پیدا کرد که هم در سطح DBA باشند و هم در سطح یک برنامه نویس سطح بالا؟ اگر پاسخ منفی است بهتر بود از واژه داشتن تجربه در زمینه مدیریت بانک اطلاعاتی یا حتی آشنایی با مدیریت بانک‌های اطلاعاتی استفاده می‌شد.

مورد دیگری که تا حدودی باعث سردرگمی می‌شد این است که نیاز به تسلط کامل به Asp.Net Web Form و هم چنین به Asp.Net MVC است. چرا هردو، آن هم در سطح تسلط کامل؟

در بخش اولویت‌ها موردی ذکر شد با عنوان آشنایی با کامپوننت‌های برنامه نویسی نظیر DevExpress و Telerik  و Kendo. آیا منظور آشنایی با تمام این موارد بوده است یا فقط یکی از آن ها؟

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

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

مطالب
کارهایی جهت بالابردن کارآیی Entity Framework #3

در قسمت‌های قبلی (^ و ^) راهکارهایی جهت بالا بردن کارآیی، ارائه شد. در ادامه، به آخرین قسمت این سری اشاره خواهم کرد.

فراخوانی متد شناسایی تغییرات

یادآوری: قبل از هر چیز با توجه به این مقاله دانستن این نکته الزامی است که فراخوانی برخی متدها مانند DbSet.Add سبب فراخوانی DataContext.ChangeTracker.DetectChanges خواهند شد.

فرض کنید قصد افزودن 2000 موجودیت دانش آموز را دارید:

for (int i = 0; i < 2000; i++)
{
    Pupil pupil = GetNewPupil();
    db.Pupils.Add(pupil);
}
db.SaveChanges();
در کد بالا بدلیل فراخوانی متد DbSet.Add و به دنبال آن فراخوانی متد DetectChanges در هر بار اجرای حلقه (2000بار) مدت زمان اجرای کد بالا بسیار زیاد است و اگر به پروفایلر نگاهی بیاندازید، اشغال CPU توسط کوئری بالا، بیش از حد متعارف است.

اگر به تصویر بالا دقت کنید بیش از 34 ثانیه (خط 193 قسمت سوم شکل) جهت افزودن 2000 موجودیت به کانتکست سپری شده است. در حالی که درج این 2000 موجودیت کمی بیش از 1 ثانیه (خط 195 قسمت سوم شکل) که 379 میلی ثانیه (قسمت دوم شکل) آن مربوط به اجرای کوئری اختصاص یافته  طول کشیده است.
بیشترین زمان صرف شده‌ی برای درج 2000 موجودیت، در کد برنامه سپری شده است که با بررسی بیشتر در پروفایلر، متوجه زمان بر بودن فراخوانی متد ()DetectChanges که در فضای نام Data.Entity.Core وجود دارد خواهید شد. این متد 2000 بار به تعداد موجودیت هایی که قصد داریم به بانک اطلاعاتی اضافه نماییم، فراخوانی شده است.

همانطور که در شکل بالا مشخص است همان 34 ثانیه جهت ردیابی تغییرات صرف شده است. EF ردیابی تغییرات را بصورت پیش فرض هر زمانی که قصد افزودن یا ویرایش موجودیتی را داشته باشید، انجام خواهد داد و هر چه موجودیت‌های بیشتری را بخواهید ویرایش یا اضافه نمایید، این زمان نیز بیشتر خواهد شد. در حقیقت زمان لازم برای الگوریتم ردیابی تغییرات بصورت نمایی با رشد موجودیت‌ها افزوده می‌شود. به عبارت دیگر اگر این تعداد موجودیت‌ها را به 4000 عدد برسانید، مدت زمان لازم بیش از 2 برابر افزوده خواهد شد.

راه حل اول:
استفاده از متد ()AddRange ارائه شده در EF6 که جهت درج دسته‌ای (Bulk Insert) ارائه شده است:
var list = new List<Pupil>();

for (int i = 0; i < 2000; i++)
{
    Pupil pupil = GetNewPupil();
    list.Add(pupil);
}

db.Pupils.AddRange(list);
db.SaveChanges();

راه حل دوم:

در سناریوهای پیچیده، مانند درج دسته‌ای چندین موجودیت، شاید مجبور به خاموش نمودن این قابلیت شوید:
db.Configuration.AutoDetectChangesEnabled = false;
توجه داشته باشید اگر قصد دارید از امکان ردیابی تغییرات مجددا بهره‌مند شوید، باید این قابلیت را نیز فعال نمایید. با خاموش نمودن ردیابی تغییرات بار دیگر کوئری ابتدایی را اجرا نمایید. مدت زمان لازم جهت افزودن 2000 موجودیت به کانتکست از بیش از 34 ثانیه به 85 میلی ثانیه کاهش یافته است؛ ولی اعمال تغییرات به بانک اطلاعاتی همانند مرتبه اول بیش از 1 ثانیه طول خواهد کشید.


ردیابی تغییرات

هنگامی که موجودیتی را از بانک اطلاعاتی دریافت نمایید، می‌توانید آن را ویرایش نمایید و مجددا به بانک اطلاعاتی اعمال نمایید. چون EF اطلاعی از قصد شما برای موجودیت نمی‌داند، مجبور است تغییرات شما را زیر نظر بگیرد که این زیر نظر گرفتن، هزینه و سربار دارد و این سربار و هزینه برای داده‌های زیاد، بیشتر خواهد شد. بنابراین اگر قصد دارید اطلاعاتی فقط خواندنی را از بانک اطلاعاتی دریافت نمایید، بهتر است صراحتا به EF بگویید این موجودیت را تحت ردیابی قرار ندهد.
string city = "New York";

List<School> schools = db.Schools
    .AsNoTracking()
    .Where(s => s.City == city)
    .Take(100)
    .ToList();

استفاده از متد AsNoTracking  در کد بالا سبب خواهد شد 100 مدرسه که در شهر نیویورک وجود دارد توسط EF، بدون تحت نظر گرفتن آن‌ها از بانک اطلاعاتی دریافت شوند و سرباری نیز تحمیل نشود.

ویوهای از قبل کامپایل شده

معمولا، هنگامی که از EF برای اولین بار استفاده می‌نمایید، ویوهایی ایجاد می‌گردد که برای ایجاد کوئری‌ها مورد استفاده قرار می‌گیرند. این ویوها در طول حیات برنامه فقط یکبار ایجاد می‌شوند. ولی همین یکبار هم زمانبر هستند. خوشبختانه راه‌هایی وجود دارد که ایجاد این ویوها را در زمان runtime انجام نداد و آن راه، استفاده از ویوهای از پیش کامپایل شده است. یکی از راههای ایجاد این ویوها استفاده از Entity Framework Power Tools است. بعد از نصب اکستنشن، بر روی فایل کانتکست راست کلیک کرده و سپس گزینه‌ی Generate Views را از منوی Entity Framework انتخاب کنید.

توجه داشته باشید که هر تغییری را بعد از ایجاد این ویوها بر روی کانتکست اعمال نمایید، باید آن‌ها را مجددا تولید کنید. برای آشنایی بیشتر با این ویوها به این لینک مراجعه کنید. هم چنین پکیج نیوگتی بنام EFInteractiveViews نیز برای این منظور تهیه و توزیع شده است.


حذف کوئری‌های ابتدایی غیر ضروری

در هنگام شروع به کار با EF، چندین کوئری آغازین بر روی دیتابیس اجرا می‌شوند. یکی از کوئری‌های آغازین جهت تشخیص نسخه‌ی دیتابیس است که همانطور در تصویر زیر مشاهده می‌کنید، در حدود چند میلی ثانیه می‌باشد.

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

 public class CustomManifestTokenResolver : IManifestTokenResolver
{
    public string ResolveManifestToken(DbConnection connection)
    {
        return "2014";
    }
}
و توسط کلاسی که از کلاس DbConfiguration ارث بری کرده است آن را استفاده نماییم.
 public class CustomDbConfiguration : DbConfiguration
{
    public CustomDbConfiguration()
    {
        SetManifestTokenResolver(new CustomManifestTokenResolver());
    }
}


تخریب کانتکست

تخریب و از بین بردن کانتکست هنگامی که به آن نیاز نداریم بسیار ضروری است. یکی از روشهای اصولی برای Disposing کانتکست، محصور کردن آن بوسیله دستور Using است (البته فرض بر این است که قرار نیست از الگوهای اشاره شده استفاده نماییم). در صورت عدم تخریب صحیح کانتکست باید منتظر آسیب جدی به کارایی Garbage Collector جهت آزاد سازی منابع مورد استفاده کانتکست و هم چنین باز نمودن اتصالات جدید به دیتابیس باشید.


پاسخگویی به چندین درخواست بر روی یک کانکشن

EF از قابلیتی بنام Multiple Result Sets می‌تواند بهره ببرد که این قابلیت باعث می‌شود بر روی یک کانکشن ایجاد شده، یک یا چند درخواست از دیتابیس ارسال و یا دریافت شود که سبب کاهش تعداد رفت و برگشت به دیتابیس می‌شود. کاربرد دیگر این قابلیت، زمانی است که تاخیر زیادی (latency) بین اپلیکیشن و دیتابیس وجود دارد.

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

MultipleActiveResultSets=True;


استفاده از متدهای ناهمگام

در C#5 و EF6 پشتیبانی خوبی از متدهای ناهمگام فراهم شده است و اکثر متدهایی مانند ToListAsync, CountAsync, FirstAsync, SaveChangesAsync و غیره که باعث رفت و برگشت به دیتابیس می‌شوند امکان پشتیبانی ناهمگام را نیز دارند. البته این قابلیت برای برنامه‌های یک درخواست در یک زمان شاید مفید نباشد؛ ولی برای برنامه‌های وبی برعکس. در برنامه وب جهت پشتیبانی از بارگذاری همزمان (concurrent) قابلیت ناهمگام (Async) سبب خواهد شد منابع تا زمان اجرای کوئری به ThreadPool بازگردانده شود و برای سرویس دهی مهیا باشند که باعث افزایش scalability خواهد شد.

بررسی و آزمایش با داده‌های واقعی

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

مطالب
تاریخ شمسی با Extension Method برای DateTime
برای تبدیل تاریخ میلادی به شمسی راه‌های زیادی وجود داره که خوب این هم یک نوع از اونها هست.
برای این کار با استفاده از Extension Method برای کلاس DateTime تاریخ رو تبدیل می‌کنیم.
برای فرمت‌های‌های نمایشی تاریخ هم می‌بایست به صورت متن به Method بدهید تا تاریخ رو به اون صورتی که می‌خواهید نمایش بدهد، برای نوشتن فرمت‌ها هم می‌توانید از این لینک استفاده کنید.
خوب برای این کار ابتدا یک کلاس با نام PersianDateExtensionMethods  بسازید و تمام کد زیر رو جایگزین کد‌های کلاس کنید:
using System;
using System.Globalization;
using System.Reflection;

namespace System 
{
    public static class PersianDateExtensionMethods
    {
        private static CultureInfo _Culture;
        public static CultureInfo GetPersianCulture()
        {
            if (_Culture == null)
            {
                _Culture = new CultureInfo("fa-IR");
                DateTimeFormatInfo formatInfo = _Culture.DateTimeFormat;
                formatInfo.AbbreviatedDayNames = new[] { "ی", "د", "س", "چ", "پ", "ج", "ش" };
                formatInfo.DayNames = new[] { "یکشنبه", "دوشنبه", "سه شنبه", "چهار شنبه", "پنجشنبه", "جمعه", "شنبه" };
                var monthNames = new[]
                {
                    "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن",
                    "اسفند",
                    ""
                };
                formatInfo.AbbreviatedMonthNames =
                    formatInfo.MonthNames =
                    formatInfo.MonthGenitiveNames = formatInfo.AbbreviatedMonthGenitiveNames = monthNames;
                formatInfo.AMDesignator = "ق.ظ";
                formatInfo.PMDesignator = "ب.ظ";
                formatInfo.ShortDatePattern = "yyyy/MM/dd";
                formatInfo.LongDatePattern = "dddd, dd MMMM,yyyy";
                formatInfo.FirstDayOfWeek = DayOfWeek.Saturday;
                System.Globalization.Calendar cal = new PersianCalendar();

                FieldInfo fieldInfo = _Culture.GetType().GetField("calendar", BindingFlags.NonPublic | BindingFlags.Instance);
                if (fieldInfo != null)
                    fieldInfo.SetValue(_Culture, cal);

                FieldInfo info = formatInfo.GetType().GetField("calendar", BindingFlags.NonPublic | BindingFlags.Instance);
                if (info != null)
                    info.SetValue(formatInfo, cal);

                _Culture.NumberFormat.NumberDecimalSeparator = "/";
                _Culture.NumberFormat.DigitSubstitution = DigitShapes.NativeNational;
                _Culture.NumberFormat.NumberNegativePattern = 0;
            }
            return _Culture;
        }

        public static string ToPeString(this DateTime date,string format = "yyyy/MM/dd")
        {
            return date.ToString(format,GetPersianCulture());
        }
    }
}

در کد بالا ما بااستفاده از متد GetPersianCulture یک کالچر برای تاریخ شمسی و فرمت‌های اون می‌سازیم و در متد ToPeString از اون استفاده و به متد ToString از کلاس DateTime پاس می‌دهیم.
چند مثال هم برای استفاده از متد ToPeString :
var date1 = DateTime.Now.ToPeString("yyyy/MM/dd");
var date2 = DateTime.Now.ToPeString("dddd, dd MMMM,yyyy");

//Output:
//1391/12/13
//یکشنبه, 13 اسفند,1391

نکته : با استفاده از Culture ای که در کلاس بالا در متد GetPersianCulture ساخته می‌شود امکانش هست که خود کلاس DateTime رو به شمسی تبدیل کرد، برای این کار باید به صورت زیر عمل کرد:
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture =  PersianDateExtensionMethods.GetPersianCulture();

var d1 = DateTime.Now.ToString();
//Output : 1391/12/13 11:25:44 ب.ظ

یک نکته‌ی تکمیلی: کتابخانه‌ی «DNTPersianUtils.Core» به همراه نکات این مطلب است؛ مانند:
Thread.CurrentThread.CurrentCulture = PersianCulture.Instance;
مطالب دوره‌ها
مراحل Refactoring یک قطعه کد برای اعمال تزریق وابستگی‌ها
برای رسیدن به الگوی معکوس سازی وابستگی‌ها عموما مراحل زیر طی می‌شوند:

الف) در متدهای لایه جاری خود واژه‌های کلیدی new و همچنین کلیه فراخوانی‌های استاتیک را بیابید.
ب) وهله سازی این‌ها را به یک سطح بالاتر (نقطه آغازین برنامه) منتقل کنید. اینکار باید بر اساس اتکای به Abstraction و برای مثال استفاده از اینترفیس‌ها صورت گیرد.
ج) اینکار را آنقدر تکرار کنید تا دیگر در کدهای لایه جاری خود واژه کلیدی new یا فراخوانی متدهای استاتیک مشاهده نشود.
د) در آخر وهله سازی object graphهای مورد نیاز را به یک IoC Container محول کنید.


یک مثال: ابتدا بررسی یک قطعه کد متداول

using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Mvc;

namespace DI06.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            string result = string.Empty;
            using (var client = new WebClient { Encoding = Encoding.UTF8 })
            {
                result = client.DownloadString("https://www.dntips.ir/");
            }
            var match = new Regex(@"(?s)<title>(.+?)</title>", RegexOptions.IgnoreCase).Match(result);
            var title = match.Groups[1].Value.Trim();

            ViewBag.PageTitle = title;
            return View();
        }
    }
}
فرض کنید یک برنامه ASP.NET MVC را به نحو فوق تهیه کرده‌ایم. در کدهای کنترلر آن قصد داریم محتویات Html یک صفحه خاص را دریافت و سپس عنوان آن‌را استخراج کرده و نمایش دهیم.
مشکلات کد فوق:
الف) قرار گرفتن منطق تجاری پیاده سازی کدها مستقیما داخل کدهای یک اکشن متد؛ این مساله در دراز مدت به تکرار شدید کدها منجر خواهد شد که نهایتا قابلیت نگهداری آن‌را کاهش می‌دهند.
ب) در این کد حداقل دو بار واژه کلیدی new ذکر شده است. مورد اول یا new WebClient، از همه مهم‌تر است؛ از این جهت که نوشتن آزمون واحد را برای این کنترلر بسیار مشکل می‌کند. آزمون‌های واحد باید سریع و بدون نیاز به منابع خارجی، قابل اجرا باشند. تعویض آن هم مطابق کدهای تدارک دیده شده کار ساده‌ای نیست.


بهبود کیفیت قطعه کد متداول فوق با استفاده از الگوی معکوس سازی وابستگی‌ها

در اصل معکوس سازی وابستگی‌ها عنوان کردیم لایه بالایی سیستم نباید مستقیما به لایه‌های زیرین در حال استفاده از آن، وابسته باشد. این وابستگی باید معکوس شده و همچنین بر اساس Abstraction یا برای مثال استفاده از اینترفیس‌ها صورت گیرد.
به همین منظور یک پروژه دیگر را از نوع Class library، مثلا به نام DI06.Services به Solution جاری اضافه می‌کنیم.
namespace DI06.Services
{
    public interface IWebClientServices
    {
        string FetchUrl(string url);
        string GetWebPageTitle(string url);
    }
}

using System.Net;
using System.Text;
using System.Text.RegularExpressions;

namespace DI06.Services
{
    public class WebClientServices : IWebClientServices
    {
        public string FetchUrl(string url)
        {
            using (var client = new WebClient { Encoding = Encoding.UTF8 })
            {
                return client.DownloadString(url);
            }
        }

        public string GetWebPageTitle(string url)
        {
            var html = FetchUrl(url);
            var match = new Regex(@"(?s)<title>(.+?)</title>", RegexOptions.IgnoreCase).Match(html);
            return match.Groups[1].Value.Trim();
        }
    }
}
در این لایه، سرویس WebClient ایی را تدارک دیده‌ایم. این سرویس می‌تواند محتوای Html یک آدرس را برگرداند و یا عنوان آن آدرس خاص را استخراج نماید.
هنوز کار معکوس سازی وابستگی‌ها رخ نداده است. صرفا اندکی تمیزکاری و انتقال پیاده سازی منطق تجاری به یک سری کلاس‌هایی با قابلیت استفاده مجدد صورت گرفته است. به این ترتیب اگر باگی در این کدها وجود داشته باشد و همچنین از آن در چندین نقطه برنامه استفاده شده باشد، اصلاح این کلاس مرکزی، به یکباره تمامی قسمت‌های مختلف برنامه را تحت تاثیر مثبت قرار داده و از تکرار کدها و فراموشی احتمالی بهبود قسمت‌های مشابه جلوگیری می‌کند.
کار معکوس سازی وابستگی‌ها در یک لایه بالاتر صورت خواهد گرفت:
using System.Web.Mvc;
using DI06.Services;

namespace DI06.Controllers
{
    public class HomeController : Controller
    {
        readonly IWebClientServices _webClientServices;
        public HomeController(IWebClientServices webClientServices)
        {
            _webClientServices = webClientServices;
        }

        public ActionResult Index()
        {
            ViewBag.PageTitle = _webClientServices.GetWebPageTitle("https://www.dntips.ir/");
            return View();
        }
    }
}
اینبار کنترلر Home را توسط تزریق وابستگی‌ها در سازنده کنترلر، بازنویسی کرده‌ایم. کد نهایی بسیار تمیزتر از حالت قبل است. دیگر پیاده سازی متد GetWebPageTitle مستقیما داخل یک اکشن متد قرار نگرفته است. همچنین این کنترلر اصلا نمی‌داند که قرار است از کدام پیاده سازی اینترفیس IWebClientServices استفاده کند. اگر در تنظیمات اولیه IoC Container مورد استفاده، کلاس WebClientServices ذکر شده باشد، از آن استفاده خواهد کرد؛ یا اگر حتی کلاس WebClientServicesFake نیز معرفی گردد (جهت انجام آزمون غیر وابسته به new WebClient)، باز هم بدون کوچکترین تغییری در کدهای آن قابل استفاده خواهد بود.

در مورد نحوه تنظیمات اولیه یک IoC Container و یا پیشنیازهای ASP.NET MVC جهت آماده شدن برای تزریق خودکار وابستگی‌ها در سازنده کنترلرها، پیشتر مطالبی را در این سری مطالعه کرده‌اید؛ در اینجا نیز اصول مورد استفاده یکی است و تفاوتی نمی‌کند.
نظرات اشتراک‌ها
بهبودهای LINQ در دات نت شش
بله اتفاقا من اطلاعی از این موضوع نداشتم و مثل سابق نوشتم ولی دیدم رایدر پیشنهاد تغییر کد رو داد مطلع شدم