مطالب
تغییر عملکرد و یا ردیابی توابع ویندوز با استفاده از Hookهای دات نتی
مقدمه
در حالت پیشرفته‌ی تزریق وابستگی‌ها در دات نت، با توجه به اینکه کار وهله سازی کلاس‌ها به یک کتابخانه جانبی به نام IoC Container واگذار می‌شود، امکان یک سری دخل و تصرف نیز در این میان فراهم می‌گردد. برای مثال الان که ما می‌توانیم یک کلاس را توسط IoC container به صورت خودکار وهله سازی کنیم، خوب، چرا اجرای متدهای آن‌را تحت نظر قرار ندهیم. مثلا حاصل آن‌ها را بتوانیم پیش از اینکه به فراخوان بازگشت داده شود، کش کنیم یا کلا تغییر دهیم. به این کار AOP یا Aspect orinted programming نیز گفته می‌شود.
واقعیت این است که یک چنین مفهومی از سال‌های دور به نام Hooking در برنامه‌های WIN32 API خالص نیز وجود داشته است. Hookها یا قلاب‌ها دقیقا کار Interception دنیای AOP را انجام می‌دهند. به این معنا که خودشان را بجای یک متد ثبت کرده و کار ردیابی یا حتی تغییر عملکرد آن متد خاص را می‌توانند انجام دهند. برای مثال اگر برای متد gethostbyname ویندوز یک Hook بنویسیم، برنامه استفاده کننده، تنها متد ما را بجای متد اصلی gethostbyname واقع در Kernel32 ویندوز، مشاهده می‌کند و درخواست‌های DNS خودش را به این متد ویژه ما ارسال خواهد کرد؛ بجای ارسال درخواست‌ها به متد اصلی. در این بین می‌توان درخواست‌های DNS را لاگ کرد و یا حتی تغییر جهت داد.
انجام Interception در دنیای دات نت با استفاده از امکانات Reflection و Reflection.Emit قابل انجام است و یا حتی بازنویسی اسمبلی‌ها و افزودن کدهای IL مورد نیاز به آن‌ها که به آن IL Weaving هم گفته می‌شود. اما در دنیای WIN32 انجام چنین کاری ساده نیست و ترکیبی است از زبان اسمبلی و کتابخانه‌های نوشته شده به زبان C.
برای ساده سازی نوشتن Hookهای ویندوزی، کتابخانه‌ای به نام easy hook ارائه شده است که امکان تزریق Hookهای دات نتی را به درون پروسه برنامه‌های Native ویندوز دارد. این قلاب‌ها که در اینجا متدهای دات نتی هستند، نهایتا بجای توابع اصلی ویندوز جا زده خواهند شد. بنابراین می‌توانند عملیات آن‌ها را ردیابی کنند و یا حتی پارامترهای آن‌ها را دریافت و مقدار دیگری را بجای تابع اصلی، بازگشت دهند. در ادامه قصد داریم اصول و نکات کار با easy hook را در طی یک مثال بررسی کنیم.


صورت مساله
می‌خواهیم کلیه درخواست‌های تاریخ اکسپلورر ویندوز را ردیابی کرده و بجای ارائه تاریخ استاندارد میلادی، تاریخ شمسی را جایگزین آن کنیم.


از کجا شروع کنیم؟
ابتدا باید دریابیم که اکسپلورر ویندوز از چه توابع API ایی برای پردازش‌های درخواست‌های تاریخ و ساعت خودش استفاده می‌کند، تا بتوانیم برای آن‌ها Hook بنویسیم. برای این منظور می‌توان از برنامه‌ی بسیار مفیدی به نام API Monitor استفاده کرد. این برنامه‌ی رایگان را از آدرس ذیل می‌توانید دریافت کنید:
اگر علاقمند به ردیابی برنامه‌های 32 بیتی هستید باید apimonitor-x86.exe را اجرا کنید و اگر نیاز به ردیابی برنامه‌های 64 بیتی دارید باید apimonitor-x64.exe را اجرا نمائید. بنابراین اگر پس از اجرای این برنامه، برای مثال فایرفاکس را در لیست پروسه‌های آن مشاهده نکردید، یعنی apimonitor-x64.exe را اجرا کرده‌اید؛ از این جهت که فایرفاکس عمومی تا این تاریخ، نسخه 32 بیتی است و نه 64 بیتی.
پس از اجرای برنامه API Monitor، در قسمت API Filter آن باید مشخص کنیم که علاقمند به ردیابی کدامیک از توابع API ویندوز هستیم. در اینجا چون نمی‌دانیم دقیقا کدام تابع کار ارائه تاریخ را به اکسپلورر ویندوز عهده دار است، شروع به جستجو می‌کنیم و هر تابعی را که نام date یا time در آن وجود داشت، تیک می‌زنیم تا در کار ردیابی لحاظ شود.



سپس نیاز است بر روی نام اکسپلورر در لیست پروسه‌های این برنامه کلیک راست کرده و گزینه Start monitoring را انتخاب کرد:



اندکی صبر کنید یا یک صفحه جدید اکسپلورر ویندوز را باز کنید تا کار ردیابی شروع شود:


همانطور که مشاهده می‌کنید، ویندوز برای ردیابی تاریخ در اکسپلورر خودش از توابع GetDateFormatW و GetTimeFormatW استفاده می‌کند. ابتدا یک تاریخ را توسط آرگومان lpDate یا lpTime به یکی از توابع یاد شده ارسال کرده و سپس خروجی را از آرگومان lpDateStr یا lpTimeStr دریافت می‌کند.
خوب؛ به نظر شما اگر این خروجی‌ها را با یک ساعت و تاریخ شمسی جایگزین کنیم بهتر نیست؟!


نوشتن Hook برای توابع GetDateFormatW و GetTimeFormatW ویندوز اکسپلورر

ابتدا کتابخانه easy hook را از مخزن کد CodePlex آن دریافت کنید:

سپس یک پروژه کنسول ساده را آغاز کنید. همچنین به این Solution، یک پروژه Class library جدید را نیز اضافه نمائید. پروژه کنسول، کار نصب Hook را انجام می‌دهد و پروژه کتابخانه‌ای اضافه شده، کار مدیریت هوک‌ها را انجام خواهد داد. سپس به هر دو پروژه، ارجاعی را به اسمبلی EasyHook.dll  اضافه کنید.

الف) ساختار کلی کلاس Hook
کلاس Hook  واقع در پروژه Class library باید یک چنین امضایی را داشته باشد:
namespace ExplorerPCal.Hooks
{
    public class GetDateTimeFormatInjection : IEntryPoint
    {

        public GetDateTimeFormatInjection(RemoteHooking.IContext context, string channelName)
        {
            // connect to host...
            _interface = RemoteHooking.IpcConnectClient<MessagesReceiverInterface>(channelName);
            _interface.Ping();
        }

        public void Run(RemoteHooking.IContext context, string channelName)
        {
        }
    }
}
یعنی باید اینترفیس IEntryPoint کتابخانه easy hook را پیاده سازی کند. این اینترفیس خالی است و صرفا کار علامتگذاری کلاس Hook را انجام می‌دهد. همچنین این کلاس باید دارای سازنده‌ای با امضایی که ملاحظه می‌کنید و یک متد Run، دقیقا با همین امضای فوق باشد.

ب) نوشتن توابع Hook
کار نوشتن قلاب برای توابع API ویندوز در متد Run انجام می‌شود. سپس باید توسط متد LocalHook.Create کار را شروع کرد. در اینجا مشخص می‌کنیم که نیاز است تابع GetDateFormatW واقع در kernel32.dll ردیابی شود.
        public void Run(RemoteHooking.IContext context, string channelName)
        {
                GetDateFormatHook = LocalHook.Create(
                                        InTargetProc: LocalHook.GetProcAddress("kernel32.dll", "GetDateFormatW"),
                                        InNewProc: new GetDateFormatDelegate(getDateFormatInterceptor),
                                        InCallback: this);
در ادامه توسط یک delegate، کلیه فراخوانی‌های ویندوز را که قرار است به GetDateFormatW اصلی ارسال شوند، ردیابی کرده و تغییر می‌دهیم.

ج) نحوه مشخص سازی امضای delegateهای Hook
اگر امضای متد GetDateFormatW به نحو ذیل باشد:
        [DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int GetDateFormatW(
                                        uint locale,
                                        uint dwFlags, // NLS_DATE_FLAGS
                                        SystemTime lpDate,
                                        [MarshalAs(UnmanagedType.LPWStr)] string lpFormat,
                                        StringBuilder lpDateStr,
                                        int sbSize);
دقیقا delegate متناظر با آن نیز باید ابتدا توسط ویژگی UnmanagedFunctionPointer مزین شده و آن نیز دارای امضایی همانند تابع API اصلی باشد:
        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Auto, SetLastError = true)]
        private delegate int GetDateFormatDelegate(
                                        uint locale,
                                        uint dwFlags,
                                        SystemTime lpDate,
                                        [MarshalAs(UnmanagedType.LPWStr)] string lpFormat,
                                        StringBuilder lpDateStr,
                                        int sbSize);
سپس callback نهایی که کار دریافت پیام‌های ویندوز را انجام خواهد داد نیز، همان امضاء را خواهد داشت:
        private int getDateFormatInterceptor(
                                        uint locale,
                                        uint dwFlags,
                                        SystemTime lpDate,
                                        string lpFormat,
                                        StringBuilder lpDateStr,
                                        int sbSize)
        {

        }
در اینجا برای تغییر فرمت تاریخ ویندوز تنها کافی است lpDateStr را مقدار دهی کنیم. ویندوز lpDate و سایر پارامترها را به این متد ارسال می‌کند؛ در اینجا فرصت خواهیم داشت تا بر اساس این اطلاعات، lpDateStr صحیحی را تولید و مقدار دهی کنیم.

د) نصب Hook نوشته شده
باید دقت داشت که هر دو برنامه نصاب Hook و همچنین کتابخانه Hook، باید دارای امضای دیجیتال باشند. بنابراین به برگه signing خواص پروژه مراجعه کرده و یک فایل snk را به هر دو پروژه اضافه نمائید.
سپس در برنامه نصاب، یک کلاس را با امضای ذیل تعریف کنید:
public class MessagesReceiverInterface : MarshalByRefObject
{
    public void Ping()
    {
    }
}
این کلاس با استفاده از امکانات Remoting دات نت، پیام‌های دریافتی از هوک دات نتی تزریق شده به درون یک پروسه دیگر را دریافت می‌کند.
سپس در ابتدای برنامه نصاب، یک کانال Remoting باز شده (که آرگومان جنریک آن دقیقا همین نام کلاس MessagesReceiverInterface فوق را دریافت می‌کند)
 var channel = RemoteHooking.IpcCreateServer<MessagesReceiverInterface>(ref _channelName, WellKnownObjectMode.SingleCall);
و سپس توسط متد RemoteHooking.Inject کار تزریق ExplorerPCal.Hooks.dll به درون پروسه اکسپلورر ویندوز انجام می‌شود:
 RemoteHooking.Inject(
  explorer.Id,
  InjectionOptions.Default | InjectionOptions.DoNotRequireStrongName,
  "ExplorerPCal.Hooks.dll", // 32-bit version (the same, because of using AnyCPU)
  "ExplorerPCal.Hooks.dll", // 64-bit version (the same, because of using AnyCPU)
  _channelName
);
پارامتر اول متد RemoteHooking.Inject، شماره PID یک پروسه است. این شماره را از طریق متد Process.GetProcesses می‌توان بدست آورد. سپس یک سری پیش فرض مشخص می‌شوند و در ادامه مسیر کامل دو DLL هوک دات نتی باید مشخص شوند. چون تنظیمات پروژه هوک را بر روی Any CPU قرار داده‌ایم، فقط کافی است یک نام DLL را برای هوک‌های 64 بیتی و 32 بیتی ذکر کنیم.
پارامتر و پارامترهای بعدی، اطلاعاتی هستند که به سازنده کلاس هوک ارسال می‌شوند. بنابراین این سازنده می‌تواند تعداد پارامترهای متغیری داشته باشد:
 .ctor(IContext, %ArgumentList%)
void Run(IContext, %ArgumentList%)


چند نکته تکمیلی مهم برای کار با کتابخانه Easy hook
- کتابخانه easy hook فعلا با ویندوز 8 سازگار نیست.
- برای توزیع هوک‌های خود باید تمام فایل‌های همراه کتابخانه easy hook را نیز توزیع کنید و فقط به چند DLL ابتدایی آن بسنده نباید کرد.
- اگر هوک شما بلافاصله سبب کرش پروسه هدف شد، یعنی امضای تابع API شما ایراد دارد و نیاز است چندین و سایت را جهت یافتن امضایی صحیح بررسی کنید. برای مثال در امضای عمومی متد GetDateFormatW، پارامتر SystemTime به صورت struct تعریف شده است؛ درحالیکه ویندوز ممکن است برای دریافت زمان جاری به این پارامتر نال ارسال کند. اما struct دات نت برخلاف struct زبان C یک value type است و نال پذیر نیست. به همین جهت کلیه امضاهای عمومی که در مورد این متد در اینترنت یافت می‌شوند، در عمل غلط هستند و باید SystemTime را یک کلاس دات نتی که Refrence type است، تعریف کرد تا نال پذیر شود و hook کرش نکرده یا اشتباه عمل نکند.
- زمانیکه یک هوک easy hook بر روی پروسه هدف نصب می‌شود، دیگر قابل unload کامل نیست و نیاز است برای کارهای برنامه نویسی و به روز رسانی فایل dll جدید، پروسه هدف را خاتمه داد.
- متد Run هوک باید همیشه در حال اجرا باشد تا توسط CLR بلافاصه خاتمه نیافته و هوک از حافظه خارج نشود. اینکار را توسط روش ذیل انجام دهید:
             try
            {
                while (true)
                {
                    Thread.Sleep(500);
                    _interface.Ping();
                }
            }
            catch
            {
                _interface = null;
                // .NET Remoting will raise an exception if host is unreachable

            }
تا زمانیکه برنامه نصاب هوک که توسط Remoting دات نت، کانالی را به این هوک گشوده است، باز است، حلقه فوق اجرا می‌شود. با بسته شدن برنامه نصاب، متد Ping دیگر قابل دستیابی نبوده و بلافاصله این حلقه خاتمه می‌یابد.
- استفاده همزمان از API Monitor ذکر شده در ابتدای بحث و یک هوک نصب شده، سبب کرش برنامه هدف خواهد شد.

سورس کامل این پروژه را در اینجا می‌توانید دریافت کنید

 
مطالب
گرفتن خروجی XML از جداول در SQL Server 2012
فرض کنید که می‌خواهیم خروجی از جدول خود را به صورت XML نمایش یا از طریق وب سرویس در برنامه مان استفاده نماییم. اولین راهی که به ذهنمان می‌رسد خودمان  رشته xml را با حلقه ای ایجاد نماید یا استفاده از فضای نام System.Xml و کلاس‌های نوشته شده برای این کار . اما خود Sql Server امکانات ویژه ای برای کار با ساختار xml مهیا نموده که براحتی می‌توانید خروجی xml از داده هایتان ایجاد نمایید.

برای این کار از عبارت For XML در Select می‌توان استفاده نمود. برای مثال برای بدست آوردن ساختار ساده از For Xml Auto استفاده نمایید
SELECT BusinessEntityID, PersonType, Title, FirstName, MiddleName, LastName
FROM Person
WHERE BusinessEntityID = 10001
FOR XML AUTO
که خروجی بصورت node attribute زیر می‌باشد:

اما اگر بخواهیم خروجی به صورت node Elements باشد کافیست از پارامتر Elements استفاده نمایید 

SELECT BusinessEntityID, PersonType, Title, FirstName, MiddleName, LastName
FROM Person
WHERE BusinessEntityID = 10001
FOR XML AUTO, ELEMENTS

خروجی بصورت زیر می‌باشد:

اگر بخواهیم node attributes و node elements با هم ترکیب کنیم بصورت زیر عمل می‌کنیم:

SELECT BusinessEntityID AS '@ID', PersonType, Title,  FirstName, MiddleName, LastName
FROM Person
WHERE BusinessEntityID = 10001
FOR XML ELEMENTS

خروجی بصورت زیر است:

حال می‌خواهیم همه node‌ها را یک node ریشه قرار دهیم برای این کار از پارامتر ROOT در کنار AUTO به صورت زیر استفاده نمایید:

SELECT *
FROM Person
WHERE BusinessEntityID = 15291
FOR XML AUTO , ROOT('Persons')

اما اگر بخواهیم نام جدول را با نام دلخواه خود تغییر دهیم از پارامتر PATH به جای  AUTO به صورت زیر استفاده نمایید:

SELECT *
FROM Person
WHERE BusinessEntityID = 15291
FOR XML PATH('P') , ROOT('Persons')
مطالب
دایرکتوری‌های لینک در ایران

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

سایت مهندس:
http://www.mohand.es
بیشتر در زمینه مباحث علوم و تکنولوژی فعال است.

سایت بالاترین:
https://balatarin.com
اگر علاقمند باشید که خلاصه‌ی یک روز وبلاگ‌های ایرانی را که در زمینه مسایل اجتماعی (و غیره!) فعالیت می‌کنند، مطالعه کنید، این سایت در صدر قرار دارد.

سایت سی لینک:
http://www.30link.com/
شبیه به بالاترین که کمتر سیاسی است و بیشتر اجتماعی

سایت دنباله:
http://donbaleh.com
شبیه به سایت بالاترین

سایت oyax :
http://www.oyax.com/
که این روزها به نظر به دلیل نظارت ناکافی، پر از اسپم و لینک‌های تبلیغاتی شده!

لطفا اگر موارد دیگری را سراغ دارید، لینک‌ آن‌ها را به صورت کامنت قرار دهید.
با سپاس

بازخوردهای پروژه‌ها
راهنمایی برای دستورات for .use.جهت Initialize کردن ObjectFactory
من سرویس‌ها و اینترفیسهای مربوطه و مپینگ بین ویومدلها و کلاسهای POCO ی خودم را بشکل فایلهای پروفایل کامل کردم و با استفاده از StructureMap و مقالات تزریق وابستگی برای جدولی شبیه به Applicant (متقاضی) توانستم جواب بگیرم: 
استفاده از StructureMap به عنوان یک IoC Container
 خیلی شیوا روند کار را بیان کرده ولی برای کلاسی شبیه به Address (که یک ارتباط یک به چند با Applicant دارد ) که این کلاس جدید را خودم ایجاد کرده ام خطای No default Instance is registered and cannot be automatically determined for type را دریافت می‌کنم در سایت جستجو کردم و علت را متوجه شدم یعنی عدم وجود جملاتی مثل  
ObjectFactory.Initialize(x =>
{
  x.For<IEmailsService>().Use<EmailsService>();
  x.For<IUsersService>().Use<UsersService>();
});
در صورتی که آقای مهندس ربال این بخش را مثل کدهای بالا ننوشته اند و من ممنون میشم اگه باز راهنمایی بفرمایید.
مطالب
استفاده از #F در پروژه های MVC4
در این پست با روش پیاده سازی پروژه‌های WPF با استفاده از #F آشنا شدید. قصد دارم در طی چند پست روش پیاده سازی پروژه‌های Asp.Net MVC 4  با استفاده از #F را شرح دهم.
»اگر با #F آشنایی ندارید می‌توانید از اینجا شروع کنید.
به صورت کلی برای استفاده گسترده از #F در پروژه‌های وب نیاز به یک سری template‌های آماده داریم در غیر این صورت کار کمی سخت خواهد شد. به تصویر زیر دقت نمایید:

واضح است که با توجه به تصویر بالا کنترلر‌ها و البته مدل‌های app  و هر آنچه که سمت سرور به آن نیاز است باید با استفاده از #F پیاده سازی شوند. اما هنگامی که کنترلر‌ها با استفاده از #F نوشته شوند سیستم مسیر یابی نیز تحت تاثیر قرار خواهد گرفت. علاوه بر آن باید فکری برای بخش Bundling و همچنین فیلتر‌هاو... نمود. در نتیجه با توجه به template پروژه مورد نظر بر خلاف حالت پیش فرض #C آن که در قالب یک پروژه ارائه می‌شود در این جا حداقل به دو پروژه نیاز داریم. خوشبختانه همانند پروژه FSharpX که برای WPF مناسب است برای MVC نیز template آماده موجود است که در ادامه با آن آشنا خواهیم شد.

شروع به کار

ابتدا در VS.Net یک پروژه جدید ایجاد نمایید. از بخش Online Template گزینه FSharp MVC 4 را جستجو کنید. 

بعد از انتخاب نام پروژه و  کلیک بر روی Ok ( و البته دانلود حدود ده MB اطلاعات) صفحه زیر نمایان می‌شود. در این قسمت تنظیمات مربوط به انتخاب View Engine و نوع قالب پروژه را وجود دارد. در صورتی که قصد استفاده از Web Api را دارید گزینه  Web Api Project را انتخاب کنید در غیر این صورت گزینه Empty Project.

البته از Visual Studio 2012 به بعد این بخش به صورت زیر خواهد بود که قسمت Single Page App به آن اضافه شده است:

بعد از کلیک بر روی Ok یک پروژه بر اساس Template مورد نظر ساخته می‌شود. همانند تصویر زیر:

بررسی تغییرات

در یک نگاه به راحتی می‌توان تغییرات زیر را در پروژه Web تشخیص داد:

»پوشه Controller وجود ندارد؛

»پوشه مدل وجود ندارد؛

»فایل Global.asax دیگر فایلی به نام Global.asax.cs را همراه با خود ندارد.

دلیل اصلی عدم وجود موارد بالا این است که تمام این موارد باید به صورت #F پیاده سازی شوند در نتیجه به پروژه #F ساخته شده منتقل شده اند. فایل Global.asax را باز نمایید. سورس زیر قابل مشاهده است:

<%@ Application Inherits="FsWeb.Global" Language="C#" %> <script Language="C#" RunAt="server">
 // Defines the Application_Start method and calls Start in // System.Web.HttpApplication from which Global inherits. protected void Application_Start(Object sender, EventArgs e) { base.Start(); } </script>
تمامی کاری که تکه کد بالا انجام می‌دهد فراخواهی متد Start در فایل Global متناظر در پروژه #F است. اگر به قسمت reference‌های پروژه Web دقت کنید خواهید که به پروژه #F متناظر رفرنس دارد.
حال به بررسی پروژه #F  ساخته شده خواهیم پرداخت. در این پروژه یک فایل  Global.fs وجود دارد که سورس آن به صورت زیر است:

namespace FsWeb

open System
open System.Web
open System.Web.Mvc
open System.Web.Routing

type Route = { controller : string
               action : string
               id : UrlParameter }

type Global() =
    inherit System.Web.HttpApplication() 

    static member RegisterRoutes(routes:RouteCollection) =
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
        routes.MapRoute("Default", 
                        "{controller}/{action}/{id}", 
                        { controller = "Home"; action = "Index"
                          id = UrlParameter.Optional } )

    member this.Start() =
        AreaRegistration.RegisterAllAreas()
        Global.RegisterRoutes(RouteTable.Routes)
  در پروژه‌های MVC بر اساس زبان #C،  کلاسی به نام RouteConfig وجود دارد که در فایل Global.asax.cs فراخوانی می‌شود:
RouteConfig.RegisterRoutes(RouteTable.Routes);
و کد‌های مورد نظر جهت تعیین الگوی مسیر یابی کنترلر‌ها و درخواست‌ها در کلاس RouteConfig نیز به صورت زیر می‌باشد:
public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
        }
    }
اما در اینجا بر خلاف #C، تعریف و اجرای سیستم مسیر یابی در یک فایل انجام می‌شود. در بخش اول ابتدا یک type تعریف می‌شود که معادل با تعیین Routing در زبان #C است. علاوه بر آن متد RegisterRoutes جهت تعیین و انتساب Route Type تعریف شده به Route Collection مورد نظر نیز در این فایل می‌باشد. 

//تعریف الکوی مسیر یابی
type Route = { controller : string
               action : string
               id : UrlParameter }

type Global() =
    inherit System.Web.HttpApplication() 

    static member RegisterRoutes(routes:RouteCollection) =
       //فراخوانی  و انتساب الگوی مسیر یابی به  مسیر‌های تعریف شده
       routes.IgnoreRoute("{resource}.axd/{*pathInfo}")
        routes.MapRoute("Default", 
                        "{controller}/{action}/{id}", 
                        { controller = "Home"; action = "Index"
                          id = UrlParameter.Optional } )
در نهایت نیز تابع RegisterRoute در تابع Start فراخوانی خواهد شد.
Global.RegisterRoutes(RouteTable.Routes)
نتیجه کلی تا اینجا:
در این پست با Template پروژه‌های F# MVC 4 اشنا شدیم و از طرفی مشخص شد که برای پیاده سازی این گونه پروژه‌ها حداقل نیاز به دو پروژه داریم. یک پروژه که از نوع #C است ولی در آن فقط View ‌ها و فایل جاوااسکریپتی و البته Css وجود دارد. از طرف دیگر کنترلر‌ها و مدل‌ها و هر چیز دیگر که مربوط به سمت سرور است در قالب یک پروژه #F پیاده سازی می‌شود.
در پست بعدی با روش تعریف و توسعه کنترلر‌ها و مدل‌ها آشنا خواهیم شد.
نظرات مطالب
امکان ساخت قالب برای پروژه‌های NET Core.
قصد دارید همین پروژه را در داخل پوشه‌ای که دقیقا همان پروژه از پیش وجود دارد، مجددا ایجاد کنید. یک پوشه‌ی جدید و خالی را در جایی ایجاد کنید (نه در داخل پوشه‌ی پروژه‌ای که از GitHub گرفتید) و سپس این دستور را در آنجا اجرا کنید.
مطالب
ساخت تم سفارشی در انگیولار متریال ۲ - بخش اول

در  قسمت قبل  بیان شد همراه با نصب Angular Material، تعدادی تم از قبل ساخته شده نیز نصب خواهند شد که شامل یکسری استایل با رنگهای مشخصی هستند. این تم‌ها عبارتند از:

  • indigo-pink
  • deeppurple-amber
  • purple-green
  • pink-bluegrey 

انگیولار متریال ۲ علاوه بر این، امکاناتی برای ایجاد تم سفارشی را نیز فراهم کرده است. در این بخش قصد داریم برای قالب نمونه‌ای که قبلا ایجاد کرده بودیم یک تم سفارشی بسازیم.


مقدمه

تم در انگیولار متریال، از ترکیب چند پالت رنگی، ساخته می‌‌شود. پالت‌های رنگ را در طراحی متریال ( Material Design ) در  اینجا می‌توانید مشاهده کنید. انگیولار متریال رنگهای مورد استفاده خود را در گروه‌های زیر دسته بندی کرده است. 

  • Primary : این پالت رنگی به صورت گسترده در بخشهای مختلف صفحه و کامپوننت‌ها مورد استفاده قرار می‌گیرد.
  • Accent : این پالت رنگی برای دکمه‌های شناور و همچنین المنتهای تعاملی مورد استفاده قرار می‌گیرد.
  • Warn : این پالت رنگی برای مشخص کردن حالت‌های خطا، مورد استفاده قرار می‌گیرد.
  • Foreground : این پالت رنگی برای متون و آیکونها مورد استفاده قرار می‌گیرد.
  • Background : این پالت رنگی برای المنت‌های پس زمینه مورد استفاده قرار می‌گیرد.

در انگیولار متریال تمامی تم‌ها در زمان build به صورت استاتیک تولید می‌شوند. این قابلیت با خارج کردن چرخه تولید تم از چرخه راه‌اندازی برنامه، باعث بهبود در راه‌اندازی خواهد شد. 


تعریف تم سفارشی

برای ساخت تم سفارشی نیاز به یک فایل Sass خواهیم داشت. پس در مسیر /src یک فایل Sass را  با نام my-custom-theme.scss ایجاد می‌کنیم (شما می‌توانید از هر نام دیگری برای فایل Sass استفاده کنید). اگر از AngularCLI برای برنامه‌های خود استفاده می‌کنید، بایستی فایل Sass ایجاد شده را به لیست استایل‌ها در فایل angular-cli.json اضافه کنید. این کار باعث می‌شود AngularCLI این فایل Sass را در زمان build به css کامپایل کند. 

نکته: استفاده از فایل Sass برای ساختن تم سفارشی به این معنی نیست که شما از Sass برای سایر Style های برنامه خود استفاده کنید.

"styles": [
  "styles.css",
  "my-custom-theme.scss"
],

اگر از AngularCLI استفاده نمی‌کنید، شما نیاز به ابزاری برای کامپایل فایل Sass به css خواهید داشت.  ابزارهای بسیاری در این زمینه وجود دارند از جمله: gulp-sass و grunt-sass . ولی ساده‌ترین ابزار برای این کار node-sass می‌باشد. کافی است بعد از نصب، دستور زیر را اجرا کنید تا فایل sass به css کامپایل شود. فایل css تولید شده را مستقیما در صفحه index.html خود می‌توانید استفاده کنید.

node-sass src/my-custom-theme.scss dist/my-custom-theme.css

در فایل تم ایجاد شده ( my-custom-theme.scss ) ابتدا بایستی فایل Sass اصلی انگیولار متریال را وارد کنید.

@import '~@angular/material/theming';

در قدم بعدی mixin تعریف شده با نام mat-core  را در فایل Sass  انگیولار متریال، وارد می‌کنیم. این mixin شامل تمامی Styleهای مشترکی است که توسط کامپوننت‌های مختلف استفاده می‌شود. 

@include mat-core();

نکته: مطمئن شوید فقط یک بار این mixin را در سرتاسر برنامه خود وارد کرده باشید. در غیر این صورت، فایل css تولید شده شامل یکسری Style تکراری خواهد بود و این باعث بزرگ و حجیم شدن فایل css نهایی خواهد شد. 


تا اینجا فایل تم ایجاد شده اینگونه خواهد بود:  

@import '~@angular/material/theming';
@include mat-core();

حالا نوبت تعریف تم سفارشی است. ولی قبل از آن باید با سیستم رنگها در طراحی متریال ( Material Design ) آشنایی داشته باشیم. در طراحی متریال ۱۹ پالت رنگی با نام‌های مختلف وجود دارند. برای ۱۶ پالت رنگی، ۱۴ طیف رنگی و برای ۳ پالت رنگی دیگر، ۱۰ طیف رنگی در نظر گرفته شده است. هر کدام از این طیف‌های رنگی، دارای یک مقدار عددی است. یعنی یک رنگ در سیستم طراحی متریال متشکل از یک نام رنگ و یک شماره طیف رنگ است که مقدار پیش فرض طیف رنگ، عدد ۵۰۰ می‌باشد. 


حالا با استفاده از تابع mat-palette تعریف شده در فایل Sass انگیولار متریال، سه متغیر را برای رنگهای Primary ، Accent و Warn در فایل my-custom-theme.scss ، به شکل زیر تعریف می‌کنیم. 

$my-app-primary: mat-palette($mat-indigo);
$my-app-accent:  mat-palette($mat-pink, 500, A100, A400);
$my-app-warn:    mat-palette($mat-deep-orange);

تابع mat-palette در فایل Sass اصلی انگیولار متریال، به شکل زیر تعریف شده است. 

@function mat-palette($base-palette, $default: 500, $lighter: 100, $darker: 700)

این تابع یک پارامتر اجباری دارد و بقیه پارامترها اختیاری هستند.

  • base-palette $: نام رنگ را دریافت می‌کند. این پارامتر اجباری است و باید مشخص شود. 
  • default$: با این پارامتر، طیف پیش‌فرض رنگ انتخاب شده را مشخص می‌کنیم. این پارامتر اختیاری است و مقدار پیش فرض آن 500 است.
  • lighter$: با این پارامتر، طیف روشن رنگ انتخاب شده را مشخص می‌کنیم. این پارامتر اختیاری است و مقدار پیش فرض آن 100 است.
  • darker$: با این پارامتر، طیف تیره رنگ انتخاب شده را مشخص می‌کنیم. این پارامتر اختیاری است و مقدار پیش فرض آن 700 است.

در قدم آخر با استفاده از تابع mat-light-theme یا mat-dark-theme، رنگهای تعریف شده در مرحله قبل را ترکیب کرده و نتیجه را به عنوان ورودی به mixin به نام angular-material-theme  ارسال و بارگذاری می‌کنیم. 

تابع mat-light-theme و mat-dark-theme سه پارامتر را دریافت می‌کند. پارارمتر اول پالت رنگ ایجاد شده توسط تابع mat-palette برای گروه Primary ، پارامتر دوم پالت رنگ ایجاد شده برای گروه Accent و پارامتر سوم پالت رنگ ایجاد شده برای گروه Warn را دریافت می‌کند. دو پارامتر اول اجباری و پارامتر سوم اختیاری با مقدار پیش فرض mat-palette($mat-red) می‌باشد. 

شکل کلی فایل Sass در نهایت به شکل زیر خواهد بود. 

@import '~@angular/material/theming';
@include mat-core();
$my-app-primary: mat-palette($mat-teal);
$my-app-accent:  mat-palette($mat-amber, 500, A100, A400);
$my-app-warn:    mat-palette($mat-deep-orange);

$my-app-theme: mat-light-theme($my-app-primary, $my-app-accent, $my-app-warn);

@include angular-material-theme($my-app-theme);

برای استفاده از پالت رنگ‌های ایجاد شده، از خصوصیت color در المنت‌های انگولار متریال استفاده می‌کنیم. برای نمونه بعد از تغییر فایل Sass به شکل بالا و حذف لینک تم از پیش ساخته شده که در پست قبلی به Style.cs اضافه کرده بودیم، می‌توانیم کار خود را به صورت زیر آزمایش کنیم. در فایل app.component.html در تگ main کدهای زیر را اضافه کنید. 

<md-card>
  <button md-raised-button color="primary">
    Primary
  </button>
  <button md-raised-button color="accent">
    Accent
  </button>
  <button md-raised-button color="warn">
    Warning
  </button>
</md-card>

خروجی زیر را مشاهده خواهید کرد. 

همچنین می‌توانید به جای استفاده از تابع mat-light-theme از تابع mat-dark-theme استفاده کنید. دراین صورت خروجی زیر را خواهید دید. 

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

کدهای این قسمت را از اینجا دریافت کنید:  ساخت-تم-سفارشی-در-انگولار-متریال-۲---بخش-اول.rar 

مطالب
آموزش MDX Query - قسمت چهارم –آشنایی با AdventureWorksDW2008R2 و آشنایی با محیط BIMS

در این قسمت تلاش می‌کنم در خصوص محیط BIMS (Business Intelligence Management Studio)  و همچنین AdventureWorksDW2008R2 توضیحاتی را ارائه کنم. در ابتدا در خصوص طراحی انجام شده در Data Warehouse  مربوط به پایگاه داده‌ی Adventure Works 2008 توضیحاتی ارایه می‌گردد.

شاید بهترین کار در خصوص آشنایی با یک پایگاه داده نگاه کردن به دیاگرام کلی آن پایگاه داده باشد. بنابر این در ابتدا می‌بایست یک دیاگرام از پایگاه داده‌ی AdventureWorksDW2008R2 بسازیم (این کار را در SQL Server Management Studio انجام می‌دهیم) . قبل از ساخت دیاگرام می‌بایست کاربر Sa را به عنوان Owner پایگاه داده معرفی کنیم.

برای این منظور ابتدا Properties پایگاه داده‌ی AdventureWorksDW2008R2  را گرفته و به قسمت Files رفته و با انتخاب دکمه‌ی ... در مقابل Owner و جستجوی کاربر Sa ، اقدام به مشخص کردن مالک پایگاه داده می‌کنیم. و سپس دکمه‌ی Ok را می‌زنیم.  

مطابق شکل زیر 


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


با یک نگاه اجمالی مشخص می‌گردد که نام تمامی جداول پایگاه داده‌ی DW یا با کلمه‌ی Dim یا با کلمه‌ی Fact شروع شده‌اند. 

همان طور که در مقاله‌ی شماره‌ی یک نیز عنوان شد، چندین روش طراحی DW وجود دارد :

1. ستاره ای

2. دانه برفی

3. کهکشانی

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

یکی از روش‌های تهیه‌ی DW این می‌باشد که کاربران خبره در هر سیستم، مشخص نمایند چه گزارشاتی مورد نظر آنها می‌باشد. سپس توسط تیم پشتیبانی آن سیستم‌ها، جداول Fact,Dimension  مورد نیاز برای حصول گزارش مربوطه تهیه گردد.

شاید ذکر این نکته جالب باشد که برای توسعه‌ی یک پایگاه داده‌ی Multidimensional توسط Solution ‌های ماکروسافت نیازی به آشنایی با یک محیط کار ( IDE ) جدید نمی‌باشد. همان طور هم که در مقاله‌ی قبلی اشاره شد، برای Deploy کردن یک پایگاه داده‌ی چند بعدی ( Multidimensional ) از خود محیط   Visual Studio .Net استفاده می‌شود. بنابر این آن دسته از برنامه نویسانی که با این محیط آشنا می‌باشند به راحتی می‌توانند به توسعه‌ی پایگاه داده‌ی چند بعدی بپردازند.

لازم به ذکر می‌باشد که اساسا هدف من از شروع این سری مقالات ، آموزش MDX Query ‌ها می‌باشد و نه آموزش BIMS ، با این وجود در این قسمت و در قسمت بعدی، توضیحات مقدماتی کار با BIMS ارایه می‌گردد و همچنین در فرصت مناسب در خصوص BIMS یک مجموعه مقاله‌ی جامع ارایه خواهم کرد.

در ابتدا اجزا BIMS را برای شما توضیح می‌دهم و سپس در خصوص ساخت هر کدام از آنها و ترتیب ساخت آنها توضیحاتی ارایه خواهم داد.

مسیر باز کردن برنامه‌ی  SQL Server Business Intelligence Development Studio = BIDS در زیر آمده است:

C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft SQL Server 2012\ SQL Server Data Tools

دقت داشته باشید که در صورت استفاده از نسخه‌ی Sql Server 2008 می‌بایست مسیر زیر را جستجو نمایید:

C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Microsoft SQL Server 2008 R2

با نگاه کردن به محیط BIMS  می توانید پنجره‌ی Solution Explorer  را مشاهده کنید .(در صورت عدم مشاهده، می‌توانید این پنجره را از منوی View باز کنید)



در پنجره‌ی Solution Explorer ابتدا نام Solution و در زیر آن، نام پروژه را خواهیم دید (نام پروژه و نام پایگاه داده‌ی چند بعدی، مشابه یکدیگر می‌باشند) و در زیر نام پروژه، موارد زیر را می‌بینیم:

1. Data Source

2. Data Source View

3. Cubes

4. Dimensiones

5. ….

Data Source  : عملا برقرار کننده‌ی پروژه با Data Warehouse می‌باشد. دقت داشته باشید که امکان تهیه یک پایگاه داده‌ی چند بعدی از چندین DW وجود دارد و حتا نوع DW ‌ها می‌تواند متفاوت باشد (به عبارت دیگر ما می‌توانیم چندین DW در RDBMS ‌های متفاوت داشته باشیم و همه‌ی آنها را در یک Multidimensional Database تجمیع کنیم). برای انجام چنین کاری باید چندین Data Source  تعریف کنیم. 


Data Source View : هر Data Source می‌تواند دارای چندین تقسیم بندی با مفاهیم Business ی باشد. برای هر کدام از این دسته بندی‌ها می‌توانیم یک یا چند Data Source View  ایجاد کنیم . به عبارت دیگر ایجاد Data Source View ‌ها سبب خلاصه شدن تعداد جداول Fact , Dimension براساس یک بیزینس خاص می‌باشد و در ادامه راحت‌تر می‌توانیم Cube ‌ها را تولید کنیم. 


نکته: جداول Fact , Dimension در ساختار D ata Warehouse ساخته می‌شوند.

Cubes : محل تعریف Cube ‌ها در این قسمت می‌باشد. در سری آموزش SSAS در خصوص نحوه‌ی ساخت Cube ‌ها شرح کاملی ارایه خواهم کرد. 


Dimensions : با توجه به این که در روال ساخت Cube ما مشخص می‌کنیم چه Dimension هایی داریم، یک سری از Dimension ‌ها به صورت پیش فرض در این قسمت قرار می‌گیرند و البته در صورت تغییر در Data Source View   می‌توانیم یک Dimension را به صورت دستی در این قسمت ایجاد نماییم و سپس آن را به Cube مورد نظر اضافه نماییم. 


دقت داشته باشید که برای ساخت یک پروژه می‌بایست بعد از ساخت Data Warehouse در برنامه‌ی BIMS اقدام به ساخت یک Data Source کنیم و سپس با توجه به Business‌های موجود در سیستم‌های OLTP اقدام به ساخت Data Source View‌های مناسب کرده و در نهایت اقدام به ساخت Cube کنیم. بعد از انجام تنظیمات مختلف در Cube مانند ساخت Hierarchy , KPI و ... نیاز می‌باشد که پروژه را Deploy کنیم تا پایگاه داده‌ی چند بعدی (MDB) ساخته شود. 

در قسمت بعدی نحوه‌ی ساخت یک پروژه در SSAS و چگونگی باز کردن یک پایگاه داده را بررسی خواهیم کرد. 

مطالب
معادل‌های چندسکویی اجزای فایل web.config در ASP.NET Core
هنوز هم اجزای مختلف فایل web.config در ASP.NET Core قابل تعریف و استفاده هستند؛ اما اگر صرفا بخواهیم از این نوع برنامه‌ها در ویندوز و به کمک وب سرور IIS استفاده کنیم. با انتقال برنامه‌های چندسکویی مبتنی بر NET Core. به سایر سیستم عامل‌ها، دیگر اجزایی مانند استفاده‌ی از ماژول فشرده سازی صفحات IIS و یا ماژول URL rewrite آن و یا تنظیمات static cache تعریف شده‌ی در فایل web.config، شناسایی نشده و تاثیری نخواهند داشت. به همین جهت تیم ASP.NET Core، معادل‌های توکار و چندسکویی را برای عناصری از فایل web.config که به IIS وابسته هستند، تهیه کرده‌است که در ادامه آن‌‌ها را مرور خواهیم کرد.


میان‌افزار چندسکویی فشرده سازی صفحات در ASP.NET Core

پیشتر مطلب «استفاده از GZip توکار IISهای جدید و تنظیمات مرتبط با آن‌ها» را در سایت جاری مطالعه کرده‌اید. این قابلیت صرفا وابسته‌است به IIS و همچنین در صورت نصب بودن ماژول httpCompression آن کار می‌کند. بنابراین قابلیت انتقال به سایر سیستم عامل‌ها را نخواهد داشت و هرچند تنظیمات فایل web.config آن هنوز هم در برنامه‌های ASP.NET Core معتبر هستند، اما چندسکویی نیستند. برای رفع این مشکل، تیم ASP.NET Core، میان‌افزار توکاری را برای فشرده سازی صفحات ارائه داده‌است که جزئی از تازه‌های ASP.NET Core 1.1 نیز به‌شمار می‌رود.
برای نصب آن دستور ذیل را در کنسول پاورشل نیوگت، اجرا کنید:
 PM> Install-Package Microsoft.AspNetCore.ResponseCompression
که معادل است با افزودن وابستگی ذیل به فایل project.json پروژه:
{
    "dependencies": {
        "Microsoft.AspNetCore.ResponseCompression": "1.0.0"
    }
}

مرحله‌ی بعد، افزودن سرویس‌های و میان افزار مرتبط، به کلاس آغازین برنامه هستند. همیشه متدهای Add کار ثبت سرویس‌های میان‌افزار را انجام می‌دهند و متدهای Use کار افزودن خود میان‌افزار را به مجموعه‌ی موجود تکمیل می‌کنند.
public void ConfigureServices(IServiceCollection services)
{
    services.AddResponseCompression(options =>
    {
        options.MimeTypes = Microsoft.AspNetCore.ResponseCompression.ResponseCompressionDefaults.MimeTypes;
    });
}
متد AddResponseCompression کار افزودن سرویس‌های مورد نیاز میان‌افزار ResponseCompression را انجام می‌دهد. در اینجا می‌توان تنظیماتی مانند MimeTypes فایل‌ها و صفحاتی را که باید فشرده سازی شوند، تنظیم کرد. ResponseCompressionDefaults.MimeTypes به این صورت تعریف شده‌است:
namespace Microsoft.AspNetCore.ResponseCompression
{
    /// <summary>
    /// Defaults for the ResponseCompressionMiddleware
    /// </summary>
    public class ResponseCompressionDefaults
    {
        /// <summary>
        /// Default MIME types to compress responses for.
        /// </summary>
        // This list is not intended to be exhaustive, it's a baseline for the 90% case.
        public static readonly IEnumerable<string> MimeTypes = new[]
        {
            // General
            "text/plain",
            // Static files
            "text/css",
            "application/javascript",
            // MVC
            "text/html",
            "application/xml",
            "text/xml",
            "application/json",
            "text/json",
        };
    }
}
اگر علاقمند بودیم تا عناصر دیگری را به این لیست اضافه کنیم، می‌توان به نحو ذیل عمل کرد:
services.AddResponseCompression(options =>
{
    options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
                                {
                                    "image/svg+xml",
                                    "application/font-woff2"
                                });
            });
در اینجا تصاویر از نوع svg و همچنین فایل‌های فونت woff2 نیز اضافه شده‌اند.

به علاوه options ذکر شده‌ی در اینجا دارای خاصیت options.Providers نیز می‌باشد که نوع و الگوریتم فشرده سازی را مشخص می‌کند. در صورتیکه مقدار دهی نشود، مقدار پیش فرض آن Gzip خواهد بود:
services.AddResponseCompression(options =>
{
  //If no compression providers are specified then GZip is used by default.
  //options.Providers.Add<GzipCompressionProvider>();

همچنین اگر علاقمند بودید تا میزان فشرده سازی تامین کننده‌ی Gzip را تغییر دهید، نحوه‌ی تنظیمات آن به صورت ذیل است:
services.Configure<GzipCompressionProviderOptions>(options =>
{
  options.Level = System.IO.Compression.CompressionLevel.Optimal;
});

به صورت پیش‌فرض، فشرده سازی صفحات Https انجام نمی‌شود. برای فعال سازی آن تنظیم ذیل را نیز باید قید کرد:
 options.EnableForHttps = true;

مرحله‌ی آخر این تنظیمات، افزودن میان افزار فشرده سازی خروجی به لیست میان افزارهای موجود است:
public void Configure(IApplicationBuilder app)
{
   app.UseResponseCompression()  // Adds the response compression to the request pipeline
   .UseStaticFiles(); // Adds the static middleware to the request pipeline  
}
در اینجا باید دقت داشت که ترتیب تعریف میان‌افزارها مهم است و اگر UseResponseCompression پس از UseStaticFiles ذکر شود، فشرده سازی صورت نخواهد گرفت؛ چون UseStaticFiles کار ارائه‌ی فایل‌ها را تمام می‌کند و نوبت اجرا، به فشرده سازی اطلاعات نخواهد رسید.


تنظیمات کش کردن چندسکویی فایل‌های ایستا در ASP.NET Core

تنظیمات کش کردن فایل‌های ایستا در web.config مخصوص IIS به صورت ذیل است :
<staticContent>
   <clientCache httpExpires="Sun, 29 Mar 2020 00:00:00 GMT" cacheControlMode="UseExpires" />
</staticContent>
معادل چندسکویی این تنظیمات در ASP.NET Core با تنظیم Response.Headers میان افزار StaticFiles انجام می‌شود:
public void Configure(IApplicationBuilder app,
                      IHostingEnvironment env,
                      ILoggerFactory loggerFactory)
{
    app.UseResponseCompression()
       .UseStaticFiles(
           new StaticFileOptions
           {
               OnPrepareResponse =
                   _ => _.Context.Response.Headers[HeaderNames.CacheControl] = 
                        "public,max-age=604800" // A week in seconds
           })
       .UseMvc(routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"));
}
در اینجا پیش از آماده شدن یک فایل استاتیک برای ارائه‌ی نهایی، می‌توان تنظیمات Response آن‌را تغییر داد و برای مثال هدر Cache-Control آن‌را به یک هفته تنظیم نمود.


معادل چندسکویی ماژول URL Rewrite در ASP.NET Core

مثال‌هایی از ماژول URL Rewrite را در مباحث بهینه سازی سایت برای بهبود SEO پیشتر بررسی کرده‌ایم (^ و ^ و ^). این ماژول نیز همچنان در ASP.NET Core هاست شده‌ی در ویندوز و IIS قابل استفاده است (البته به شرطی که ماژول مخصوص آن در IIS نصب و فعال شده باشد). معادل چندسکویی این ماژول به صورت یک میان‌افزار توکار به ASP.NET Core 1.1 اضافه شده‌است.
برای استفاده‌ی از آن، ابتدا نیاز است بسته‌ی نیوگت آن‌را به نحو ذیل نصب کرد:
 PM> Install-Package Microsoft.AspNetCore.Rewrite
و یا افزودن آن به لیست وابستگی‌های فایل project.json:
{
    "dependencies": {
        "Microsoft.AspNetCore.Rewrite": "1.0.0"
    }
}

پس از نصب آن، نمونه‌ای از نحوه‌ی تعریف و استفاده‌ی آن در کلاس آغازین برنامه به صورت ذیل خواهد بود:
public void Configure(IApplicationBuilder app)
{
    app.UseRewriter(new RewriteOptions()
                            .AddRedirectToHttps()
                            .AddRewrite(@"app/(\d+)", "app?id=$1", skipRemainingRules: false) // Rewrite based on a Regular expression
                            //.AddRedirectToHttps(302, 5001) // Redirect to a different port and use HTTPS
                            .AddRedirect("(.*)/$", "$1")  // remove trailing slash, Redirect using a regular expression
                            .AddRedirect(@"^section1/(.*)", "new/$1", (int)HttpStatusCode.Redirect)
                            .AddRedirect(@"^section2/(\\d+)/(.*)", "new/$1/$2", (int)HttpStatusCode.MovedPermanently)
                            .AddRewrite("^feed$", "/?format=rss", skipRemainingRules: false));
این میان‌افزار نیز باید پیش از میان افزار فایل‌های ایستا و همچنین MVC معرفی شود.

در اینجا مثال‌هایی را از اجبار به استفاده‌ی از HTTPS، تا حذف / از انتهای مسیرهای وب سایت و یا هدایت آدرس قدیمی فید سایت، به آدرسی جدید واقع در مسیر format=rss، توسط عبارات باقاعده مشاهده می‌کنید.
در این تنظیمات اگر پارامتر skipRemainingRules به true تنظیم شود، به محض برآورده شدن شرط انطباق مسیر (پارامتر اول ذکر شده)، بازنویسی مسیر بر اساس پارامتر دوم، صورت گرفته و دیگر شرط‌های ذکر شده، پردازش نخواهند شد.

این میان‌افزار قابلیت دریافت تعاریف خود را از فایل‌های web.config و یا htaccess (لینوکسی) نیز دارد:
 app.UseRewriter(new RewriteOptions()
.AddIISUrlRewrite(env.ContentRootFileProvider, "web.config")
.AddApacheModRewrite(env.ContentRootFileProvider, ".htaccess"));
بنابراین اگر می‌خواهید تعاریف قدیمی <system.webServer><rewrite><rules> وب کانفیگ خود را در اینجا import کنید، متد AddIISUrlRewrite چنین کاری را به صورت خودکار برای شما انجام خواهد داد و یا حتی می‌توان این تنظیمات را در یک فایل UrlRewrite.xml نیز قرار داد تا توسط IIS پردازش نشود و مستقیما توسط ASP.NET Core مورد استفاده قرار گیرد.

و یا اگر خواستید منطق پیچیده‌تری را نسبت به عبارات باقاعده اعمال کنید، می‌توان یک IRule سفارشی را نیز به نحو ذیل تدارک دید:
public class RedirectWwwRule : Microsoft.AspNetCore.Rewrite.IRule
{
    public int StatusCode { get; } = (int)HttpStatusCode.MovedPermanently;
    public bool ExcludeLocalhost { get; set; } = true;
    public void ApplyRule(RewriteContext context)
    {
        var request = context.HttpContext.Request;
        var host = request.Host;
        if (host.Host.StartsWith("www", StringComparison.OrdinalIgnoreCase))
        {
            context.Result = RuleResult.ContinueRules;
            return;
        }
        if (ExcludeLocalhost && string.Equals(host.Host, "localhost", StringComparison.OrdinalIgnoreCase))
        {
            context.Result = RuleResult.ContinueRules;
            return;
        }
        string newPath = request.Scheme + "://www." + host.Value + request.PathBase + request.Path + request.QueryString;
 
        var response = context.HttpContext.Response;
        response.StatusCode = StatusCode;
        response.Headers[HeaderNames.Location] = newPath;
        context.Result = RuleResult.EndResponse; // Do not continue processing the request
    }
}
در اینجا تنظیم context.Result به RuleResult.ContinueRules سبب ادامه‌ی پردازش درخواست جاری، بدون تغییری در نحوه‌ی پردازش آن خواهد شد. در آخر کار، با تغییر HeaderNames.Locatio به مسیر جدید و تنظیم Result = RuleResult.EndResponse، سبب اجبار به بازنویسی مسیر درخواستی، به مسیر جدید تنظیم شده، خواهیم شد.

و سپس می‌توان آن‌را به عنوان یک گزینه‌ی جدید Rewriter معرفی نمود:
 app.UseRewriter(new RewriteOptions().Add(new RedirectWwwRule()));
کار این IRule جدید، اجبار به درج www در آدرس‌های هدایت شده‌ی به سایت است؛ تا تعداد صفحات تکراری گزارش شده‌ی توسط گوگل به حداقل برسد (یک نگارش با www و دیگری بدون www).

یک نکته: در اینجا در صورت نیاز می‌توان از تزریق وابستگی‌های در سازنده‌ی کلاس Rule جدید تعریف شده نیز استفاده کرد. برای اینکار باید RedirectWwwRule را به لیست سرویس‌های متد ConfigureServices معرفی کرد و سپس نحوه‌ی دریافت وهله‌ای از آن جهت معرفی به میان‌افزار بازنویسی مسیرهای وب به صورت ذیل درخواهد آمد:
 var options = new RewriteOptions().Add(app.ApplicationServices.GetService<RedirectWwwRule>());
مطالب
WF:Windows Workflow #۳
اضافه کردن عناصر رویه ای 

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

همانطور که در شکل مشاهده می‌کنید دو متغیر به نام‌های Counter و numberBells تعریف شده است و نوع هر دو، از جنس Int32 می‌باشد و در محدوده Sequence  قرار گرفته‌اند. در قسمت پیش فرض، مقدار مورد نظر را تعیین کرده‌ایم.
نکته : این مورد را در نظر داشته باشید که قبل از تعریف Variables باید حتما یک Sequence در صفحه داشته باشیم تا بتوانیم محدوده متغیر مورد نظر را مشخص کنیم. 
حال مانند قسمت پیش، ابتدا باید از  Toolbox، قسمت Control Flow، کنترل  Sequence را به داخل صفحه کشید و پس از آن می‌توانیم از سایر کنترل‌ها استفاده نمائیم. پس از این کار، از قسمت  Primitives کنترل  WriteLine را به درون Sequence انتقال می‌دهیم٬ سپس مانند شکل زیر یک کنترل IF که در قسمت Control Flow موجود می‌باشد را انتخاب کرده و به زیر کنترل WriteLine انتقال می‌دهیم. مانند شکل زیر:


همانطور که در شکل مشاهده می‌کنید، در کنترل IF در قسمت Condition شرط مورد نظر را مشخص می‌کنیم. در قسمت THEN از کنترل Assign استفاده شده است. از این کنترل وقتی استفاده می‌شود که قصد انتساب یک مقدار را به متغیری داریم. این کنترل در قسمت Primitives  موجود است.
حال مانند شکل زیر عمل کرده و یک کنترل While را به زیر IF اضافه می‌کنیم؛ مانند شکل زیر :

کنترل While از دو قسمت تشکیل شده٬ شرط حلقه و بدنه آن. در قسمت شرط، مانند دستور IF عمل کرده و در قسمت بدنه، دستورات مورد نظر را مشخص می‌کنیم. در حلقه موجود تا زمانیکه متغیر  Counter از numberBelss کوچکتر مساوی باشد، این حلقه اجرا می‌شود و در طی این جریان، ابتدا مقدار متغیر counter چاپ می‌شود٬ سپس یکی به مقدار آن اضافه می‌شود؛ البته با یک وقفه مشخص. برای اینکه بتوانیم در هر قسمت Workflow وقفه ایجاد کنیم، از کنترل Delay استفاده می‌شود. این کنترل دارای خاصیتی است به نام Duration. در این قسمت می‌توان میزان وقفه را مشخص نمود.
برای مقدار دادن به این خاصیت، کنترل Delay را انتخاب کرده٬ سپس از قسمت Properties در VS2010 یا VS2012 می‌توان به خاصیت Duration مقدار داد.
TimeSpan.FromSeconds(1)
از این طریق وقفه‌ای که در بر نامه ایجاد می‌شود، یک ثانیه می‌باشد.

در این قسمت هم چک می‌شود که اگر ساعت جاری سیستم بیشتر از ۱۸ بود مقدار «عصر بخیر» چاپ شود، در غیر اینصورت مقدار «روز خوب» چاپ می‌شود.
نکته : در قسمت بدنه حلقه While حتما باید از کنترل Sequence استفاده شود٬ در غیر این صورت امکان تعریف بدنه حلقه While وجود ندارد.