مطالب
بررسی واژه کلیدی static

تفاوت بین یک کلاس استاتیک، متدی استاتیک و یا متغیر عضو استاتیک چیست؟ چه زمانی باید از آن‌ها‌ استفاده کرد و لزوم بودن آن‌ها‌ چیست؟
برای پاسخ دادن به این سؤالات باید از نحوه‌ی تقسیم بندی حافظه شروع کرد.
RAM برای هر نوع پروسه‌ای که در آن بارگذاری می‌شود به سه قسمت تقسیم می‌گردد: Stack ، Heap و Static (استاتیک در دات نت در حقیقت قسمتی از Heap است که به آن High Frequency Heap نیز گفته می‌شود).
این قسمت استاتیک حافظه، محل نگهداری متدها و متغیرهای استاتیک است. آن متدها و یا متغیرهایی که نیاز به وهله‌ای از کلاس برای ایجاد ندارند، به صورت استاتیک ایجاد می‌گردند. در سی شارپ از واژه کلیدی static برای معرفی آن‌ها کمک گرفته می‌شود. برای مثال:

class MyClass
{
public static int a;
public static void DoSomething();
}
در این مثال برای فراخوانی متد DoSomething نیازی به ایجاد یک وهله جدید از کلاس MyClass نمی‌باشد و تنها کافی است بنویسیم:

MyClass.DoSomething(); // and not -> new MyClass().DoSomething();
نکته‌ی مهمی که در اینجا وجود دارد این است که متدهای استاتیک تنها قادر به استفاده از متغیرهای استاتیک تعریف شده در سطح کلاس هستند. علت چیست؟
به مثال زیر دقت نمائید:

class MyClass
{
// non-static instance member variable
private int a;
//static member variable
private static int b;
//static method
public static void DoSomething()
{
//this will result in compilation error as “a” has no memory
a = a + 1;
//this works fine since “b” is static
b = b + 1;
}
}
در این مثال اگر متد DoSomething را فراخوانی کنیم، تنها متغیر b تعریف شده، در حافظه حضور داشته (به دلیل استاتیک معرفی شدن) و چون با روش فراخوانی MyClass.DoSomething هنوز وهله‌ای از کلاس مذکور ایجاد نشده، به متغیر a نیز حافظه‌ای اختصاص داده نشده است و نامعین می‌باشد.
بر این اساس کامپایلر نیز از کامپایل شدن این کد جلوگیری کرده و خطای لازم را گوشزد خواهد کرد.

اکنون تعریف یک کلاس به صورت استاتیک چه اثری را خواهد داشت؟
با تعریف یک کلاس به صورت استاتیک مشخص خواهیم کرد که این کلاس تنها حاوی متدها و متغیرهای استاتیک می‌باشد. امکان ایجاد یک وهله از آن‌ها وجود نداشته و نیازی نیز به این امر ندارند. این کلاس‌ها امکان داشتن instance variables را نداشته و به صورت پیش فرض از نوع sealed به حساب خواهند آمد و امکان ارث بری از آن‌ها نیز وجود ندارد. علت این امر هم این است که یک کلاس static هیچ نوع رفتاری را تعریف نمی‌کند.

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

با کلاس‌های استاتیک نیز همانند سایر کلاس‌های یک برنامه توسط JIT compiler رفتار می‌شود، اما با یک تفاوت. کلاس‌های استاتیک فقط یکبار هنگام اولین دسترسی به آن‌ها ساخته شده و در قسمت High Frequency Heap حافظه قرار می‌گیرند. این قسمت از حافظه تا پایان کار برنامه از دست garbage collector‌ در امان است (بر خلاف garbage-collected heap‌ یا object heap که جهت instance classes مورد استفاده قرار می‌گیرد)


نکته:
در برنامه‌های ASP.Net از بکارگیری متغیرهای عمومی استاتیک برحذر باشید (از static fields و نه static methods). این متغیرها بین تمامی کاربران همزمان یک برنامه به اشتراک گذاشته شده و همچنین باید مباحث قفل‌گذاری و امثال آن‌را در محیط‌های چند ریسمانی هنگام کار با آن‌ها رعایت کرد (thread safe نیستند).

مطالب
مدیریت هدایت خودکار صفحات در حین خطاهای عملیاتی ای‌جکسی در ASP.NET MVC
قطعه کد زیر را در نظر بگیرید :
     [HttpPost,AjaxOnly, ValidateAntiForgeryToken]
        public virtual JsonResult Create(AddDeviceGroupViewModel deviceGroupViewModel)
        {
        
            if (ModelState.IsNotValid())
            {
                Response.StatusCode = (int)HttpStatusCode.BadRequest;
                return Json(new { success = false, message = ModelState.FirstErrorMessage(), notificationType = NotificationType.Error }, JsonRequestBehavior.AllowGet);
            }
            var result = _deviceGroupService.Add(deviceGroupViewModel);

           // other codes

        }
اکشن متد فوق به صورت زیر فراخوانی می‌شود :
@using (Ajax.BeginForm(MVC.Admin.DeviceGroup.Create(), 
new AjaxOptions { HttpMethod = "POST", OnSuccess = "saveAjaxForm", OnFailure = "SaveFailure" }))
{
// form content
}
و تابع جاوا اسکریپت SaveFailure  به صورت زیر پیاده سازی شده بود :
function SaveFailure(data) {
  
    $("button[type=submit]").prop('disabled', false);
    var result = $.parseJSON(data.responseText);
    showMessage(result.message, result.notificationType);
}
کار تابع فوق، فعال کردن مجدد دکمه ثبت و نمایش پیغامی به کاربر می‌باشد. قطعه کد فوق بدون هیچ مشکلی اجرا می‌شد تا اینکه تمامی اکشن متد‌هایی که به صورت ای‌جکس به صورت اکشن متد فوق پیاده سازی شده بودند، از کار افتادند.
مشکل به وجود آمده حاصل اضافه شدن تگ‌های زیر به وب کانفیگ بود :
<httpErrors errorMode="Custom">
      <remove statusCode="404"/>
      <error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
بعد از افزودن کد‌های فوق وقتی برنامه به خط زیر می‌رسید، سعی در انتقال به کنترلر Error می‌کرد:
Response.StatusCode = (int)HttpStatusCode.BadRequest;

و در خروجی تنها مقداری که به سمت کاربر برگشت داده می‌شد، مقدار BadRequest بود و خط زیر باعث خطا و توقف برنامه می‌شد:
    var result = $.parseJSON(data.responseText);
با کامنت کامنت کردن کدهای اضافه شده در Web.config مشکل فوق رفع خواهد شد.
همچنین در صورتیکه قصد داشتید تگ‌های فوق را در web.config داشته باشید (جهت هندل کردن صفحات پیدا نشده) می‌توانید از مقدار دهی TrySkipIISCustomError با true این مشکل را رفع کنید.
  Response.TrySkipIisCustomErrors = true;

روشی دیگر:
<system.webServer>
    <httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough" />
اشتراک‌ها
فعال سازی تست اتماتیک در ویژوال استودیو

پنجره VS Test Explorer یکی دیگر از ابزارهای مفید است که به منظور اجرا، مانیتور، دسته بندی، فیلتر و مدیریت تست‌های واحد کاربرد دارد، همچنین با استفاده از این پنجره قادر به اجرا و مشاهده کلیه تست‌های نوشته شده در سطح پروژه به صورت دستی پرداخت...

فعال سازی تست اتماتیک در ویژوال استودیو
نظرات مطالب
اعمال تزریق وابستگی‌ها به مثال رسمی ASP.NET Identity
البته پنجره گرافیکی nuget هم  این امکان رو میده که مجبور نباشیم تک تکشون رو نصب کنیم
به محض باز شدن پنجره ، خودش شناسایی میکنه چه بسته‌های تعریف شدند ولی موجود نیست و با کلیک بر دکمه مربوطه همه رو نصب میکنه
اشتراک‌ها
مقاله ای کامل درباره استفاده از MVC و jQuery و JSON و paging و mapRoute
How to make CRUD with good performance in MVC
How to use jQuery dialog instead of JavaScript confirm or alert
How to make paging in an MVC list
How to make "show more" link using jQuery in MVC
How to use attributes with link
How to make a AJAX call in jQuery
How to use the Form collection in MVC
How to delete multiple records at one shot
How to use partial action in MVC
How to use JSON format in an MVC application
How to fill a master-detail combobox
How to use the jQuery datepicker
How to upload an image in MVC with a jQuery dialog
How to create a table row at client side
How to customize a maproute in Global.asax 
مقاله ای کامل درباره استفاده از MVC و  jQuery و  JSON و  paging و  mapRoute
نظرات مطالب
کار با اسکنر در برنامه های تحت وب (قسمت دوم و آخر)
یک نکته‌ی تکمیلی
در متد GetScan در پایان کار، نیاز است منابع COM مورد استفاده رها شوند تا نشتی حافظه رخ ندهد:
finally
{
  if(image!=null) Marshal.ReleaseComObject(image);
  if(dialog!=null) Marshal.ReleaseComObject(dialog);
}
مطالب
بررسی Bad code smell ها: الگوی Shotgun Surgery
برای مشاهده طبقه بندی Bad code smell‌ها می‌توانید به اینجا مراجعه کنید.
زمانیکه به ازای هر تغییر، نیاز باشد تغییرات کوچکی در تعداد کلاس‌های زیادی انجام شود، این بوی بد کد بوجود آمده است. این الگو از دسته بندی «جلوگیری کنندگان از تغییر» است. نام این دسته بندی به طور واضح گویای مشکلی است که این الگوی بد ایجاد می‌کند. 

چرا چنین بویی به راه می‌افتد؟ 

یکی از نشانه‌های وجود چنین الگوی بدی در کدها، مشاهده کدهای تکراریست. ریشه اصلی این بوی بد، پراکنده کردن مسئولیت‌ها در کلاس‌های مختلف است. مسئولیت‌هایی که بهتر بود در یک کلاس جمع شوند. معمولا برای رفع این بوی بد اقدام به جمع کردن مسئولیت‌ها از نقاط مختلف به یک کلاس می‌کنند.  
با توجه به توضیحات ارائه شده، این بوی بد عملا یکی از علایم اجرایی نکردن اصل Single responsibility  و Open closed از اصول طراحی شیء گرایی است. موارد دیگری که در ایجاد چنین مشکلی کمک می‌کنند به صورت زیر هستند:
  • استفاده نادرست از الگوهای طراحی شیء گرا 
  • عدم درک درست مسئولیت‌های کلاس‌های ایجاد شده 
  • عدم تشخیص مکانیزم‌های مشترک در کد و جداسازی مناسب آنها 
برای بررسی بیشتر این موضوع فرض کنید کلاس‌هایی در نرم افزار خود دارید که شماره تلفن کاربر را به صورت ورودی دریافت و روی آن کار خاصی را انجام می‌دهند. در ابتدای تولید نرم افزار فرمت صحیح شماره تلفن به صورت "04135419999" تشخیص داده شده است و مکانیزم اعتبارسنجی آن نیز با استفاده regular express‌ionها پیاده سازی شده‌است.  بعدا نیازمندی دیگری بوجود می‌آید که شماره تلفن‌هایی با کد بین المللی نیز در نرم افزار قابل استفاده باشند. مانند "984135410000+" دو نوع پیاده سازی (از میان روش‌های فراوان پیاده سازی) برای تشریح این موضوع می‌توان متصور بود. فرض کنید در دو موجودیت «کاربر» و «آدرس» نیاز به ذخیره سازی شماره تلفن وجود دارد. 

اول: هر جائیکه نیاز به اعتبارسنجی شماره تلفن وجود داشته باشد؛ این کار تماما در همان مکان انجام شود.
public class UserService 
{ 
        public void SaveUser(dynamic userEntity) { 
            var regEx = "blablabla"; 
            var phoneIsValid = Regex.IsMatch(userEntity.PhoneNumber, regEx); 
            if (!phoneIsValid) 
                return; 
            // ... 
        } 
}  

public class AddressService 
{ 
        public void SaveAddress(dynamic addressEntity) 
        { 
            var regEx = "blablabla"; 
            var phoneIsValid = Regex.IsMatch(addressEntity.PhoneNumber, regEx); 
            if (!phoneIsValid) 
                return; 
        } 
}
در این روش پیاده سازی اگر دقت کرده باشید روال مربوط به اعتبارسنجی در دو متد «ذخیره کاربر» و «ذخیره آدرس» تکرار شده‌است . این الگوی کد نویسی، علاوه بر این که خود نوعی بوی بد کد محسوب می‌شود، باعث ایجاد الگوی Shotgun surgery نیز است.  
در اینجا اگر قصد اعمال تغییری در منطق مربوط به اعتبارسنجی شماره تلفن وجود داشته باشد، نیاز خواهد بود تمامی مکان‌هایی که این منطق پیاده سازی شده‌است، بسته به شرایط جدید تغییر کند. یعنی برای تغییر یک منطق اعتبارسنجی نیاز خواهد بود کلاس‌های زیادی تغییر کنند.  

دوم: راه بهتر در انجام چنین کاری، جداسازی منطق مربوط به اعتبارسنجی شماره تلفن و انتقال آن به کلاسی جداگانه‌است؛ به صورت زیر: 
public class PhoneValidator
{ 
        public bool IsValid(string phoneNumber) 
        { 
            var regEx = "blablabla"; 
            var phoneIsValid = Regex.IsMatch(phoneNumber, regEx); 
            if (!phoneIsValid) 
                return false; 
            return true; 
        } 
 } 
 
public class UserService 
{ 
        public void SaveUser(dynamic userEntity) 
        { 
            var validator = new PhoneValidator(); 
            var phoneIsValid  = validator.IsValid(userEntity.PhoneNumber); 
            if (!phoneIsValid) 
                return; 
            // ... 
        } 
 } 
 
public class AddressService 
{ 
        public void SaveAddress(dynamic addressEntity) 
        { 
            var validator = new PhoneValidator(); 
            var phoneIsValid = validator.IsValid(addressEntity.PhoneNumber); 
            if (!phoneIsValid) 
                return; 
           // ... 
        } 
}

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

شکل دیگر 

شکل دیگر این بوی بد کد، Divergent Change است. با این تفاوت که در الگوی Divergent Change تغییرات در یک کلاس اتفاق می‌افتند نه در چندین کلاس به طور همزمان. 

جمع بندی

تشخیص چنین الگوی بد کد نویسی ای همیشه به این سادگی نیست. یکی از راه‌های تشخیص سریع چنین بوی بد کدی این است که به کارهای تکراری عادت نکنید! و زمانیکه متوجه شدید کار خاصی را در کد به صورت تکراری انجام می‌دهید، دقت لازم را برای تغییر آن داشته باشید؛ به صورتیکه نیاز به اعمال تغییرات تکراری در مکان‌های مختلف کد وجود نداشته باشد. راه دیگر زمانی است که کدی تکراری را مشاهده کردید. زمانیکه کدی تکراری در کدها وجود داشته باشد، اطمینان داشته باشید هنگام تغییر آن به این مشکل دچار خواهید شد. برای رفع موضوع کد تکراری می‌توانید از روش‌های مختلفی که عنوان شد استفاده کنید. 
مطالب
اثبات قانون مشاهده‌گر در برنامه نویسی
امروز حین کدنویسی به یک مشکل نادر برخورد کردم. کلاسی پایه داشتم (مثلا Person) که یک سری کلاس دیگر از آن ارث بری میکردند (مثلا کلاس‌های Student و Teacher).در اینجا در کلاس پایه بصورت اتوماتیک یک ویژگی(Property) را روی کلاس‌های مشتق شده مقدار دهی میکردم؛ مثلا به این شکل:
 public class Person
    {      
        public Person()
        {
            personId= this.GetType().Name + (new Random()).Next(1, int.MaxValue);          
        }
     }
سپس در یک متد مجموعه‌ای از Studentها و teacher‌ها را ایجاد کرده و به لیستی از Person‌ها اضافه میکنم:
var student1=new Student(){Name="Iraj",Age=21};
var student1=new Student(){Name="Nima",Age=20};
var student1=new Student(){Name="Sara",Age=25};
var student1=new Student(){Name="Mina",Age=22};
var student1=new Student(){Name="Narges",Age=26};
var teacher1=new Student(){Name="Navaei",Age=45};
var teacher2=new Student(){Name="Imani",Age=50};
اما در نهایت اتفاقی که رخ میداد این بود که PersonId همه Student‌ها یکسان می‌شد ولی قضیه به همین جا ختم نشد؛ وقتی خط به خط برنامه را Debug و مقادیر را Watch می‌کردم، مشاهده می‌کردم که PersonId به درستی ایجاد می‌شود.
در فیزیک نوین اصلی هست به نام عدم قطعیت هایزنبرگ که به زبان ساده میتوان گفت نحوه رخداد یک اتفاق، با توجه به وجود یا عدم وجود یک مشاهده‌گر خارجی نتیجه‌ی متفاوتی خواهد داشت.
کم کم داشتم به وجود قانون مشاهده‌گر در برنامه نویسی هم ایمان پیدا میکردم که این کد فقط در صورتیکه آنرا مرحله به مرحله بررسی کنم جواب خواهد داد!
جالب اینکه زمانیکه personId  را نیز ایجاد میکردم، یک دستور برای دیدن خروجی نوشتم مثل این
 public class Person
    {      
        public Person()
        {
            personId= this.GetType().Name + (new Random()).Next(1, int.MaxValue);  
            Debug.Print(personId)       
        }
     }
در این حالت نیز دستورات درست عمل میکردند و personId متفاوتی ایجاد می‌شد!
قبل از خواندن ادامه مطلب شما هم کمی فکر کنید که مشکل کجاست؟
 این مشکل ربطی به قانون مشاهده‌گر و یا دیگر قوانین فیزیکی نداشت. بلکه بدلیل سرعت بالای ایجاد وهله ها(instance) از کلاسی‌های مطروحه (مثلا در زمانی کمتر از یک میلی ثانیه) زمانی در بازه یک کلاک CPU رخ می‌داد.
هر نوع ایجاد کندی (همچون نمایش مقادیر در خروجی) باعث می‌شود کلاک پردازنده نیز تغییر کند و عدد اتفاقی تولید شده فرق کند.
همچنین برای حل این مشکل میتوان از کلاس تولید کننده اعداد اتفاقی، شبیه زیر استفاده کرد:
using System;
using System.Threading;

public static class RandomProvider
{    
    private static int seed = Environment.TickCount;

    private static ThreadLocal<Random> randomWrapper = new ThreadLocal<Random>(() =>
        new Random(Interlocked.Increment(ref seed))
    );

    public static Random GetThreadRandom()
    {
        return randomWrapper.Value;
    }
}

مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 15 - بررسی تغییرات Caching
در نگارش‌های پیشین ASP.NET MVC با استفاده از Output Cache، امکان کش کردن خروجی یک اکشن متد، وجود دارد. مکانیزم Output Cache از ASP.NET Core حذف شده‌است؛ اما جایگزین‌های قابل توجهی برای آن تدارک دیده شده‌اند.


معرفی Response Cache

جایگزین ویژگی حذف شده‌ی OutputCache در ASP.NET Core، ویژگی جدیدی است به نام ResponseCache و هدف آن تنظیم هدرهای مرتبط با caching مخصوص HTTP Response ارائه شده‌است. به همین جهت با مکانیزم OutputCache قدیمی ASP.NET MVC که اطلاعات را در حافظه‌ی سرور کش می‌کرد، کاملا متفاوت است.
البته قرار است میان افزار OutputCache را در نگارش‌های آتی ASP.NET Core نیز ارائه کنند.
[ResponseCache(Duration = 60)]
public IActionResult Contact()
{
   ViewData["Message"] = "Your contact page.";
   return View();
}
در اینجا مثالی را از نحوه‌ی تعریف این ویژگی جدید، ملاحظه می‌کنید که در آن مقدار خاصیت مدت زمان کش شدن، برحسب ثانیه است. استفاده‌ی از آن سبب خواهد شد تا هدر HTTP ذیل به خروجی از سرور اضافه شود:
 Cache-Control: public,max-age=60

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


تعیین مکان کش شدن خروجی یک اکشن متد

در هدر فوق، عبارت public را مشاهده می‌کنید. این public بودن به این معنا است که امکان کش شدن این خروجی، توسط کش سرورهای اشتراکی بین راه هم وجود دارد.
اگر می‌خواهید این امکان را غیرفعال کنید، نیاز است این public به private تنظیم شود:
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Client)]
تنظیم Location فوق به Client به معنای private شدن هدر تنظیم شده و صرفا کش شدن خروجی، توسط کش مرورگر کاربر می‌باشد.


غیرفعال کردن کش شدن خروجی یک اکشن متد

اگر خواستید از کش شدن خروجی یک اکشن متد تحت هر حالتی جلوگیری کنید، مکان آن‌را به None و NoStore آن‌را به true تنظیم کنید:
[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
   return View();
}
این تنظیم سبب افزوده شدن یک چنین هدر HTTP ایی به خروجی از سرور می‌شود:
Cache-Control: no-store,no-cache
Pragma: no-cache


امکان تعریف پروفایل‌های کش

بجای اینکه تنظیمات کش کردن تکراری را به انواع و اقسام اکشن متدها اعمال کنیم، می‌توان برای آن‌ها پروفایل ایجاد کرده و از نام این پروفایل، جهت به اشتراک گذاری تنظیمات استفاده کنیم. برای این منظور به کلاس آغازین برنامه مراجعه کرده و جایی که سرویس ASP.NET MVC را فعال سازی کرده‌اید، پروفایل کش جدیدی را تعریف کنید:
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.CacheProfiles.Add("PrivateCache",
            new CacheProfile
            {
                Duration = 60,
                Location = ResponseCacheLocation.Client
            }); 
    });
پس از آن برای استفاده‌ی از این تنظیمات اشتراکی، فقط کافی است تا نام پروفایل مرتبطی را ذکر کنیم:
[ResponseCache(CacheProfileName = "PrivateCache")]


معرفی سرویس کش درون حافظه‌ای

در نگارش‌های پیشین ASP.NET، متدهایی برای کش کردن موقتی اطلاعات در حافظه و سپس بازیابی آن‌ها وجود داشتند. در ASP.NET Core، این متدها توسط سرویس ارائه کننده‌ی IMemoryCache در اختیار برنامه قرار می‌گیرند. برای فعال سازی این سرویس جدید باید مراحل ذیل طی شوند:
الف) ابتدا بسته‌ی Microsoft.Extensions.Caching.Memory را به لیست وابستگی‌های پروژه در فایل project.json اضافه کنید:
{
    "dependencies": {
      //same as before
      "Microsoft.Extensions.Caching.Memory": "1.0.0"
 },
ب) سپس به کلاس آغازین برنامه مراجعه کرده و سرویس آ‌ن‌را معرفی و ثبت کنید:
public void ConfigureServices(IServiceCollection services)
{
  services.AddMemoryCache();
ج) پس از آن سرویس پیاده سازی کننده‌ی IMemoryCache،  در تمام اجزای برنامه در دسترس خواهد بود. برای مثال:
[Route("DNT/[controller]")]
public class AboutController : Controller
{
    private readonly IMemoryCache _memoryCache;
 
    public AboutController(IMemoryCache memoryCache)
    {
        _memoryCache = memoryCache;
    }
 
    [Route("")]
    public ActionResult Hello()
    {
 
        string cacheKey = "my-cache-key";
        string greeting;
 
        if (!_memoryCache.TryGetValue(cacheKey, out greeting))
        {
             greeting = "Hello";
            // store in the cache
            _memoryCache.Set(cacheKey, greeting,
                new MemoryCacheEntryOptions()
                .SetAbsoluteExpiration(TimeSpan.FromMinutes(1)));
        }
 
        return Content($"{greeting} from DNT!");
    }
در مثال فوق، ابتدا وابستگی سرویس کش درون حافظه‌ای، به سازنده‌ی کنترلر تزریق شده‌است. تامین آن هم توسط سرویسی که در کلاس آغازین برنامه ثبت کردیم، انجام می‌شود. پس از آن در اکشن متد Hello، سعی کرده‌ایم بر اساس کلید کشی که مشخص کرده‌ایم، مقداری را بازیابی کنیم. اگر این مقدار وجود نداشته باشد، آن‌را توسط متد Set تنظیم خواهیم کرد تا برای دفعات آتی فراخوانی این متد، مورد استفاده قرار گیرد.
تنظیمات منقضی شدن کش نیز به حالت absolute تنظیم شده‌است. یعنی پس از یک دقیقه حتما منقضی می‌شود. اگر فراخوانی‌های این متد زیاد است، می‌توان حالت منقضی شدن sliding را تنظیم کرد:
 new MemoryCacheEntryOptions()
  .SetSlidingExpiration(TimeSpan.FromMinutes(5))
در این حالت اگر پیش از اتمام 5 دقیقه‌ی تنظیم شده، درخواستی به سرور رسید، این کش برای 5 دقیقه‌ی بعد نیز مجددا تمدید می‌شود.
اگر خواستیم تا این کش سر ساعت منقضی شود، اما در طی این یک ساعت به صورت sliding عمل کند، می‌توان از ترکیب دو حالت مطلق و لغزشی استفاده کرد:
 new MemoryCacheEntryOptions()
  .SetSlidingExpiration(TimeSpan.FromMinutes(5))
  .SetAbsoluteExpiration(TimeSpan.FromHours(1))

یک نکته: اگر فشار حافظه‌ی سرور زیاد شود، مدیر حافظه‌ی این کش، شروع به منقضی کردن آیتم‌هایی با حق تقدم پایین می‌کند. بالاترین حق تقدم را حالت NeverRemove ذیل دارد:
 new MemoryCacheEntryOptions()
  .SetPriority(CacheItemPriority.NeverRemove))


معرفی Tag Helpers مخصوص کش کردن قسمتی از صفحه

در ادامه‌ی مبحث معرفی Tag Helpers، تعدادی از آن‌ها جهت کش کردن محتوای قسمتی از صفحه، طراحی شده‌اند:
<cache expires-after="@TimeSpan.FromMinutes(10)">
    @Html.Partial("_WhatsNew")
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>
تگ جدید cache محتوای دربرگیرنده‌ی آن‌را «در حافظه‌ی سرور» کش می‌کند (و در پشت صحنه از همان کش درون حافظه‌ای که پیشتر بحث شد، استفاده می‌کند). تگ cache در خروجی HTML نهایی مشاهده نمی‌شود و صرفا مفهومی سمت سرور است.
برای نمونه در مثال فوق، محتوای پارشال ویوو رندر شده و همچنین تاریخی که پس از آن نمایش داده شده‌است، به مدت 10 دقیقه در حافظه‌ی سرور کش می‌شوند. اگر این زمان تنظیم نشود، تا زمانیکه برنامه در سرور مشغول به کار است، این قسمت منقضی نخواهد شد.
در اینجا اگر expires-after ذکر شده بود، یعنی پس از این مدت زمان، کش منقضی می‌شود.
<cache expires-after="@TimeSpan.FromSeconds(5)">
    <!--View Component or something that gets data from the database-->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>
 اگر expires-on آن ذکر شود، می‌توان تاریخ و زمان مشخصی را در اینجا ذکر کرد (برای مثال فردا ساعت 10، با فراخوانی DateTime.Today.AddDays).
<cache expires-on="@DateTime.Today.AddDays(1).AddTicks(-1)">
  <!--View Component or something that gets data from the database-->
 *last updated  @DateTime.Now.ToLongTimeString()
</cache>
همچنین می‌توان از expires-sliding نیز استفاده کرد. به این معنا که اگر در طی مدتی خاص این صفحه درخواست نشد، آنگاه این کشی منقضی می‌شود.
<cache expires-sliding="@TimeSpan.FromMinutes(5)">
    <!--View Component or something that gets data from the database-->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>
همچنین در اینجا می‌توان کش کردن را به ازای کاربران مختلف، کوئری استرینگ‌های مختلف و امثال آن انجام داد (با ارائه‌ی محتوای متفاوتی به ازای پارامترهای مختلف):
<cache vary-by-user="true">
    <!--View Component or something that gets data from the database-->
    *last updated @DateTime.Now.ToLongTimeString()
</cache>
در این حالت دیگر نیازی نیست تا نگران این باشیم که آیا محتوای قسمت کش شده‌ی از صفحه برای تمام کاربران در دسترس است یا خیر؟ در اینجا هر کاربر لاگین شده‌ی به سیستم، نگارش کش شده‌ی خاص خودش را دریافت می‌کند.

<cache vary-by-route="id">
    <!--View Component or something that gets data from the database-->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>
در اینجا به ازای پارامتر آی‌دی مسیریابی، نگارش‌های مختلف کش شده‌ای از صفحه تامین می‌شوند. در اینجا می‌توان لیستی از پارامترهای جدا شده‌ی با کاما را مشخص کرد.

<cache vary-by-query="search">
    <!--View Component or something that gets data from the database-->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>
امکان کش کردن محتوای صفحه به ازای کوئری استرینگ‌های مختلف تنظیم شده نیز وجود دارد.

<cache vary-by-cookie="MyAppCookie">
    <!--View Component or something that gets data from the database-->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>
در اینجا به ازای محتواهای مختلف کوکی خاصی به نام MyAppCookie، نگارش‌های مختلف کش شده‌ای از صفحه ذخیره می‌شوند.

 <cache vary-by-header="User-Agent">
    <!--View Component or something that gets data from the database-->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>
در اینجا می‌توان به ازای هدرهای مختلف پروتکل HTTP نگارش‌های کش شده‌ی متفاوتی را ارائه داد.

<cache vary-by="@ViewBag.ProductId">
    <!--View Component or something that gets data from the database-->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>
اگر خواستید کلید کش را خودتان تعیین کنید از vary-by استفاده کنید.

 <cache vary-by-user="true" vary-by-route="id">
    <!--View Component or something that gets data from the database-->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>
امکان ترکیب این موارد با هم نیز وجود دارد.

به علاوه چون زیر ساخت این Tag Helper همان Microsoft.Extensions.Caching.Memory است، امکان تنظیم حق تقدم حذف شدن آیتم‌های کش شده نیز وجود دارد:
<cache expires-sliding="@TimeSpan.FromMinutes(10)"
priority="@Microsoft.Extensions.Caching.Memory.CacheItemPriority.NeverRemove">
    <!--View Component or something that gets data from the database-->
    *last updated  @DateTime.Now.ToLongTimeString()
</cache>


مبحث تکمیلی

امکان ذخیره سازی آیتم‌های کش شده در بانک اطلاعاتی (بجای حافظه‌ی فرار) نیز پیش بینی شده‌است که تحت عنوان «کش توزیع شده» در دسترس است.
Working with a Distributed Cache
مطالب
OpenCVSharp #3
در قسمت دوم با نحوه‌ی بارگذاری تصاویر در OpenCVSharp آشنا شدیم. در این قسمت قصد داریم با نحوه‌ی ایجاد یک clone و نمونه‌ای مشابه از تصویر اصلی بارگذاری شده آشنا شویم. برای مثال هرچند متد LoadImage، دارای پارامتر بارگذاری تصویر، به صورت سیاه و سفید است، اما توصیه نمی‌شود که در بدو امر، تصویر را سیاه و سفید بارگذاری کنید. چون هرگونه تغییری در تصویر اصلی، امکان استفاده‌ی از آن‌را در سایر متدها و الگوریتم‌ها با مشکل مواجه می‌کند و استفاده‌ی از حالت LoadMode.GrayScale جهت بالا بردن سرعت عملیات، در کارهای پردازش تصویر بسیار معمول است.


تهیه‌ی یک نمونه‌ی سیاه و سفید از تصویر اصلی در OpenCVSharp

برای تهیه‌ی یک نمونه‌ی مشابه تصویر اصلی، از متد CreateImage استفاده می‌شود:
using (var src = Cv.LoadImage(@"..\..\images\ocv02.jpg", LoadMode.Color))
using (var dst = Cv.CreateImage(new CvSize(src.Width, src.Height), BitDepth.U8, 1))
{
    Cv.CvtColor(src, dst, ColorConversion.BgrToGray);
 
    using (new CvWindow("src", image: src))
    using (new CvWindow("dst", image: dst))
    {
        Cv.WaitKey();
    }
}
با این خروجی



معرفی متد CreateImage

پارامتر اول متد CreateImage، اندازه‌ی تصویر تولیدی را مشخص می‌کند. پارامتر دوم آن تعداد بیت تصویر را تعیین خواهد کرد. این تعداد بیت عموما بر اساس نیاز متدهای مختلف پردازش تصویر، متغیر خواهند بود و برای تعیین آن نیاز است مستندات هر متد را مطالعه کرد. BitDepth.U8 به معنای 8bit unsigned است.
پارامتر سوم این متد، تعیین کننده‌ی تعداد کانال تصویر است. تصاویر رنگی دارای سه کانال سبز، قرمز و آبی‌، هستند. چون در اینجا قصد داریم تصویر را سیاه و سفید کنیم، تعداد کانال را به عدد یک تنظیم کرده‌ایم.

متد CreateImage جهت سازگاری با اینترفیس C مربوط به OpenCV در اینجا وجود دارد. معادل
 using (var dst = Cv.CreateImage(new CvSize(src.Width, src.Height), BitDepth.U8, 1))
را می‌توان به نحو ذیل نیز نوشت:
 var dst = new IplImage(new CvSize(src.Width, src.Height), BitDepth.U8, 1)
و یا حتی پارامتر تعیین اندازه‌ی تصویر را نیز می‌توان ساده‌تر کرد:
 using (var dst = new IplImage(src.Size, BitDepth.U8, 1))


تبدیل تصویر به حالت سیاه و سفید

متد CvtColor جهت تغییر color space بکار می‌رود که در اینجا (BGR (Blue/Green/Red را به Gray تبدیل کرده‌است:
 Cv.CvtColor(src, dst, ColorConversion.BgrToGray);
این متد را در OpenCVSharp به نحو ذیل نیز می‌توان بازنویسی کرد:
 src.CvtColor(dst, ColorConversion.BgrToGray);

بنابراین به صورت خلاصه می‌توان کدهای ابتدای بحث را به صورت زیر نیز نوشت که با کلاس‌های OpenCVSharp بیشتر سازگاری دارد:
using (var src = new IplImage(@"..\..\images\ocv02.jpg", LoadMode.Color))
//using (var dst = new IplImage(new CvSize(src.Width, src.Height), BitDepth.U8, 1))
using (var dst = new IplImage(src.Size, BitDepth.U8, 1))
{
    src.CvtColor(dst, ColorConversion.BgrToGray);
 
    using (new CvWindow("src", image: src))
    using (new CvWindow("dst", image: dst))
    {
        Cv.WaitKey();
    }
}

کدهای کامل این مثال را از اینجا می‌توانید دریافت کنید.