نظرات مطالب
ASP.NET MVC #21
یک سری از مجوز‌های سورس باز به این شکل هستند. نمونه دیگر آن LGPL است. مثلا NHibernate مجوز LGPL دارد. به این معنا که مجاز هستید از آن به شکل بایناری (یعنی فایل‌های dll کامپایل شده آن) در هر نوع پروژه تجاری، غیرتجاری، باز، بسته ... بدون محدودیت استفاده کنید. اما اگر سورس آن‌ها را مستقیما به پروژه خود اضافه و کامپایل کنید، نیاز است تا سورس کارتان را هم ارائه دهید.
نظرات مطالب
بررسی علت CPU Usage بالای برنامه در حال اجرا
جهت کامپایل سورس "How to get CPU usage of processes and threads" ، در پروژه CpuUsageAPI ،‌ پوشه properties آن خالی است. یک سری فایل نیست. مهم هم نیستند. این‌ها را از پروژه حذف کنید. تست شد با دات نت 4 هم کامپایل می‌شود.
نظرات مطالب
سفارشی سازی ASP.NET Core Identity - قسمت پنجم - سیاست‌های دسترسی پویا
- مطلب آن فرد هندی که لینک دادید، به نظر از این مطلب استفاده کرده.
- برای دسترسی به اکشن متدهای محافظت شده‌ی با ویژگی خالی Authorize (بدون هیچگونه تنظیم دیگری)، تنها وجود شرط ورود به سیستم کافی است. اگر Policy یا Role خاصی بر روی آن‌ها اعمال شود، آنگاه آن شخص باید User Claims ویژه‌ای را داشته باشد تا بتواند شرایط آن Policy را تامین کند. در مورد نوشتن انواع و اقسام Policyهای سفارشی (چه بر اساس شرایط User Claims ثابت (policy => policy.RequireClaim) و چه پویا که به همراه AuthorizationHandlerها هستند) در این مطلب بحث شده و شما محدود به تنظیمات پیش‌فرض پروژه نیستید.
- مطلب «تنظیمات کش توزیع شده‌ی مبتنی بر SQL Server در ASP.NET Core» و خصوصا نظرات آن‌را مطالعه کنید.
نظرات مطالب
روش‌هایی برای بهبود قابلیت دیباگ بسته‌های NuGet
راستش اینا که سهله، قبلا مقالات جامع‌تری رو مطالعه کردم و ایشو‌های sourcelink داخل ریپوی خودش و dotnetsdk رو زیرُ رو کردم.
واقعیت اینه که این قابلیت آنچنان stable و قابل استفاده نیست. توی ورژن‌های مختلف sdk مشکلات متعددی براش پیش اومده و workaround‌های متفاوتی هم براش دادن. 
این موارد رو میشه توی issue معروف این قضیه که بعد از گذشت 3 سال هنوز باز هست مشاهده کنین. انصافا من از بعضی از workaround هاش هم جواب گرفتم ولی همچنان راه سر راست و درست و درمون نداره مگر با اون کدی که فرستادم.
پکیج EFCoreSecondLevelCacheInterceptor  رو روی چندین سیستم هم امتحان کردم و جواب نداد و نهایتا با قرار دادن کد زیر توی csproj (که یکی از workaround‌های همون issue هست) جواب داد. البته اونم رو فقط رو net5.0!
<!-- https://github.com/dotnet/sdk/issues/1458#issuecomment-420456386 -->
<Target Name="_ResolveCopyLocalNuGetPackagePdbsAndXml" Condition="$(CopyLocalLockFileAssemblies) == true" AfterTargets="ResolveReferences">
<ItemGroup>
  <ReferenceCopyLocalPaths
Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).pdb')"
Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' != '' and Exists('%(RootDir)%(Directory)%(Filename).pdb')" />
  <ReferenceCopyLocalPaths
Include="@(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).xml')"
Condition="'%(ReferenceCopyLocalPaths.NuGetPackageId)' != '' and Exists('%(RootDir)%(Directory)%(Filename).xml')" />
</ItemGroup>
</Target>
در کل چون این یه موضوعی هست که به چندین تیم و محصول وابسته هست (تیم های 
dotnet SDK  و VS و Nuget و  SourceLink) و نیاز به هماهنگی هست متاسفانه خیلی کند پیش میرن و در حال حاضر پلن کردند واسه  milestone دات نت 6 که انشالا برطرف بشه
مطالب دوره‌ها
آشنایی با نحوه ایجاد یک IoC Container
قبل از اینکه وارد بحث استفاده از کتابخانه‌های بسیار غنی IoC Container موجود شویم، بهتر است یک نمونه ساده آن‌ها را طراحی کنیم تا بهتر بتوان با عملکرد و ساختار درونی آن‌ها آشنا شد.


IoC Container چیست؟

IoC Container، فریم ورکی است برای انجام تزریق وابستگی‌ها. در این فریم ورک امکان تنظیم اولیه وابستگی‌های سیستم وجود دارد. برای مثال زمانیکه برنامه از یک IoC Container، نوع اینترفیس خاصی را درخواست می‌کند، این فریم ورک با توجه به تنظیمات اولیه‌اش، کلاسی مشخص را بازگشت خواهد داد.
IoC Containerهای قدیمی‌تر، برای انجام تنظیمات اولیه خود از فایل‌های کانفیگ استفاده می‌کردند. نمونه‌های جدیدتر آن‌ها از روش‌های Fluent interfaces برای مشخص سازی تنظیمات خود بهره می‌برند.

زمانیکه از یک IOC Container در کدهای خود استفاده می‌کنید، مراحلی چند رخ خواهند داد:
الف) کد فراخوان، از IOC Container، یک شیء مشخص را درخواست می‌کند. عموما اینکار با درخواست یک اینترفیس صورت می‌گیرد؛ هرچند محدودیتی نیز نداشته و امکان درخواست یک کلاس از نوعی مشخص نیز وجود دارد.
ب) در ادامه IOC Container به لیست اشیاء قابل ارائه توسط خود نگاه کرده و در صورت وجود، وهله سازی شیء درخواست شده را انجام و نهایتا شیء مطلوب را بازگشت خواهد داد.
در این بین زنجیره‌ی وابستگی‌های مورد نیاز نیز وهله سازی خواهند شد. برای مثال اگر وابستگی اول به وابستگی دوم برای وهله سازی نیاز دارد، کار وهله سازی وابستگی‌های وابستگی دوم نیز به صورت خودکار انجام خواهند شد. (این موردی است که بسیاری از تازه واردان به این بحث تا یکبار آن‌را امتحان نکنند باور نخواهند کرد!)
ج) سپس کد فراخوان وهله دریافتی را مورد پردازش قرار داده و سپس شروع به استفاده از متدها و خواص آن خواهد نمود.


در تصویر فوق محل قرارگیری یک IOC Container را مشاهده می‌کنید. یک IOC Container در مورد تمام وابستگی‌های مورد نیاز، اطلاعات لازم را دارد. همچنین این فریم ورک در مورد کلاسی که قرار است از وابستگی‌های سیستم استفاده نماید نیز مطلع است؛ به این ترتیب می‌تواند به صورت خودکار در زمان وهله سازی آن، نوع‌های وابستگی‌های مورد نیاز آن‌را در اختیارش قرار دهد.
برای مثال در اینجا MyClass، وابستگی مشخص شده در سازنده خود را به نام IDependency از IOC Container درخواست می‌کند. سپس این IOC Container بر اساس تنظیمات اولیه خود، یکی از وابستگی‌های A یا B را بازگشت خواهد داد.


آغاز به کار ساخت یک IOC Container نمونه

در ابتدا کدهای آغازین مثال بحث جاری را در نظر بگیرید:
using System;

namespace DI01
{
    public interface ICreditCard
    {
        string Charge();
    }

    public class Visa : ICreditCard
    {
        public string Charge()
        {
            return "Charging with the Visa!";
        }
    }

    public class MasterCard : ICreditCard
    {
        public string Charge()
        {
            return "Swiping the MasterCard!";
        }
    }

    public class Shopper
    {
        private readonly ICreditCard creditCard;

        public Shopper(ICreditCard creditCard)
        {
            this.creditCard = creditCard;
        }

        public void Charge()
        {
            var chargeMessage = creditCard.Charge();
            Console.WriteLine(chargeMessage);
        }
    }    
}
در اینجا وابستگی‌های کلاس خریدار از طریق سازنده آن که متداول‌ترین روش تزریق وابستگی‌ها است، در اختیار آن قرار خواهد گرفت. یک اینترفیس کردیت کارت تعریف شده‌است به همراه دو پیاده سازی نمونه آن مانند مسترکارت و ویزا کارت. ساده‌ترین نوع فراخوانی آن نیز می‌تواند مانند کدهای ذیل باشد (تزریق وابستگی‌های دستی):
 var shopper = new Shopper(new Visa());
shopper.Charge();
در ادامه قصد داریم این فراخوانی‌ها را اندکی هوشمندتر کنیم تا بتوان بر اساس تنظیمات برنامه، کار تزریق وابستگی‌ها صورت گیرد و به سادگی بتوان اینترفیس‌های متفاوتی را در اینجا درخواست و مورد استفاده قرار داد. اینجا است که به اولین IoC Container خود خواهیم رسید:
using System;
using System.Collections.Generic;
using System.Linq;

namespace DI01
{
    public class Resolver
    {
        //کار ذخیره سازی و نگاشت از یک نوع به نوعی دیگر در اینجا توسط این دیکشنری انجام خواهد شد
        private Dictionary<Type, Type> dependencyMap = new Dictionary<Type, Type>();

        /// <summary>
        /// یک نوع خاص از آن درخواست شده و سپس بر اساس تنظیمات برنامه، کار وهله سازی
        /// نمونه معادل آن صورت خواهد گرفت
        /// </summary>
        public T Resolve<T>()
        {
            return (T)Resolve(typeof(T));
        }

        private object Resolve(Type typeToResolve)
        {
            Type resolvedType;

            // ابتدا بررسی می‌شود که آیا در تنظیمات برنامه نگاشت متناظری برای نوع درخواستی وجود دارد؟
            if (!dependencyMap.TryGetValue(typeToResolve, out resolvedType))
            {
                //اگر خیر، کار متوقف خواهد شد
                throw new Exception(string.Format("Could not resolve type {0}", typeToResolve.FullName));
            }

            var firstConstructor = resolvedType.GetConstructors().First();
            var constructorParameters = firstConstructor.GetParameters();
            // در ادامه اگر این نوع، دارای سازنده‌ی بدون پارامتری است
            // بلافاصله وهله سازی خواهد شد
            if (!constructorParameters.Any())
                return Activator.CreateInstance(resolvedType);


            var parameters = new List<object>();
            foreach (var parameterToResolve in constructorParameters)
            {
                // در اینجا یک فراخوانی بازگشتی صورت گرفته است برای وهله سازی
                // خودکار پارامترهای مختلف سازنده یک کلاس
                parameters.Add(Resolve(parameterToResolve.ParameterType));
            }            
            return firstConstructor.Invoke(parameters.ToArray());
        }

        public void Register<TFrom, TTo>()
        {
            dependencyMap.Add(typeof(TFrom), typeof(TTo));
        }
    }
}
در اینجا کدهای کلاس Resolver یا همان IoC Container ابتدایی بحث را مشاهده می‌کنید. توضیحات قسمت‌های مختلف آن به صورت کامنت ارائه شده‌اند.
 var resolver = new Resolver();
//تنظیمات اولیه
resolver.Register<Shopper, Shopper>();
resolver.Register<ICreditCard, Visa>();
//تزریق وابستگی‌ها و وهله سازی
var shopper = resolver.Resolve<Shopper>();
shopper.Charge();
در ادامه نحوه استفاده از IoC Container ایجاد شده را مشاهده می‌کنید.
ابتدا کار تعاریف نگاشت‌های اولیه انجام می‌شود. در این صورت زمانیکه متد Resolve فراخوانی می‌گردد، نوع درخواستی آن به همراه سازنده دارای آرگومانی از نوع ICreditCard وهله سازی شده و بازگشت داده خواهد شد. سپس با در دست داشتن یک وهله آماده، متد Charge آن‌را فراخوانی خواهیم کرد.


بررسی نحوه استفاده از Microsoft Unity به عنوان یک IoC Container

Unity چیست؟

Unity یک فریم ورک IoC Container تهیه شده توسط مایکروسافت می‌باشد که آن‌را به عنوان جزئی از Enterprise Library خود قرار داده است. بنابراین برای دریافت آن یا می‌توان کل مجموعه Enterprise Library را دریافت کرد و یا به صورت مجزا به عنوان یک بسته نیوگت نیز قابل تهیه است.
برای این منظور در خط فرمان پاورشل نیوگت در VS.NET دستور ذیل را اجرا کنید:
 PM> Install-Package Unity

پیاده سازی مثال خریدار توسط Unity

همان مثال قسمت قبل را درنظر بگیرید. قصد داریم اینبار بجای IoC Container دست سازی که تهیه شد، پیاده سازی آن‌را به کمک MS Unity انجام دهیم.
using Microsoft.Practices.Unity;

namespace DI02
{
    class Program
    {
        static void Main(string[] args)
        {
            var container = new UnityContainer();

            container.RegisterType<ICreditCard, MasterCard>();

            var shopper = container.Resolve<Shopper>();
            shopper.Charge();
        }
    }
}
همانطور که ملاحظه می‌کنید، API آن بسیار شبیه به کلاس دست سازی است که در قسمت قبل تهیه کردیم.
مطابق کدهای فوق، ابتدا تنظیمات IoC Container انجام شده است. به آن اعلام کرده‌ایم که در صورت نیاز به ICreditCard، نوع MasterCard را یافته و وهله سازی کن. با این تفاوت که Unity هوشمند‌تر بوده و سطر مربوط به ثبت کلاس Shoper ایی را که در قسمت قبل انجام دادیم، در اینجا حذف شده است.
سپس به این IoC Container اعلام کرده‌ایم که نیاز به یک وهله از کلاس خریدار داریم. در اینجا Unity کار وهله سازی‌های خودکار وابستگی‌ها و تزریق آن‌ها را در سازنده کلاس خریدار انجام داده و نهایتا یک وهله قابل استفاده را در اختیار ادامه برنامه قرار خواهد داد.

یک نکته:
به صورت پیش فرض کار تزریق وابستگی‌ها در سازنده کلاس‌ها به صورت خودکار انجام می‌شود. اگر نیاز به Setter injection و مقدار دهی خواص کلاس وجود داشت می‌توان به نحو ذیل عمل کرد:
 container.RegisterType<ICreditCard, MasterCard>(new InjectionProperty("propertyName", 5));
نام خاصیت و مقدار مورد نظر به عنوان پارامتر متد RegisterType باید تعریف شوند.


مدیریت طول عمر اشیاء در Unity

توسط یک IoC Container می‌توان یک وهله معمولی از شیءایی را درخواست کرد و یا حتی طول عمر این وهله را به صورت Singleton معرفی نمود (یک وهله در طول عمر کل برنامه). در Unity اگر تنظیم خاصی اعمال نشود، هربار که متد Resolve فراخوانی می‌گردد، یک وهله جدید را در اختیار ما قرار خواهد داد. اما اگر پارامتر متد RegisterType را با وهله‌ای از ContainerControlledLifetimeManager مقدار دهی کنیم:
 container.RegisterType<ICreditCard, MasterCard>(new ContainerControlledLifetimeManager());
از این پس با هربار فراخوانی متد Resolve، در صورت نیاز به وابستگی از نوع ICreditCard، تنها یک وهله مشترک از MasterCard ارائه خواهد شد.
حالت پیش فرض مورد استفاده، بدون ذکر پارامتر متد RegisterType، مقدار TransientLifetimeManager می‌باشد.
مطالب
استفاده از IIS Express 7.5 در VS.NET

استفاده از IIS در VS.NET و پروژه‌های ASP.NET داستان خودش را دارد. در نگارش‌های 2002 و 2003 آن، تنها وب سرور قابل استفاده جهت کار با VS.NET همان IIS اصلی بود. مهم‌ترین مشکل این روش، نیاز به داشتن دسترسی مدیریتی بر روی سیستم بود (که در بعضی از شرکت‌ها، این مورد برای عموم کاربران ممنوع است) به همراه نصب جداگانه‌ و تنظیمات مخصوص IIS ، صرفا جهت آزمایش یک برنامه‌ی ساده؛ همچنین با توجه به اینکه IIS جزو کامپوننت‌ها ویندوز بوده و هر نگارشی، IIS خاص خودش را دار است، این مورد هم مشکلات ویژه‌ای را به همراه دارد (برای مثال IIS5 ویندوز XP را با IIS7 ویندوز سرور 2008 در نظر بگیرید؛ یکی برای توسعه یکی جهت محیط کاری). این روش در VS.Net 2005 کنار گذاشته شد و از وب سرور توکاری به نام Cassini یا ASP.NET Development Server استفاده گردید. به این صورت دیگر نیازی به نصب مجزای IIS کامل جهت آزمایش‌ برنامه‌های ASP.NET نبود و همچنین نیاز به داشتن دسترسی مدیریتی الزامی نیز منتفی گردید. این روش هنوز هم تا نگارش 2010 ویژوال استودیو مرسوم است؛ اما ... اما کسانی که با Cassini کار کرده باشند می‌دانند که یک سری از رفتار‌های آن با IIS واقعی تطابق ندارد و اگر برنامه‌ی ASP.NET شما با Cassini خوب نمایش داده می‌شود الزامی ندارد که با IIS واقعی هم به همان نحو رفتار کند، برای نمونه رفتار مسیریابی آدرس‌های نسبی در IIS واقعی و Cassini یکی نیست. علاوه بر آن IIS های 7 و 7.5 هم امکانات و ویژگی‌های خاص خود را دارند که Cassini آن‌ها را پوشش نمی‌دهد؛ به علاوه این دو فقط در ویندوزهای جدید مانند ویندوز سرور 2008 یا ویندوز 7 قابل دسترسی هستند. به همین جهت اخیرا یک نسخه‌ی سبک و express از IIS 7.5 به صورت جداگانه برای برنامه نویس‌ها فقط جهت آزمودن برنامه‌های خود تهیه شده‌ است و البته هدفگیری اصلی آن پروژه‌ی WebMatrix است؛ به همراه ویژگی‌های جدید IIS7 مانند امکان آزمودن تنظیمات ویژه IIS7 در وب کانفیگ برنامه، پشتیبانی کامل از SSL ، Url Rewrite و سایر ماژول‌های IIS7، عدم نیاز به دسترسی مدیریتی برای اجرای آن، امکان اجرای آن بر روی پورت‌های مختلف بدون تداخل با وب سرور(های) موجود بر روی سیستم و همچنین برخلاف IIS7 اصلی، بر روی ویندوز XP نیز قابل اجرا است. حجم نگارش IIS Express 7.5 تنها 3.9 مگابایت است:


سرویس پک یک ویژوال استودیوی 2010 (که در زمان نگارش این مطلب نسخه‌ی بتای آن ارائه شده)، یک گزینه‌ی جدید را به منوی کلیک راست بر روی نام پروژه در VS.NET به نام Use IIS Express ، اضافه کرده است تا به سادگی بتوان از این امکان جدید استفاده کرد (یا به عبارتی با IIS Express یکپارچه است و نیاز به تنظیم خاصی ندارد).
در سایر حالات (و نسخه‌هایی که این یکپارچگی وجود ندارد و نخواهد داشت) به صورت زیر می‌توان عمل کرد:
روش اول:
دستور زیر را در خط فرمان وارد نمائید:
"C:\Program Files\IIS Express\iisexpress.exe" /path:D:\Prog\1389\MySite\ /port:4326 /clr:v4.0
به این صورت وب سروری جهت ارائه‌ی سایتی با مسیر ذکر شده بر روی پورت 4326 (http://localhost:4326/) بر اساس دات نت 4 تشکیل خواهد شد (برای نمونه جهت دات نت سه و نیم مقدار v3.5 را وارد نمائید).

روش دوم (که در حقیقت همان روش اول با ارائه‌ی پشت صحنه‌ی موقت آن است):
الف) ابتدا به مسیر My Documents\IISExpress\config مراجعه کرده و فایل applicationhost.config را باز کنید. سپس گره مربوط به site را یافته (حدود سطر 153) و گزینه‌ی serverAutoStart را حذف کنید:
<site name="WebSite1" id="1">
<application path="/">
<virtualDirectory path="/" physicalPath="%IIS_SITES_HOME%\WebSite1" />
</application>
<bindings>
<binding protocol="http" bindingInformation=":8080:localhost" />
</bindings>
</site>
ب) سپس تنظیمات سایت مورد نظر خود را به صورت دستی به این فایل اضافه کنید. برای مثال:
<site name="WebSite2" id="2">
<application path="/" applicationPool="Clr4IntegratedAppPool">
<virtualDirectory path="/" physicalPath="D:\Prog\1389\MyTestSite\" />
</application>
<bindings>
<binding protocol="http" bindingInformation=":1389:localhost" />
</bindings>
</site>
توضیحات:
Name در اینجا نامی دلخواه است که وارد خواهید نمود.
Id شماره سایتی است که ثبت خواهد شد.
applicationPool در اینجا بسیار مهم است. اگر سایت شما مبتنی بر دات نت 4 است، Clr4IntegratedAppPool را وارد نمائید و اگر غیر از این است، Clr2IntegratedAppPool باید تنظیم شود.
physicalPath همان مسیر پروژه شما است.
در قسمت bindingInformation هم می‌توان شماره پورت مورد نظر را وارد کرد.

اکنون فایل applicationhost.config را ذخیره کرده و ببندید.
سپس دستور زیر را در خط فرمان ویندوز وارد نمائید:
"C:\Program Files\IIS Express\iisexpress.exe" /site:WebSite2
که در اینجا WebSite2 همان مدخل جدیدی است که به فایل applicationhost.config اضافه شده است. به این صورت آدرس http://localhost:1389/ جهت دسترسی به سایت شما آماده استفاده خواهد بود.

تنظیمات دیباگر VS.NET :
تا اینجا تنها موفق شده‌ایم که این وب سرور آزمایشی را راه اندازی کنیم. اما نکته‌ی مهم امکان دیباگ کردن برنامه توسط آن‌را از دست داده‌ایم. برای این منظور در VS.NET به خواص پروژه، برگه‌ی Web آن مراجعه کنید. در قسمت Servers گزینه‌ی use custom web server را انتخاب کرده و آدرسی را که در یکی از دو روش فوق ساخته‌اید وارد نمایید. برای مثال http://localhost:4326/
همچنین باید دقت داشت که در همین قسمت هیچکدام از debuggers ذیل گزینه‌ی use custom web server نباید تیک خورده باشند (چون VS.NET دقیقا نمی‌داند که باید به کدام پروسه در ویندوز attach شود).
اکنون برنامه را در حالت دیباگ در VS.NET آغاز کنید (بدیهی است فرض بر این است که iisexpress.exe با تنظیمات ذکر شده باید در حال اجرا باشد).
و ... حداقل مزیت آن بسیار سریع‌تر بودن این روش نسبت به Cassini یا ASP.NET Development Server است.
تا اینجا فقط VS.NET به صورت خودکار مرورگر را باز کرده و سایت نمایش داده می‌شود؛ اما اگر در قسمتی از کدهای خود breakpoint قرار دهیم کار نمی‌کند. برای این منظور باید در حین اجرای برنامه، از منوی debug ، گزینه‌ی attach to process را انتخاب کرده و به iisexpress متصل شوید.

مطالب
بررسی اجمالی Redis
نام Redis از Remote Dictionary server گرفته شده‌است. Redis یکی از محبوب‌ترین key-value store‌ها می‌باشد و هم چنین توسط برند‌های بزرگ IT جهان استفاده می‌شود. لازم به ذکر است  Amazon Elastic Cache از Redis پشتیبانی می‌کند. Redis یک دیتابیس No SQL است و بر روی مفهوم زوج  کلید-مقدار (key-value ) کار می‌کند. key-value store امکانی را برای ذخیره داده‌ها که Value  نامیده میشود، در یک Key فراهم می‌کند. شما می‌توانید بعدا این داد‌ه‌ها را دریافت کنید، تنها اگر نام دقیق کلیدی را که برای ذخیره داده استفاده کرده‌اید، بدانید.

What Is In-Memory, Key-Value Store  

Key-Value store یک سیستم ذخیره سازی است؛ جایی که داده‌ها به صورت زوج کلید-مقدار ذخیره می‌شوند. وقتی که میگوییم in-memory key-value store (زوج کلید-مقدار مقیم در حافظه)، منظور این است که زوج کلید-مقدار در حافظه اصلی RAM ذخیره می‌شوند. بنابراین می‌توانیم بگوییم Redis داده‌ها را در حافظه به شکل زوج کلید-مقدار ذخیره کرده است. 
در Redis کلید‌ها باید string باشند؛ ولی value ‌ها می‌توانند یک string ، list ، set ، sorted set یا hash باشند. 
 
Advantage And Disadvantage of Redis over DBMS  

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

 Redis  RDBMS
Redis  همه چیز را در حافظه اصلی ذخیره می‌کند. RDBMS همه چیز را در حافظه ثانویه ذخیره می‌کند.
در Redis بخاطر ذخیره سازی داده‌ها در حافظه اصلی، خواندن و نوشتن عملیات به شدت سریع می‌باشد. در RDBMS بخاطر ذخیره سازی داده‌ها در حافظه ثانویه، خواندن و نوشتن
عملیات کند است.
حافظه اصلی از نظر size کوچکتر و از لحاظ قیمت نسبت به حافظه ثانویه گرانتر می‌باشد. Redis نمی‌تواند داده‌های بزرگ یا binary data را ذخیره کند.    حافظه ثانویه از نظر size  بزرگتر و از لحاظ قیمت نسبت به حافظه اصلی ارزان‌تر می‌باشد. RDBMS به آسانی می‌تواند با انواع فایل‌ها کار کند.   


Redis Advantages

  • Redis  : Exceptionally fast خیلی سریع است و می‌تواند حدود 110000  ، SET   و 81000 ،  GET را به ازای هر ثانیه انجام دهد.
  • Redis : Supports rich data type بیشتر دیتا تایپ‌ها را  که توسعه دهندگان قبلا آن‌ها را شناخته‌اند، پشتیبانی می‌کند؛ از قبیل string ، list ، set ، sorted set یا hash .
  •  Operations are atomic  : تمام عملیات Redis اتمیک می‌باشند که این اطمینان خاطر را میدهد اگر دو کلاینت به صورت همزمان به آن دسترسی داشته باشند، Redis server مقدار update شده را دریافت خواهد کرد. 
  • Redis : Multi-utility tool یک ابزار چند منظوره است که می‌تواند در برخی از سناریو‌ها استفاده شود از قبیل:  Redis ) messaging-queues , caching   به صورت بومی از Publish/Subscribe پشتیبانی می‌کند ) , هر داده ای با طول عمر کوتاه در Application مانند web application sessions , ... .
 

Redis Single Instance Architecture 

معماری Redis شامل دو پروسه اصلی است: 
1- Redis client
2- Redis Server


Redis client و Redis Server هر دو می‌توانند در یک کامپیوتر یا کامپیوتر‌های متفاوت باشند. Redis server مسئول ذخیره سازی داده‌ها در حافظه می‌باشد. همانطور که متوجه هستیم، Redis همه چیز را در حافظه اصلی ذخیره می‌کند و حافظه اصلی فرار است؛ از این رو زمانیکه Redis server یا کامپیوتر را راه اندازی مجدد (restart) می‌کنیم، همه داده‌های ذخیره شده را از دست خواهیم داد. بنابراین نیازمند یک راه‌حل، جهت ماندگاری datastore می‌باشیم. 


Redis Persistance 
 
سه راه متفاوت وجود دارد که Redis را پایدار می‌کند : RDB ، AOF و دستور SAVE

1-  RDB : RDB Mechanism یک نمونه از تمام داده‌های در حافظه را تهیه و آن‌ها را در حافظه ثانویه ذخیره می‌کند (ذخیره سازی ماندگار) که در یک وقفه مشخص اتفاق می‌افتد. بنابراین این شانس وجود دارد که شما داده‌هایی را از دست بدهید که بعد از آخرین Set , RDB’s snapshot  شده‌اند . 

2-AOF : AOF همه عملیات نوشتن دریافت شده توسط سرور را ثبت می‌کند. بنابراین همه چیز پایدار است. مشکل استفاده از AOF  این است که برای هر عملیات، شروع به نوشتن در دیسک می‌کند و این یک کار هزینه‌بر است و هم چنین اندازه فایل AOF بزرگتر از RDB می‌باشد. 

3-SAVE Command : شما می‌توانید Redis server را مجبور کنید که یک RDB snapshot را ایجاد کند؛ هر زمانکه Redis console client از دستور SAVE استفاده می‌کند.

در ضمن می‌توانید از AOF  و RDB با هم استفاده کنید تا بهترین نتیجه ماندگاری را داشته باشید. 
 
Redis Replication 

Replication یک تکنیک است که کامپیوتر‌ها را درگیر می‌کند تا دسترسی پذیری داده‌ها و تحمل خطا را با ضریب بیشتری امکان پذیر کنند. در یک محیط Replication، کامپیوتر‌ها، داده‌های یکسانی را با یکدیگر به اشتراک می‌گذارند؛ حتی اگر چندین کامپیوتر دچار مشکل شوند، باز هم، همه داده‌ها در دسترس خواهند بود که به صورت Master/Slaves  می‌باشند.


تمام slave‌ها شامل داده‌های یکسانی همانند master می‌باشند. وقتی‌که یک slave جدید در محیط Replication ایجاد می‌شود، master به صورت خودکار همه داده‌ها را با sync ، slave می‌کند.
تمام Query ‌ها به سرور master هدایت می‌شوند و سپس سرور master عملیات را اجرا می‌کند. وقتی‌که یک عملیات نوشتن اتفاق می‌افتد، سرور master داده‌هایی را که به‌تازگی نوشته شده‌اند، در تمام slave‌ها تکثیر می‌کند. 
 اگر اتفاقی در سرور master رخ دهد، تمام داده‌ها از بین می‌روند؛ در این حالت باید یک slave را به master تبدیل کنیم. 

Clustering In Redis 

Clustering یک تکنیک می‌باشد که توسط آن می‌توان داده‌ها را در چندین کامپیوتر تقسیم بندی کرد. فرض کنید که یک سرور Redis را با 64GB حافظه در اختیار داریم. در این حالت می‌توانیم 64GB داده داشته باشیم. اگر  10 تا clustered computer را که هر کدام 64GB حافظه اصلی دارند، داشته باشیم، در این حالت می‌توان 640GB  داده را ذخیره کرد. 
 

در تصویر بالا می‌توانیم ببینیم که داده‌ها در چهار node، ذخیره شده‌اند. هر node یک Redis Server پیکربندی شده می‌باشد؛ به عنوان یک cluster node. اگر یکی از node ‌ها دچار مشکل شوند، سپس کل cluster متوقف می‌شود. 

Redis Client 

وب سایت Try Redis ، یک Redis console client  آنلاین است و به شما کمک می‌کند تا یاد بگیرید چگونه از Redis console client  استفاده کنید.


در قسمت بعد در رابطه با نصب Redis  بر روی سیستم عامل ویندوز و دیتا تایپ‌ها در Redis صحبت خواهیم کرد.
مطالب
مروری بر Blazor (قسمت اول)

Blazer یک فریمورک جدید تحت وب هست که این امکان را به برنامه نویسان دات نت میدهد تا از طریق Open Web Standards بتوانند کدهای خود را در مرورگر اجرا و تجربه جدیدی از ساخت برنامه‌های تک صفحه‌ای را داشته باشند. در این نوشتار قصد داریم ساختار و نحوه کارکرد این فناوری را بررسی نماییم. قبل از هر چیزی به دوران قبل از ایجاد Web Assembly برمی‌گردیم :

همانطور که در شکل زیر می‌بینید، زمانی تنها جاوااسکریپت فرمانروای یک مرورگر محسوب می‌شد. در این حالت کدهای جاوااسکریپت به هر شکلی که نوشته شده باشند در اختیار parser قرار میگیرند  و یک درخت از کدهای نوشته شده ایجاد شده و از طریق یک کامپایلر، کد‌ها به سطح پایین‌تری مشابه بایت کدها تبدیل می‌گردند و سپس از طریق یک مفسر دسترسی به بخش‌های مختلف api یک مرورگر در اختیار این کدها قرار میگیرند تا کار مورد نظر انجام شود.

 

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

 

در شکل سوم Blazor که ترکیبی از نام Browser + Razor میباشد اضافه میشود. Blazor در اینجا وظیفه دارد محتوای فایل دریافتی را که شامل کدهای  HTML و  CSS و جاوااسکریپت است، به کدهای قابل فهمی برای مرورگر تبدیل کند. سپس mono وارد کار میشود. همانطور که می‌دانید mono جهت پشتیبانی از اجرای چندسکویی پروژه‌های دات نت اضافه شده که در اینجا هم همان وظیفه را منتها برای مرورگرهای مختلف، دارد. بدین جهت مونوی کامپایل شده بر روی Web Assembly قرار میگیرد تا کدهای دریافتی را تفسیر نماید. Blazor در اینجا dll‌های لازم را در mono بارگذاری میکند و سپس mono کدها را برای Web Assembly تفسیر میکند.

 

  اگر در تصویر بالا درقت کنید دو فایل Blazor.js و mono.js نیز وجود دارند که یک ارتباط به صورت Introp layer با Web Assembly برقرار کرده‌اند. البته در حال حاضر این ارتباط توسط Web Assembly پشتیبانی نمی‌شود. در صورت پیاده سازی و پشتیبانی Web Assembly از این بخش، میتوان با جاوااسکریپت هم با آن ارتباط برقرار کرد و یک ارتباط دو طرفه‌ای بین کدهای js و دات نت برقرار نمود؛ بدین صورت میتوان در دات نت توابع js را صدا زد و در js توابع دات نت صدا زده شوند.

همچنین مایکروسافت تنها به استفاده از Web Assembly اکتفا نکرده و از طریق SignalR نیز این  بستر را فراهم کرده است. با ایجاد یک سوکت به سمت سرور، تغییرات صفحه در سمت سرور، محاسبه و سپس بازگشت داده می‌شوند. در این حالت نیازی به ارسال فایل‌های dll نسبت به روش قبل نمی‌باشد. برای استفاده از این حالت میتوانید از بین گزینه‌های موجود در ایجاد پروژه، Blazor Server-side را مورد استفاده قرار دهید. البته این روش هم مزایا و معایب خودش را دارد.

جهت مقایسه این دو بخش به بررسی نکات مثبت و منفی میپردازیم:
1- در حالت استفاده از Web Assembly، حجمی حدود نزدیک به دو مگابایت بایدجابجا شود؛ ولی در حال سمت سرور، حجم صفحه حدود 100 کیلوبایت خواهد شد.
2- در حالت سمت سرور، تغییرات به دلیل رفت و برگشت به سرور با کمی تاخیر روبرو میشوند.
3- در حالت سمت سرور کارکرد آفلاین از دست میرود.
4- در حالت سرور، به دلیل اینکه همه کارها سمت سرور انجام میشود، ترافیک سرور را بالاتر میبرند.
5- استفاده از حالت سرور، معماری ساده‌تر و پیچیدگی‌های کمتری در سمت کلاینت دارد.
مطالب
گوگل ریدر و افزودن توضیحات

اگر به گوگل ریدر دقت کرده باشید، دو گزینه‌ی به اشتراک گذاری دارد: share و share with note .


اگر گزینه‌ی share with note را انتخاب کرده و توضیحی را ارسال یا اضافه کنیم، این توضیحات، به فید از نوع Atom اشتراک‌ها هم اضافه می‌شود. مثلا:

<?xml version="1.0"?>
<feed xmlns:media="http://search.yahoo.com/mrss/"
xmlns:gr="http://www.google.com/schemas/reader/atom/"
xmlns:idx="urn:atom-extension:indexing"
xmlns="http://www.w3.org/2005/Atom"
idx:index="no"
gr:dir="ltr">

...

<entry gr:crawl-timestamp-msec="1316627782108">
...
<gr:annotation>
<content type="html">text-text-text</content>
<author>
<name>Vahid</name>
</author>
</gr:annotation>
...
</entry>

...

</feed>



این افزونه استاندارد نیست و همانطور که در قسمت xmlns:gr اطلاعات فوق مشخص است، در فضای نام http://www.google.com/schemas/reader/atom/ معنا پیدا می‌کند. از دات نت سه و نیم به بعد هم کلاسی جهت خواندن فیدهای استاندارد وجود دارد (تعریف شده در فضای نام System.ServiceModel.Syndication). اما چگونه می‌توان این افزونه‌ی غیر استاندارد را با کمک امکانات توکار دات نت خواند؟
روش کار با استفاده از ElementExtensions هر آیتم یک فید است؛ به صورت زیر :

using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel.Syndication;
using System.Xml;
using System.Xml.Linq;

namespace Linq2Rss
{
public class RssEntry
{
public string Title { set; get; }
public string Description { set; get; }
public string Link { set; get; }
public DateTime PublicationDate { set; get; }
public string Author { set; get; }
public string BlogName { set; get; }
public string BlogAddress { set; get; }
public string Annotation { set; get; }
}

public static class AtomReader
{
private static string getAtomAnnotation(this SyndicationElementExtensionCollection items)
{
if (!items.Any()) return string.Empty;
var item = items.Where(x => x.OuterName.ToLowerInvariant() == "annotation").FirstOrDefault();
if (item == null) return string.Empty;

var element = item.GetObject<XElement>();
var content = element.Element("{http://www.w3.org/2005/Atom}content");
return content == null ? string.Empty : content.Value;
}

public static IList<RssEntry> GetEntries(string feedUrl)
{
using (var reader = XmlReader.Create(feedUrl))
{
var feed = SyndicationFeed.Load(reader);
if (feed == null) return null;

return feed.Items.Select(x =>
new RssEntry
{
Title = x.Title.Text,
Author = x.Authors.Any() ? x.Authors.First().Name : string.Empty,
Description = x.Content == null ? string.Empty : ((TextSyndicationContent)x.Content).Text,
Link = x.Links.Any() ? x.Links.First().Uri.AbsoluteUri : string.Empty,
PublicationDate = x.PublishDate.UtcDateTime,
BlogName = x.SourceFeed.Title.Text,
BlogAddress = x.SourceFeed.Links.Any() ? x.SourceFeed.Links.First().Uri.AbsoluteUri : string.Empty,
Annotation = x.ElementExtensions.getAtomAnnotation()

}).ToList();
}
}
}
}

در این مثال به کمک متد الحاقی getAtomAnnotation، مجموعه‌ی SyndicationElementExtensionCollection هر آیتم یک فید بررسی شده، در بین این‌ها، موردی که از نوع annotation باشد انتخاب و سپس content آن استخراج می‌گردد.


نکته‌ای دیگر:
اکثر کلاس‌های موجود در فضاهای نام مرتبط با XML در دات نت امکان خواندن اطلاعات را از یک Uri هم دارند؛ مانند مثال فوق و متد XmlReader.Create بکارگرفته شده در آن. اما اگر بخواهیم حین خواندن اطلاعات، یک پروکسی را نیز به پروسه جاری اضافه کنیم، به نظر خاصیت یا متدی جهت انجام اینکار وجود ندارد. برای رفع این مشکل می‌توان یک پروکسی سراسری را تعریف کرد. تنها کافی است خاصیت System.Net.WebRequest.DefaultWebProxy مقدار دهی شود. پس از آن به صورت خودکار بر روی کل برنامه تاثیر خواهد گذاشت.


مطالب
MVC Scaffolding #2
از آنجائیکه اصل کار با MVC Scaffolding از طریق خط فرمان پاورشل انجام می‌شود، بنابراین بهتر است در ادامه با گزینه‌ها و سوئیچ‌های مرتبط با آن بیشتر آشنا شویم.
دو نوع پارامتر حین کار با MVC Scaffolding مهیا هستند:

الف) سوئیچ‌ها
مانند پارامترهای boolean عمل کرده و شامل موارد ذیل می‌باشند. تمام این پارامترها به صورت پیش فرض دارای مقدار false بوده و ذکر هرکدام در دستور نهایی سبب true شدن مقدار آن‌ها می‌گردد:
Repository: برای تولید کدها بر اساس الگوی مخزن
Force: برای بازنویسی فایل‌های موجود.
ReferenceScriptLibraries: ارجاعاتی را به اسکریپت‌های موجود در پوشه Scripts، اضافه می‌کند.
NoChildItems: در این حالت فقط کلاس کنترلر تولید می‌شود و از سایر ملحقات مانند تولید Viewها، DbContext و غیره صرفنظر خواهد شد.

ب) رشته‌ها
این نوع پارامترها، رشته‌ای را به عنوان ورودی خود دریافت می‌کنند و شامل موارد ذیل هستند:
ControllerName: جهت مشخص سازی نام کنترلر مورد نظر
ModelType: برای ذکر صریح کلاس مورد استفاده در تشکیل کنترلر بکار می‌رود. اگر ذکر نشود، از نام کنترلر حدس زده خواهد شد.
DbContext: نام کلاس DbContext تولیدی را مشخص می‌کند. اگر ذکر نشود از نامی مانند ProjectNameContex استفاده خواهد کرد.
Project: پیش فرض آن پروژه جاری است یا اینکه می‌توان پروژه دیگری را برای قرار دادن فایل‌های تولیدی مشخص کرد. (برای مثال هربار یک سری کد مقدماتی را در یک پروژه جانبی تولید کرد و سپس موارد مورد نیاز را از آن به پروژه اصلی افزود)
CodeLanguage: می‌تواند cs یا vb باشد. پیش فرض آن زبان جاری پروژه است.
Area: اگر می‌خواهید کدهای تولیدی در یک ASP.NET MVC area مشخص قرار گیرند، نام Area مشخصی را در اینجا ذکر کنید.
Layout: در حالت پیش فرض از فایل layout اصلی استفاده خواهد شد. اما اگر نیاز است از layout دیگری استفاده شود، مسیر نسبی کامل آن‌را در اینجا قید نمائید.

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


MVC Scaffolding و مدیریت روابط بین کلاس‌ها

مثال قسمت قبلی بسیار ساده و شامل یک کلاس بود. اگر آن‌را کمی پیچیده‌تر کرده و برای مثال روابط one-to-many و many-to-many را اضافه کنیم چطور؟
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace MvcApplication1.Models
{
    public class Task
    {
        public int Id { set; get; }

        [Required]
        public string Name { set; get; }

        [DisplayName("Due Date")]
        public DateTime? DueDate { set; get; }

        [ForeignKey("StatusId")]
        public virtual Status Status { set; get; } // one-to-many
        public int StatusId { set; get; }

        [StringLength(450)]
        public string Description { set; get; }

        public virtual ICollection<Tag> Tags { set; get; } // many-to-many
    }

    public class Tag
    {
        public int Id { set; get; }

        [Required]
        public string Name { set; get; }

        public virtual ICollection<Task> Tasks { set; get; } // many-to-many
    }

    public class Status
    {
        public int Id { set; get; }

        [Required]
        public string Name { set; get; }
    }
}
کلاس Task تعریف شده اینبار دارای رابطه many-to-many با برچسب‌های مرتبط با آن است. همچنین یک رابطه one-to-many با کلاس وضعیت هر Task نیز تعریف شده است. به علاوه نکته تعریف «کار با کلیدهای اصلی و خارجی در EF Code first» نیز در اینجا لحاظ گردیده است.
در ادامه دستور تولید کنترلر‌های Task، Tag و Status ساخته شده با الگوی مخزن را در خط فرمان پاورشل ویژوال استودیو صادر می‌کنیم:
PM> Scaffold Controller -ModelType Task -ControllerName TasksController -DbContextType TasksDbContext -Repository -Force
PM> Scaffold Controller -ModelType Tag -ControllerName TagsController -DbContextType TasksDbContext -Repository -Force
PM> Scaffold Controller -ModelType Status -ControllerName StatusController -DbContextType TasksDbContext -Repository -Force
اگر به کارهایی که در اینجا انجام می‌شود دقت کنیم، می‌توان صرفه جویی زمانی قابل توجهی را شاهد بود؛ خصوصا در برنامه‌هایی که از ده‌ها فرم ورود اطلاعات تشکیل شده‌اند. فرض کنید قصد استفاده از ابزار فوق را نداشته باشیم. باید به ازای هر عملیات CRUD دو متد را ایجاد کنیم. یکی برای نمایش و دیگری برای ثبت. بعد بر روی هر متد کلیک راست کرده و Viewهای متناظری را ایجاد کنیم. سپس مجددا یک سری پیاده سازی «مقدماتی» تکراری را به ازای هر متد جهت ثبت یا ذخیره اطلاعات تدارک ببینیم. اما در اینجا پس از طراحی کلاس‌های برنامه، با یک دستور، حجم قابل توجهی از کدهای «مقدماتی» که بعدها مطابق نیاز ما سفارشی سازی و غنی‌تر خواهند شد، تولید می‌گردند.

چند نکته:
- با توجه به اینکه مدل‌ها تغییر کرده‌اند، نیاز است بانک اطلاعاتی متناظر نیز به روز گردد. مطالب مرتبط با آن‌را در مباحث Migrations می‌توانید مطالعه نمائید.
- View تولیدی رابطه many-to-many را پشتیبانی نمی‌کند. این مورد را باید دستی اضافه و طراحی کنید: (^ و ^)
- رابطه one-to-many به خوبی با View متناظری دارای یک drop down list تولید خواهد شد. در اینجا لیست تولیدی به صورت خودکار با مقادیر خاصیت Name کلاس Status پر می‌شود. اگر این نام دقیقا Name نباشد نیاز است توسط ویژگی به نام DisplayColumn که بر روی نام کلاس قرار می‌گیرد، مشخص کنید از کدام خاصیت باید استفاده شود.
@Html.DropDownListFor(model => model.StatusId,
((IEnumerable<Status>)ViewBag.PossibleStatus).Select(option => new SelectListItem {
  Text = (option == null ? "None" : option.Name),
  Value = option.Id.ToString(),
  Selected = (Model != null) && (option.Id == Model.StatusId)
}), "Choose...")
@Html.ValidationMessageFor(model => model.StatusId)


تولید آزمون‌های واحد به کمک MVC Scaffolding

MVC Scaffolding امکان تولید خودکار کلاس‌ها و متدهای آزمون واحد را نیز دارد. برای این منظور دستور زیر را در خط فرمان پاورشل وارد نمائید:
 PM> Scaffold MvcScaffolding.ActionWithUnitTest -Controller TasksController -Action ArchiveTask -ViewModel Task
دستوری که در اینجا صادر شده است نسبت به حالت‌های کلی قبلی، اندکی اختصاصی‌تر است. این دستور بر روی کنترلری به نام TasksController، جهت ایجاد اکشن متدی به نام ArchiveTask با استفاده از کلاس ViewModel ایی به نام Task اجرا می‌شود. حاصل آن ایجاد اکشن متد یاد شده به همراه کلاس TasksControllerTest است؛ البته اگر حین ایجاد پروژه جدید در ابتدای کار، گزینه ایجاد پروژه آزمون‌های واحد را نیز انتخاب کرده باشید. نام پروژه پیش فرضی که جستجوی می‌شود YourMvcProjectName.Test/Tests است.
 نکته مهم آن، عدم حذف یا بازنویسی کامل کنترلر یاد شده است. کاری هم که در تولید متد آزمون واحد متناظر انجام می‌شود، تولید بدنه متد آزمون واحد به همراه تولید کدهای اولیه الگوی Arrange/Act/Assert است. پر کردن جزئیات بیشتر آن با برنامه نویس است.
و یا به صورت خلاصه‌تر:
 PM> Scaffold UnitTest Tasks Delete
در اینجا متد آزمون واحد کنترلر Tasks و اکشن متد Delete آن، تولید می‌شود.

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