اشتراک‌ها
AutoMapper 5.0 Beta منتشر شد

The end result is a 10X performance boost in speed, but without sacrificing all of the runtime exception logic that makes AutoMapper so useful. 

AutoMapper 5.0 Beta منتشر شد
مطالب
Angular CLI - قسمت پنجم - ساخت و توزیع برنامه
ساخت و توزیع برنامه‌های Angular یکی از مهم‌ترین و بحث برانگیزترین قسمت‌های نگارش‌های جدید آن است و به ازای هر پروژه و قالبی که برای آن توسط گروه‌های مختلف ارائه شده‌است، روش‌های متفاوتی را شاهد خواهید بود. در ادامه روش توصیه شده‌ی توسط تیم Angular را که مبتنی است بر webpack و به صورت خودکار توسط Angular CLI مدیریت می‌شود، بررسی خواهیم کرد.


ساخت (Build) برنامه‌های Angular

Angular CLI کار ساخت و کامپایل برنامه را به صورت خودکار انجام داده و خروجی را در مسیری مشخص درج می‌کند. در اینجا می‌توان گزینه‌هایی را بر اساس نوع کامپایل مدنظر مانند کامپایل برای حالت توسعه و یا کامپایل برای حالت توزیع نهایی، انتخاب کرد. همچنین مباحث bundling و یکی کردن تعداد بالای ماژول‌های برنامه در آن لحاظ می‌شوند تا برنامه در حالت توزیع نهایی، سبب 100ها رفت و برگشت به سرور برای دریافت ماژول‌های مختلف آن نشود. به علاوه مباحث uglification (به نوعی obfuscation کدهای جاوا اسکریپتی نهایی) و tree-shaking (حذف کدهایی که در برنامه استفاده نشده‌اند؛ یا کدهای مرده) نیز پیاده سازی می‌شوند. با انجام tree-shaking‌، نه تنها اندازه‌ی توزیع نهایی به کاربر کاهش پیدا می‌کند، بلکه مرورگر نیز حجم کمتری از کدهای جاوااسکریپتی را باید تفسیر کند.
برای شروع می‌توان از دستور ذیل برای مشاهده‌ی تمام گزینه‌های مهیای ساخت برنامه استفاده کرد:
> ng build --help
ذکر تنهای دستور ng build‌، بدون هیچ گزینه‌ای، برای حالت «توسعه‌ی» برنامه بسیار ایده‌آل است (و دقیقا به معنای صدور دستور ng build --dev است). در این حالت خروجی کامپایل شده‌ی برنامه در پوشه‌ی dist تولید می‌شود. اگر از قسمت دوم این سری به خاطر داشته باشید، نام این پوشه‌ی خروجی، جزئی از تنظیمات فایل angular-cli.json. است:
"apps": [
{
   "outDir": "dist",
زمانیکه دستور ng build‌  صادر شود، این فایل‌ها را در پوشه‌ی dist خواهید یافت:

فایل 
توضیح 
 inline.bundle.js   WebPack runtime
از آن برای بارگذاری ماژول‌های برنامه و چسباندن قسمت‌های مختلف به یکدیگر استفاده می‌شود. 
 main.bundle.js   شامل تمام کدهای ما است. 
 polyfills.bundle.js   Polyfills - جهت پشتیبانی از مرورگرهای مختلف.
 styles.bundle.js    شامل بسته بندی تمام شیوه نامه‌های برنامه است 
vendor.bundle.js  کدهای کتابخانه‌های ثالث مورد استفاده و همچنین خود Angular، در اینجا بسته بندی می‌شوند. 
 

روشی برای بررسی محتوای bundleهای تولید شده

تولید bundleها در جهت کاهش رفت و برگشت‌های به سرور و بالا بردن کارآیی برنامه ضروری هستند؛ اما دقیقا این بسته بندی‌ها شامل چه اطلاعاتی می‌شوند؟ این اطلاعات را می‌توان از فایل‌های source map تولیدی استخراج کرد و برای این منظور می‌توان از برنامه‌ی source-map-explorer استفاده کرد.

روش نصب عمومی آن:
 > npm install -g source-map-explorer
روش اجرا:
 > source-map-explorer dist/main.bundle.js
پس از آن یک گزارش HTML ایی از محتوای bundle مدنظر تولید می‌شود.


یک مثال: ساخت برنامه‌ی مثال قسمت چهارم - تنظیمات مسیریابی در حالت dev

در ادامه، کار Build همان مثالی را که در قسمت قبل توضیح داده شد، بررسی می‌کنیم. برای این منظور از طریق خط فرمان به ریشه‌ی پوشه‌ی اصلی پروژه وارد شده و دستور ng build را صادر کنید. یک چنین خروجی را مشاهده خواهید کرد:
 D:\Prog\angular-routing>ng build
Hash: 123cae8bd8e571f44c31
Time: 33862ms
chunk {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 158 kB {4} [initial] [rendered]
chunk {1} main.bundle.js, main.bundle.js.map (main) 14.7 kB {3} [initial] [rendered]
chunk {2} styles.bundle.js, styles.bundle.js.map (styles) 9.77 kB {4} [initial] [rendered]
chunk {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.34 MB [initial] [rendered]
chunk {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]
و اگر فایل index.html تولیدی آن‌را بررسی کنید، تنها الحاق همین 4 فایل js تولیدی را مشاهده می‌نمائید:
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>AngularRouting</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
  <app-root>Loading...</app-root>
<script type="text/javascript" src="inline.bundle.js">
</script><script type="text/javascript" src="polyfills.bundle.js">
</script><script type="text/javascript" src="styles.bundle.js">
</script><script type="text/javascript" src="vendor.bundle.js">
</script><script type="text/javascript" src="main.bundle.js"></script>
</body>
</html>

یک نکته: زمانیکه دستور ng serve -o صادر می‌شود، در پشت صحنه دقیقا همین دستور ng build صادر شده و اطلاعات را درون حافظه تشکیل می‌دهد. اما اگر کار ng build را دستی انجام دهیم، اینبار ng serve -o اطلاعات را از پوشه‌ی dist دریافت می‌کند. بنابراین در حین کار با ng serve -o نیازی به build دستی پروژه نیست.

سؤال: چرا حجم فایل endor.bundle.js اینقدر بالا است و شامل چه اجزایی می‌شود؟
نکته‌ای که در اینجا وجود دارد، حجم بالای فایل vendor.bundle.js آن است که 2.34 MB می‌باشد:


چون دستور ng build بدون پارامتری ذکر شده‌است، برنامه را برای حالت توسعه Build می‌کند و به همین جهت هیچگونه بهینه سازی در این مرحله صورت نخواهد گرفت. برای بررسی محتوای این فایل می‌توان دستور ذیل را در ریشه‌ی اصلی پروژه صادر کرد:
 > source-map-explorer dist/vendor.bundle.js
پس از اجرای این دستور، بلافاصله مرورگر پیش فرض سیستم اجرا شده و گزارشی را ارائه می‌دهد.


همانطور که مشاهده می‌کنید، در حالت بهینه سازی نشده و Build برای توسعه، کامپایلر Angular حدود 41 درصد حجم فایل vendor.bundle.js را تشکیل می‌دهد. به علاوه ماژول‌ها و قسمت‌هایی را ملاحظه می‌کنید که اساسا برنامه‌ی فعلی مثال ما از آن‌ها استفاده نمی‌کند؛ مانند http، فرم‌ها و غیره.


سفارشی سازی Build برای محیط‌های مختلف

اگر به پروژه‌ی تولید شده‌ی توسط Angular CLI دقت کنید، حاوی پوشه‌ای است به نام src\environments


هدف از فایل‌های environment برای نمونه تغییر آدرس توزیع برنامه در حالت توسعه و ارائه نهایی است.
همچنین در اینجا می‌توان نحوه‌ی بهینه سازی فایل‌های تولیدی را توسط Build Targets مشخص کرد و اینکار توسط ذکر پرچم prod-- (مخفف production) صورت می‌گیرد.
در ادامه، تفاوت‌های دستورهای ng build و ng build --prod را ملاحظه می‌کنید:
- با اجرای ng build، از فایل environment.ts استفاده می‌شود؛ برخلاف حالت اجرای ng build --prod که از فایل environment.prod.ts استفاده می‌کند.
- Cache-busting در حالت ارائه‌ی نهایی، به تمام اجزای پروژه اعمال می‌شود؛ اما در حالت توسعه فقط برای تصاویر قید شده‌ی در فایل‌های css.
- فایل‌های source map فقط برای حالت توسعه تولید می‌شوند.
- در حالت توسعه، cssها داخل فایل‌های js تولیدی قرار می‌گیرند؛ اما در حالت ارائه‌ی نهایی به صورت فایل‌های css بسته بندی می‌شوند.
- در حالت توسعه برخلاف حالت ارائه‌ی نهایی، کار uglification انجام نمی‌شود.
- در حالت توسعه برخلاف حالت ارائه‌ی نهایی، کار tree-shaking یا حذف کدهای مرده و بدون ارجاع، انجام نمی‌شود.
- در حالت توسعه برخلاف حالت ارائه‌ی نهایی، کار AOT انجام نمی‌شود. در اینجا AOT به معنای Ahead of time compilation است.
- در هر دو حالت توسعه و ارائه‌ی نهایی کار bundling و دسته بندی فایل‌ها انجام خواهد شد.

به همین جهت است که ng build سریع است؛ اما حجم بالاتری را هم تولید می‌کند. چون بسیاری از بهینه سازی‌های حالت ارائه‌ی نهایی را به همراه ندارد.


دستورات build برای حالت توسعه و ارائه‌ی نهایی

برای حالت توسعه، هر 4 دستور ذیل یک مفهوم را دارند و به همین جهت مورد ng build متداول‌تر است:
>ng build --target=development --environment=dev
>ng build --dev -e=dev
>ng build --dev
>ng build

برای حالت ارائه‌ی نهایی، هر 3 دستور ذیل یک مفهوم را دارند و به همین جهت مورد ng build --prod متداول‌تر است:
>ng build --target=production --environment=prod
>ng build --prod -e=prod
>ng build --prod

همچنین هر کدام از این دستورات را توسط پرچم‌های ذیل نیز می‌توان سفارشی سازی کرد:

 پرچم  مخفف  توضیح
 sourcemap--  sm-  تولید سورس‌مپ
aot--    Ahead of Time compilation 
watch--  w-  تحت نظر قرار دادن فایل‌ها و ساخت مجدد
environment--  e-  محیط ساخت
 target--  t-  نوع ساخت
 dev--    مخفف نوع ساخت جهت توسعه
 prod--     مخفف نوع ساخت جهت ارائه نهایی

برای مثال در حالت prod، سورس‌مپ‌ها تولید نخواهند شد. اگر علاقمندید تا این فایل‌ها نیز تولید شوند، پرچم souremap را نیز ذکر کنید.
و یا اگر برای حالت dev می‌خواهید AOT را فعالسازی کنید، پرچم aot-- را در آنجا قید کنید.


یک مثال: ساخت برنامه‌ی مثال قسمت چهارم - تنظیمات مسیریابی در حالت prod

تا اینجا خروجی حالت dev ساخت برنامه‌ی قسمت چهارم را بررسی کردیم. در ادامه دستور ng build --prod را در ریشه‌ی پروژه صادر می‌کنیم:
 D:\Prog\angular-routing>ng build --prod
Hash: f5bd7fd555a85af8a86f
Time: 39932ms
chunk {0} polyfills.18173234f9641113b9fe.bundle.js (polyfills) 158 kB {4} [initial] [rendered]
chunk {1} main.c6958def7c5f51c45261.bundle.js (main) 50.3 kB {3} [initial] [rendered]
chunk {2} styles.d41d8cd98f00b204e980.bundle.css (styles) 69 bytes {4} [initial] [rendered]
chunk {3} vendor.b426ba6883193375121e.bundle.js (vendor) 1.37 MB [initial] [rendered]
chunk {4} inline.8cec210370dd3af5f1a0.bundle.js (inline) 0 bytes [entry] [rendered]


همانطور که ملاحظه می‌کنید، اینبار نه تنها حجم فایل‌ها به میزان قابل ملاحظه‌ای کاهش پیدا کرده‌اند، بلکه این نام‌ها به همراه یک سری hash هم هستند که کار cache-busting (منقضی کردن کش مرورگر، با ارائه‌ی نگارشی جدید) را انجام می‌دهند.

در ادامه اگر بخواهیم مجددا برنامه‌ی source-map-explorer را جهت بررسی محتوای فایل‌های js اجرا کنیم، به خطای عدم وجود sourcemapها خواهیم رسید (چون در حالت prod، به صورت پیش فرض غیرفعال هستند). به همین‌جهت برای این مقصود خاص نیاز است از پرچم فعالسازی موقت آن استفاده کرد:
> ng build --prod --sourcemap
> source-map-explorer dist/vendor.b426ba6883193375121e.bundle.js


همانطور که در تصویر نیز مشخص است، اینبار کامپایلر Angular به همراه تمام ماژول‌هایی که در برنامه ارجاعی به آن‌ها وجود نداشته‌است، حذف شده‌اند و کل حجم بسته‌ی Angular به 366 KB کاهش یافته‌است.


بررسی دستور ng serve

تا اینجا برای اجرای برنامه در حالت dev از دستور ng serve -o استفاده کرده‌ایم. کار ارائه‌ی برنامه توسط این دستور، از محتوای کامپایل شده‌ی درون حافظه با مدیریت webpack انجام می‌شود. به همین جهت بسیار سریع بوده و قابلیت live reload را ارائه می‌دهد (نمایش آنی تغییرات در مرورگر، با تغییر فایل‌ها).
همانند تمام دستورات دیگر، اطلاعات بیشتری را در مورد این دستور، از طریق راهنمای آن می‌توان به دست آورد:
 > ng serve --help

که شامل این موارد هستند (علاوه بر تمام مواردی را که در حالت ng build می‌توان مشخص کرد؛ مثلا ng serve --prod -o):

 پرچم مخفف
توضیح
 open-- o-
بازکردن خودکار مرورگر پیش فرض.
حالت پیش فرض آن گشودن مرورگر توسط خودتان است و سپس مراجعه‌ی دستی به آدرس برنامه. 
 port--  p-  تغییر پورت پیش فرض مانند ng server -p 8626 
 live-reload--  lr-   فعال است مگر اینکه آن‌را با false مقدار دهی کنید.
 ssl--    ارائه به صورت HTTPS
 proxy-config--  pc-  Proxy configuration file 


استخراج فایل تنظیمات webpack از Angular CLI

Angular CLI برای مدیریت build، در پشت صحنه از webpack استفاده می‌کند. فایل تنظیمات آن نیز جزئی از فایل‌های توکار این ابزار است و قرار نیست به صورت پیش فرض و مستقیم توسط پروژه‌ی جاری ویرایش شود. به همین جهت آن‌را در ساختار پروژه‌ی تولید شده، مشاهده نمی‌کنید.
اگر علاقمند به سفارشی سازی بیشتر این تنظیمات پیش فرض باشید، ابتدا باید آن‌را اصطلاحا eject کنید و سپس می‌توان آن‌را ویرایش کرد:
 > ng eject
Ejection was successful.

To run your builds, you now need to do the following commands:
- "npm run build" to build.
- "npm run test" to run unit tests.
- "npm start" to serve the app using webpack-dev-server.
- "npm run e2e" to run protractor.

Running the equivalent CLI commands will result in an error.
============================================
Some packages were added. Please run "npm install".
همانطور که مشاهده می‌کنید عنوان کرده‌است که از این پس خودتان باید بسیاری از مسایل را به صورت دستی مدیریت کنید و Angular CLI دیگر آن‌ها را به صورت خودکار مدیریت نمی‌کند و دیگر دستورات ng build و ng serve کار نخواهند کرد (این تغییرات در فایل package.json درج می‌شوند).
در این حالت است که فایل webpack.config.js به ریشه‌ی پروژه جهت سفارشی سازی شما اضافه خواهد شد. همچنین فایل‌های .angular-cli.json، package.json نیز جهت درج این تغییرات ویرایش می‌شوند.

و اگر در این لحظه پشیمان شده‌اید (!) فقط کافی است تا این مرحله‌ی جدید commit شده‌ی به مخزن کد را لغو کنید و باز هم به همان Angular CLI قبلی می‌رسید.
مطالب
طراحی یک گرید با Angular و ASP.NET Core - قسمت اول - پیاده سازی سمت سرور
یکی از نیازهای مهم هر برنامه‌ای، امکانات گزارشگیری و نمایش لیستی از اطلاعات است. به همین جهت در طی چند قسمت، قصد داریم یک گرید ساده را به همراه امکانات نمایش، صفحه بندی و مرتب سازی اطلاعات، تنها به کمک امکانات توکار Angular و ASP.NET Core تهیه کنیم.




تهیه مقدمات سمت سرور

مدلی که در تصویر فوق نمایش داده شده‌است، در سمت سرور چنین ساختاری را دارد:
namespace AngularTemplateDrivenFormsLab.Models
{
    public class Product
    {
        public int ProductId { set; get; }
        public string ProductName { set; get; }
        public decimal Price { set; get; }
        public bool IsAvailable { set; get; }
    }
}

همچنین یک منبع ساده درون حافظه‌ای را نیز جهت بازگشت 1500 محصول تهیه کرده‌ایم. علت اینجا است که ساختار نهایی اطلاعات آن شبیه به ساختار اطلاعات حاصل از ORMها باشد و همچنین به سادگی قابلیت اجرا و بررسی را داشته باشد:
using System.Collections.Generic;

namespace AngularTemplateDrivenFormsLab.Models
{
    public static class ProductDataSource
    {
        private static readonly IList<Product> _cachedItems;
        static ProductDataSource()
        {
            _cachedItems = createProductsDataSource();
        }

        public static IList<Product> LatestProducts
        {
            get { return _cachedItems; }
        }

        private static IList<Product> createProductsDataSource()
        {
            var list = new List<Product>();
            for (var i = 0; i < 1500; i++)
            {
                list.Add(new Product
                {
                    ProductId = i + 1,
                    ProductName = "نام " + (i + 1),
                    IsAvailable = (i % 2 == 0),
                    Price = 1000 + i
                });
            }
            return list;
        }
    }
}


مشخص کردن قرارداد اطلاعات دریافتی از سمت کلاینت

زمانیکه کلاینت Angular برنامه، اطلاعاتی را به سمت سرور ارسال می‌کند، یک چنین ساختاری را دریافت خواهیم کرد:
 http://localhost:5000/api/Product/GetPagedProducts?sortBy=productId&isAscending=true&page=2&pageSize=7
درخواست، به یک اکشن متد مشخص ارسال شده‌است و حاوی یک سری کوئری استرینگ مشخص کننده‌ی نام خاصیت یا فیلدی که قرار است مرتب سازی بر اساس آن صورت گیرد، صعودی و نزولی بودن این مرتب سازی، شماره صفحه‌ی درخواستی و تعداد آیتم‌های در هر صفحه، می‌باشد.
بنابراین اینترفیسی را دقیقا بر اساس نام کلیدهای همین کوئری استرینگ‌ها تهیه می‌کنیم:
    public interface IPagedQueryModel
    {
        string SortBy { get; set; }
        bool IsAscending { get; set; }
        int Page { get; set; }
        int PageSize { get; set; }
    }


کاهش کدهای تکراری صفحه بندی اطلاعات در سمت سرور

با تعریف این اینترفیس چند هدف را دنبال خواهیم کرد:
الف) استاندارد سازی نام خواصی که مدنظر هستند و اعمال یک دست آن‌ها به ViewModelهایی که قرار است از سمت کلاینت دریافت شوند:
    public class ProductQueryViewModel : IPagedQueryModel
    {
        // ... other properties ...

        public string SortBy { get; set; }
        public bool IsAscending { get; set; }
        public int Page { get; set; }
        public int PageSize { get; set; }
    }
برای مثال در اینجا یک ViewModel مخصوص Product را ایجاد کرده‌ایم که می‌تواند شامل یک سری فیلد دیگر نیز باشد. اما یک سری خواص مرتب سازی و صفحه بندی آن، یک دست و مشخص هستند.

ب) امکان استفاده‌ی از این قرارداد در متدهای کمکی که نوشته خواهند شد:
    public static class IQueryableExtensions
    {
        public static IQueryable<T> ApplyPaging<T>(
          this IQueryable<T> query, IPagedQueryModel model)
        {
            if (model.Page <= 0)
            {
                model.Page = 1;
            }

            if (model.PageSize <= 0)
            {
                model.PageSize = 10;
            }

            return query.Skip((model.Page - 1) * model.PageSize).Take(model.PageSize);
        }
    }
در حین ارائه‌ی اطلاعات نهایی صفحه بندی شده به کلاینت، همیشه یک قسمت Skip و Take وجود خواهند داشت. این متدها نیز باید بر اساس یک سری خاصیت و مقدار مشخص، مانند صفحه شماره صفحه‌ی جاری و تعداد ردیف‌های در هر صفحه کار کنند. اکنون که قرارداد IPagedQueryModel را تهیه کرده‌ایم و ViewModel ما نیز آن‌را پیاده سازی می‌کند، مطمئن خواهیم بود که می‌توان به سادگی به این خواص دسترسی یافت و همچنین این کد تکراری صفحه بندی را توانسته‌ایم به یک متد الحاقی کمکی منتقل و حجم کدهای نهایی را کاهش دهیم.
همچنین دراینجا بجای صدور استثناء در حین دریافت مقادیر غیرمعتبر شماره صفحه یا تعداد ردیف‌های هر صفحه، از حالت «بخشنده» بجای حالت «تدافعی» استفاده شده‌است. برای مثال در حالت «بخشنده» اگر شماره صفحه منفی بود، همان صفحه‌ی اول اطلاعات نمایش داده می‌شود؛ بجای صدور یک استثناء (یا حالت «تدافعی و defensive programming»).


کاهش کدهای تکراری مرتب سازی اطلاعات در سمت سرور

همانطور که عنوان شد، از سمت کلاینت، چنین لینکی را دریافت خواهیم کرد:
 http://localhost:5000/api/Product/GetPagedProducts?sortBy=productId&isAscending=true&page=2&pageSize=7
در اینجا، هربار sortBy و isAscending می‌توانند متفاوت باشند و در نهایت به یک چنین کدهایی خواهیم رسید:
if(model.SortBy == "f1")
{
   query = !model.IsAscending ? query.OrderByDescending(x => x.F1) : query.OrderBy(x => x.F1);
}
امکان نوشتن این نوع کوئری‌ها توسط قابلیت تعریف زنجیره‌وار کوئری‌های LINQ میسر است و در نهایت زمانیکه ToList نهایی فراخوانی می‌شود، آنگاه است که کوئری SQL معادل این‌ها تولید خواهد شد.
اما در این حالت نیاز است به ازای تک تک فیلدها، یکبار if/else یافتن فیلد و سپس بررسی صعودی و نزولی بودن آن‌ها صورت گیرد که در نهایت ظاهر خوشایندی را نخواهند داشت.

یک نمونه از مزیت‌های تهیه‌ی قرارداد IPagedQueryModel را در حین نوشتن متد ApplyPaging مشاهده کردید. نمونه‌ی دیگر آن کاهش کدهای تکراری مرتب سازی اطلاعات است:
namespace AngularTemplateDrivenFormsLab.Utils
{
    public static class IQueryableExtensions
    {
        public static IQueryable<T> ApplyOrdering<T>(
          this IQueryable<T> query,
          IPagedQueryModel model,
          IDictionary<string, Expression<Func<T, object>>> columnsMap)
        {
            if (string.IsNullOrWhiteSpace(model.SortBy) || !columnsMap.ContainsKey(model.SortBy))
            {
                return query;
            }

            if (model.IsAscending)
            {
                return query.OrderBy(columnsMap[model.SortBy]);
            }
            else
            {
                return query.OrderByDescending(columnsMap[model.SortBy]);
            }
        }
    }
}
در اینجا متد الحاقی ApplyOrdering، کار دریافت و بررسی خواص مدنظر را از طریق یک دیکشنری انجام می‌دهد و مابقی کدهای تکراری نوشته شده، حذف خواهند شد. برای نمونه، مثالی از نحوه‌ی استفاده‌ی از این متد الحاقی را در ذیل مشاهده می‌کنید:
var columnsMap = new Dictionary<string, Expression<Func<Product, object>>>()
            {
                ["productId"] = p => p.ProductId,
                ["productName"] = p => p.ProductName,
                ["isAvailable"] = p => p.IsAvailable,
                ["price"] = p => p.Price
            };
query = query.ApplyOrdering(queryModel, columnsMap);
ابتدا نگاشتی بین خواص رشته‌ای دریافتی از سمت کلاینت، با خواص شیء Product برقرار شده‌است. سپس این نگاشت به متد ApplyOrdering ارسال شده‌است. به این ترتیب از نوشتن تعداد زیادی if/else یا switch بر اساس خاصیت SortBy بی‌نیاز شده‌ایم، حجم کدهای نهایی تولیدی کاهش پیدا می‌کنند و برنامه نیز خواناتر می‌شود.


تهیه قرارداد ساختار اطلاعات بازگشتی از سمت سرور به سمت کلاینت

تا اینجا قرارداد اطلاعات دریافتی از سمت کلاینت را مشخص کردیم. همچنین از آن برای ساده سازی عملیات مرتب سازی و صفحه بندی اطلاعات کمک گرفتیم. در ادامه نیاز است مشخص کنیم چگونه می‌خواهیم این اطلاعات را به سمت کلاینت ارسال کنیم:
using System.Collections.Generic;

namespace AngularTemplateDrivenFormsLab.Models
{
    public class PagedQueryResult<T>
    {
        public int TotalItems { get; set; }
        public IEnumerable<T> Items { get; set; }
    }
}
عموما ساختار اطلاعات صفحه بندی شده، شامل تعداد کل آیتم‌های تمام صفحات (خاصیت TotalItems) و تنها اطلاعات ردیف‌های صفحه‌ی جاری درخواستی (خاصیت Items) است و چون در اینجا این Items از هر نوعی می‌تواند باشد، بهتر است آن‌را جنریک تعریف کنیم.


پایان کار بازگشت اطلاعات سمت سرور با تهیه اکشن متد GetPagedProducts

در اینجا اکشن متدی را مشاهده می‌کنید که اطلاعات نهایی مرتب سازی شده و صفحه بندی شده را بازگشت می‌دهد:
    [Route("api/[controller]")]
    public class ProductController : Controller
    {
        [HttpGet("[action]")]
        public PagedQueryResult<Product> GetPagedProducts(ProductQueryViewModel queryModel)
        {
            var pagedResult = new PagedQueryResult<Product>();

            var query = ProductDataSource.LatestProducts
                                         .AsQueryable();

            //TODO: Apply Filtering ... .where(p => p....) ...

            var columnsMap = new Dictionary<string, Expression<Func<Product, object>>>()
            {
                ["productId"] = p => p.ProductId,
                ["productName"] = p => p.ProductName,
                ["isAvailable"] = p => p.IsAvailable,
                ["price"] = p => p.Price
            };
            query = query.ApplyOrdering(queryModel, columnsMap);

            pagedResult.TotalItems = query.Count();
            query = query.ApplyPaging(queryModel);
            pagedResult.Items = query.ToList();
            return pagedResult;
        }
    }
توضیحات تکمیلی

امضای این اکشن متد، شامل دو مورد مهم است:
 public PagedQueryResult<Product> GetPagedProducts(ProductQueryViewModel queryModel)
الف) ViewModel ایی که پیاده سازی کننده‌ی IPagedQueryModel است. به این ترتیب می‌توان به ساختار استانداردی از مقادیر مورد نیاز برای صفحه بندی و مرتب سازی رسید و همچنین این ViewModel می‌تواند حاوی خواص اضافی ویژه‌ی خود نیز باشد.
ب) خروجی آن از نوع PagedQueryResult است که در مورد آن توضیح داده شد. بنابراین باید به همراه تعداد کل رکوردهای جدول محصولات و همچنین تنها آیتم‌های صفحه‌ی جاری درخواستی باشد.

در ابتدای کار، دسترسی به منبع داده‌ی درون حافظه‌ای ابتدای برنامه را مشاهده می‌کنید. برای اینکه کارکرد آن‌را شبیه به کوئری‌های ORMها کنیم، یک AsQueryable نیز به انتهای آن اضافه شده‌است.
 var query = ProductDataSource.LatestProducts
  .AsQueryable();

//TODO: Apply Filtering ... .where(p => p....) ...
اینجا دقیقا جائی است که در صورت نیاز می‌توان کار فیلتر اطلاعات و اعمال متد where را انجام داد.

پس از مشخص شدن منبع داده و فیلتر آن در صورت نیاز، اکنون نوبت به مرتب سازی اطلاعات است:
var columnsMap = new Dictionary<string, Expression<Func<Product, object>>>()
            {
                ["productId"] = p => p.ProductId,
                ["productName"] = p => p.ProductName,
                ["isAvailable"] = p => p.IsAvailable,
                ["price"] = p => p.Price
            };
query = query.ApplyOrdering(queryModel, columnsMap);
توضیحات این مورد را پیشتر مطالعه کردید و هدف از آن، تهیه یک نگاشت ساده‌ی بین خواص رشته‌ای رسیده‌ی از سمت کلاینت به خواص مدل متناظر با آن است و سپس ارسال آن‌ها به همراه queryModel دریافتی از کاربر، برای اعمال مرتب سازی نهایی.

در آخر مطابق ساختار PagedQueryResult بازگشتی، ابتدا تعداد کل آیتم‌های منبع داده محاسبه شده‌است و سپس صفحه بندی به آن اعمال گردیده‌است. این ترتیب نیز مهم است و گرنه TotalItems دقیقا به همان تعداد ردیف‌های صفحه‌ی جاری محاسبه می‌شود:
var pagedResult = new PagedQueryResult<Product>();
pagedResult.TotalItems = query.Count();
query = query.ApplyPaging(queryModel);
pagedResult.Items = query.ToList();
return pagedResult;


در قسمت بعد، نحوه‌ی نمایش این اطلاعات را در سمت Angular بررسی خواهیم کرد.


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

- Exploring MoreLINQ Part 8 - FallbackIfEmpty
- Exploring MoreLINQ Part 7 - Batching
- Exploring MoreLINQ Part 6 - Taking
- Exploring MoreLINQ Part 5 - Scan
- Exploring MoreLINQ Part 4 - Combinations
- Exploring MoreLINQ Part 3 - MinBy and MaxBy
- Exploring MoreLINQ Part 2 - Counting Elements
- Exploring MoreLINQ Part 1 - Zipping 

سری بررسی کتابخانه‌ی MoreLINQ
مطالب
Roslyn #2
شروع به کار با Roslyn

Roslyn از زمان ارائه‌ی نگارش Visual Studio 14 CTP3 با ویژوال استودیو یکپارچه شد. بنابراین اگر از نگارش نهایی آن یعنی Visual Studio 2015 استفاده می‌کنید، اولین پیشنیاز کار با آن را در اختیار دارید. البته نگارش پیش نمایش آن نیز برای VS 2013 ارائه شد که در این تاریخ منسوخ شده درنظر گرفته می‌شود و دیگر به روز رسانی نخواهد شد. بنابراین نیاز است حتما Visual Studio 2015 را نصب کنید.
در حین نصب Visual Studio 2015 نیز باید گزینه‌ی نصب SDK آن‌را انتخاب کرده باشید، تا امکان تولید فایل‌های VSIX مرتبط را پیدا کنید. از این جهت که قالب‌های پروژه‌ی Roslyn، امکان تولید افزونه‌های آنالیز کدها را نیز میسر می‌کنند.
علاوه بر این‌ها نیاز است Syntax Visualizer و قالب‌های پروژه‌ی مخصوص Roslyn را نیز جداگانه نصب کنید. برای این منظور به آدرس ذیل مراجعه کرده و افزونه‌ی آن‌را نصب کنید.
NET Compiler Platform SDK.

پس از نصب این افزونه، دو قابلیت جدید به Visual studio اضافه می‌شوند:
الف) در منوی view، ذیل گزینه‌ی other windows، گزینه‌ی جدیدی به نام Syntax visualizer اضافه شده‌است.
ب) ذیل گزینه‌ی extensibility پنجره‌ی new project، تعدادی قالب پروژه‌ی جدید مخصوص Roslyn نصب شده‌اند.


البته باید دقت داشت که این قالب‌های جدید برای نمایش در این لیست، حداقل نیاز به انتخاب دات نت 4.5.2 را دارند.

از Syntax visualizer جهت مشاهده‌ی ریز جزئیات ساختار دستوری کدهای جاری استفاده می‌شود:


زمانیکه گزینه‌ی View->Other windows->Syntax visualizer را اجرا کردید، اندکی صبر کنید تا بارگذاری شود و سپس اشاره‌گر ویرایشگر را به قسمتی دلخواه حرکت دهید. در این حالت می‌توان ساختار کدها را بر اساس تفسیر Roslyn مشاهده کرد. همچنین اگر در Syntax visualizer یک نود را انتخاب کنید، بلافاصله معادل آن در ادیتور ویژوال استودیو انتخاب می‌شود. از این ابزار جهت تولید آنالایزرهای مبتنی بر Roslyn زیاد استفاده می‌شود.


تغییرات خط فرمان Visual Studio 2015 نسبت به نگارش‌های پیشین آن

خط فرمان Visual Studio 2015 به همراه کامپایلر قدیمی خط فرمان C# 5 و همچنین کامپایلر جدید مبتنی بر Roslyn مخصوص C# 6 است. این کامپایلرها را در مسیرهای ذیل می‌توانید پیدا کنید:
کامپایلر جدید مبتنی بر Roslyn در مسیر C:\Program Files (x86)\MSBuild\14.0\Bin قرار دارد و کامپایلر قدیمی C# 5 در مسیر نصب دات نت فریم ورک یعنی C:\Windows\Microsoft.NET\Framework\v4.0.30319 قرار گرفته‌است.


همانطور که مشاهده می‌کنید، نگارش کامپایلر مبتنی بر Roslyn، یک است و شماره build آن، بیانگر تاریخ کامپایل آن است:
50618=2015/06/18

اکنون شاید این سؤال مطرح شود که کامپایلر پیش فرض کدام است؟ برای یافتن آن، به منوی استارت ویندوز مراجعه کرده و گزینه‌ی developer command prompt for vs 2015 را اجرا کنید. در اینجا اگر دستور csc را اجرا کنید، خروجی آن همان کامپایلر مبتنی بر Roslyn است:


در همینجا اگر سوئیچ ? را برای مشاهده‌ی راهنمای کامپایلر خط فرمان Roslyn صادر کنید، یکی از گزینه‌‌های جدید آن، سوئیچ analyzer است:
 C:\Program Files (x86)\Microsoft Visual Studio 14.0>csc /?
Microsoft (R) Visual C# Compiler version 1.0.0.50618
Copyright (C) Microsoft Corporation. All rights reserved.


Visual C# Compiler Options

 /analyzer:<file list> Run the analyzers from this assembly
(Short form: /a)
آنالایزرها اسمبلی‌ها و افزونه‌هایی هستند که قابلیت وارد شدن به pipeline کامپایلر را داشته و امکان دخالت در صدور خطاها و اخطارهای صادر شده‌ی توسط آن‌را دارند. به عبارتی این کامپایلر خط فرمان جدید، افزونه پذیر است.
همچنین این کامپایلر نسبت به نگارش قبلی آن، دارای سوئیچ و گزینه‌ی parallel نیز می‌باشد که به کمک ساختارهای داده‌ی immutable جدید دات نت مسیر شده‌است.
نظرات مطالب
متدی برای بررسی صحت کد ملی وارد شده
اینم برای تشخیص صحت کد ملی اشخاص حقوقی با استفاده از جاوا اسکریپت
function checkCodeMeli(code)
{
  
  var L=code.length;
  
  if(L<11 || parseInt(code,10)==0) return false;
  
  if(parseInt(code.substr(3,6),10)==0) return false;
  var c=parseInt(code.substr(10,1),10);
  var d=parseInt(code.substr(9,1),10)+2;
  var z=new Array(29,27,23,19,17);
  var s=0;
  for(var i=0;i<10;i++)
    s+=(d+parseInt(code.substr(i,1),10))*z[i%5];
  s=s%11;if(s==10) s=0;
  return (c==s);

}



اشتراک‌ها
مقایسه و تفاوت های Stress Test ، Load Test با Performance Test
  • Performance testing is a testing method used to determine the speed of a computer, network or devices.
  • Load testing simulates real-world load on any application or website.
  • Stress testing determines the stability and robustness of the system
  • Performance testing helps to check the performance of website servers, databases, networks.
  • Load testing is used for the Client/Server, Web-based applications.
  • Stress testing is done unexpected test traffic of your website. 
مقایسه و تفاوت های Stress Test ، Load Test با Performance Test
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 22 - توزیع برنامه توسط IIS
روش کار برنامه‌های ASP.NET Core در IIS کاملا متفاوت است با تمام نگارش‌های پیشین ASP.NET؛ از این جهت که برنامه‌های ASP.NET Core در اصل یک برنامه‌ی متکی به خود از نوع Console می‌باشند. به همین جهت برای هاست شدن نیازی به IIS ندارند. این نوع برنامه‌ها به همراه یک self-hosted Web server ارائه می‌شوند (به نام Kestrel) و این وب سرور توکار است که تمام درخواست‌های رسیده را دریافت و پردازش می‌کند. هرچند در اینجا می‌توان از IIS صرفا به عنوان یک «front end proxy» استفاده کرد؛ از این جهت که Kestrel تنها یک وب سرور خام است و تمام امکانات و افزونه‌های مختلف IIS را شامل نمی‌شود.
بر روی ماشین‌های ویندوزی و ویندوزهای سرور، استفاده‌ی از IIS به عنوان پروکسی درخواست‌ها و ارسال آن‌ها به Kestrel، روش توصیه شده‌است؛ از این جهت که حداقل قابلیت‌هایی مانند «port 80/443 forwarding»، مدیریت طول عمر برنامه، مدیریت مجوزهای SLL آن و خیلی از موارد دیگر توسط Kestrel پشتیبانی نمی‌شود.


معماری پردازش نگارش‌های پیشین ASP.NET در IIS


در نگارش‌های پیشین ASP.NET، همه چیز داخل پروسه‌‌ای به نام w3wp.exe و یا IIS Worker Process پردازش می‌شود که در اصل چیزی نیست بجز همان IIS Application Pool. این AppPoolها، برنامه‌های ASP.NET شما را هاست می‌کنند و همچنین سبب وهله سازی و اجرای آن‌ها نیز خواهند شد.
در اینجا درایور http.sys ویندوز، درخواست‌های رسیده را دریافت کرده و سپس آن‌ها را به سمت سایت‌هایی نگاشت شده‌ی به AppPoolهای مشخص، هدایت می‌کند.


معماری پردازش برنامه‌های ASP.NET Core در IIS

روش اجرای برنامه‌های ASP.NET Core با نگارش‌های پیشین آن‌ها کاملا متفاوت هستند؛ از این جهت که داخل پروسه‌ی w3wp.exe اجرا نمی‌شوند. این برنامه‌ها در یک پروسه‌ی مجزای کنسول خارج از پروسه‌ی w3wp.exe اجرا می‌شوند و حاوی وب سرور توکاری به نام کسترل (Kestrel) هستند.


 این وب سرور، وب سروری است تماما دات نتی و به شدت برای پردازش تعداد بالای درخواست‌ها بهینه سازی شده‌است؛ تا جایی که کارآیی آن در این یک مورد چند 10 برابر IIS است. هرچند این وب سرور فوق العاده سریع است، اما «تنها» یک وب سرور خام است و به همراه سرویس‌های مدیریت وب، مانند IIS نیست.


در تصویر فوق مفهوم «پروکسی» بودن IIS را در حین پردازش برنامه‌های ASP.NET Core بهتر می‌توان درک کرد. ابتدا درخواست‌های رسیده به IIS می‌رسند و سپس IIS آن‌ها را به طرف Kestrel هدایت می‌کند.
برنامه‌های ASP.NET Core، برنامه‌های کنسول متکی به خودی هستند که توسط دستور خط فرمان dotnet اجرا می‌شوند. این اجرا توسط ماژولی ویژه به نام AspNetCoreModule در IIS انجام می‌شود.


همانطور که در تصویر نیز مشخص است، AspNetCoreModule یک ماژول بومی IIS است و هنوز برای اجرا نیاز به IIS Application Pool دارد؛ با این تفاوت که در تنظیم AppPoolهای برنامه‌های ASP.NET Core، باید NET CLR Version. را به No managed code تنظیم کرد.


اینکار از این جهت صورت می‌گیرد که IIS در اینجا تنها نقش یک پروکسی هدایت درخواست‌ها را به پروسه‌ی برنامه‌ی حاوی وب سرور Kestrel، دارد و کار آن وهله سازی NET Runtime. نیست. کار AspNetCoreModule این است که با اولین درخواست رسیده‌ی به برنامه‌ی شما، آن‌را بارگذاری کند. سپس درخواست‌های رسیده را دریافت و به سمت برنامه‌ی ASP.NET Core شما هدایت می‌کند (به این عملیات reverse proxy هم می‌گویند).


اگر دقت کرده باشید، برنامه‌های ASP.NET Core، هنوز دارای فایل web.config ایی با محتوای ذیل هستند:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="aspNetCore" path="*" verb="*"
           modules="AspNetCoreModule" resourceType="Unspecified"/>
    </handlers>
    <aspNetCore processPath="%LAUNCHER_PATH%"
                arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false"
                stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false"/>
  </system.webServer>
</configuration>
توسط این تنظیمات است که AspNetCoreModule فایل‌های dll برنامه‌ی شما را یافته و سپس برنامه را به عنوان یک برنامه‌ی کنسول بارگذاری می‌کند (با توجه به اینکه حاوی کلاس Program و متد Main هستند).
یک نکته: در زمان publish برنامه، تنظیم و تبدیل مقادیر LAUNCHER_PATH و LAUNCHER_ARGS به معادل‌های اصلی آن‌ها صورت می‌گیرد (در ادامه مطلب بحث خواهد شد).


آیا واقعا هنوز نیازی به استفاده‌ی از IIS وجود دارد؟

هرچند می‌توان Kestrel را توسط یک IP و پورت مشخص، عمومی کرد و استفاده نمود، اما حداقل در ویندوز چنین توصیه‌ای نمی‌شود و بهتر است از IIS به عنوان یک front end proxy استفاده کرد؛ به این دلایل:
- اگر می‌خواهید چندین برنامه را بر روی یک وب سرور که از طریق پورت‌های 80 و 443 ارائه می‌شوند داشته باشید، نمی‌توانید از Kestrel  به صورت مستقیم استفاده کنید؛ زیرا از مفهوم host header routing که قابلیت ارائه‌ی چندین برنامه را از طریق پورت 80 و توسط یک IP میسر می‌کند، پشتیبانی نمی‌کند. برای اینکار نیاز به IIS و یا در حقیقت درایور http.sys ویندوز است.
- IIS خدمات قابل توجهی را به برنامه‌ی شما ارائه می‌کند. برای مثال با اولین درخواست رسیده، به صورت خودکار آن‌را اجرا و بارگذاری می‌کند؛ به همراه تمام مدیریت‌های پروسه‌ای که در اختیار برنامه‌های ASP.NET در طی سالیان سال قرار داشته‌است. برای مثال اگر پروسه‌ی برنامه‌ی شما در اثر استثنایی کرش کرد، دوباره با درخواست بعدی رسیده، حتما برنامه را بارگذاری و آماده‌ی خدمات دهی مجدد می‌کند.
- در اینجا می‌توان تنظیمات SSL را بر روی IIS انجام داد و سپس درخواست‌های معمولی را به Kestrel  ارسال کرد. به این ترتیب با یک مجوز می‌توان چندین برنامه‌ی Kestrel را مدیریت کرد.
- IISهای جدید به همراه ماژول‌های بومی بسیار بهینه و کم مصرفی برای مواردی مانند gzip compression of static content, static file caching, Url Rewriting هستند که با فعال سازی آن‌ها می‌توان از این قابلیت‌ها، در برنامه‌های ASP.NET Core نیز استفاده کرد.


نحوه‌ی توزیع برنامه‌های ASP.NET Core به IIS

روش اول: استفاده از دستور خط فرمان dotnet publish

برای این منظور به ریشه‌ی پروژه‌ی خود وارد شده و دستور dotnet publish را با توجه به پارامترهای ذیل اجرا کنید:
 dotnet publish --framework netcoreapp1.0 --output "c:\temp\mysite" --configuration Release
در اینجا برنامه کامپایل شده و همچنین مراحلی که در فایل project.json نیز ذکر شده‌اند، اعمال می‌شود. برای مثال در اینجا پوشه‌ها و فایل‌هایی که در قسمت include ذکر شده‌اند به خروجی کپی خواهند شد. همچین در قسمت scripts تمام مراحل ذکر شده مانند یکی کردن و فشرده سازی اسکریپت‌ها نیز انجام خواهد شد. قسمت postpublish تنها کاری را که انجام می‌دهد، ویرایش فایل web.config برنامه و تنظیم LAUNCHER_PATH و LAUNCHER_ARGS آن به مقادیر واقعی آن‌ها است.
{

    "publishOptions": {
        "include": [
            "wwwroot",
            "Features",
            "appsettings.json",
            "web.config"
        ]
    },
 
    "scripts": {
        "precompile": [
            "dotnet bundle"
        ],
        "prepublish": [
            //"bower install"
        ],
        "postpublish": [ "dotnet publish-iis --publish-folder %publish:OutputPath% --framework %publish:FullTargetFramework%" ]
    }
}
و در نهایت اگر به پوشه‌ی output ذکر شده‌ی در فرمان فوق مراجعه کنید، این خروجی نهایی است که باید به صورت دستی به وب سرور خود برای اجرا انتقال دهید که به همراه تمام DLLهای مورد نیاز برای برنامه نیز هست.


پس از انتقال این فایل‌ها به سرور، مابقی مراحل آن مانند قبل است. یک Application جدید تعریف شده و سپس ابتدا مسیر آن مشخص می‌شود و نکته‌ی اصلی، انتخاب AppPool ایی است که پیشتر شرح داده شد:


برنامه‌های ASP.NET Core باید به AppPool ایی تنظیم شوند که NET CLR Version. آن‌ها No Managed Code است. همچنین بهتر است به ازای هر برنامه‌ی جدید یک AppPool مجزا را ایجاد کنید تا کرش یک برنامه تاثیر منفی را بر روی برنامه‌ی دیگری نگذارد.

روش دوم: استفاده از ابزار Publish خود ویژوال استودیو

اگر علاقمند هستید که روش خط فرمان فوق را توسط ابزار publish ویژوال استودیو انجام دهید، بر روی پروژه در solution explorer کلیک راست کرده و گزینه‌ی publish را انتخاب کنید. در صفحه‌ای که باز می‌شود، بر روی گزینه‌ی custom کلیک کرده و نامی را وارد کنید. از این نام پروفایل، جهت ساده سازی مراحل publish، در دفعات آتی فراخوانی آن استفاده می‌شود.


در صفحه‌ی بعدی اگر گزینه‌ی file system را انتخاب کنید، دقیقا همان مراحل روش اول تکرار می‌شوند:


سپس می‌توانید فریم ورک برنامه و نوع ارائه را مشخص کنید:


و در آخر کار، Publish به این پوشه‌ی مشخص شده که به صورت پیش فرض در ذیل پوشه‌ی bin برنامه‌است، صورت می‌گیرد.


روش عیب یابی راه اندازی اولیه‌ی برنامه‌های ASP.NET Core

در اولین سعی در اجرای برنامه‌ی ASP.NET Core بر روی IIS به این خطا رسیدم:


در event viewer ویندوز چیزی ثبت نشده بود. اولین کاری را که در این موارد می‌توان انجام داد به این صورت است. از طریق خط فرمان به پوشه‌ی publish برنامه وارد شوید (همان پوشه‌ای که توسط IIS عمومی شده‌است). سپس دستور dotnet prog.dll را صادر کنید. در اینجا prog.dll نام dll اصلی برنامه یا همان نام پروژه است:


همانطور که مشاهده می‌کنید، برنامه به دنبال پوشه‌ی bower_components ایی می‌گردد که کار publish آن انجام نشده‌است (این پوشه در تنظیمات آغازین برنامه عمومی شده‌است و در لیست include قسمت publishOptions فایل project.json فراموش شده‌است).

روش دوم، فعال سازی stdoutLogEnabled موجود در فایل وب کانفیگ، به true است. در اینجا web.config نهایی تولیدی توسط عملیات publish را مشاهده می‌کنید که در آن پارامترهای  processPath و arguments مقدار دهی شده‌اند (همان قسمت postpublish فایل project.json). در اینجا مقدار stdoutLogEnabled به صورت پیش فرض false است. اگر true شود، همان خروجی تصویر فوق را در پوشه‌ی logs خواهید یافت:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <handlers>
      <add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModule" resourceType="Unspecified" />
    </handlers>
    <aspNetCore processPath="dotnet" arguments=".\Core1RtmEmptyTest.dll" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
  </system.webServer>
</configuration>
البته اگر کاربر منتسب به AppPool برنامه، دسترسی نوشتن در این پوشه را نداشته باشد، خطای ذیل را در event viewer ویندوز مشاهده خواهید کرد:
 Warning: Could not create stdoutLogFile \\?\D:\Prog\1395\Core1RtmEmptyTest\src\Core1RtmEmptyTest\bin\Release\PublishOutput\logs\stdout_10064_201672893654.log, ErrorCode = -2147024893.
همچنین پوشه‌ی logs را هم باید خودتان از پیش ایجاد کنید. به عبارتی کاربر منتسب به AppPool برنامه باید دسترسی نوشتن در پوشه‌ی logs واقع در ریشه‌ی برنامه را داشته باشد. این کاربر  IIS AppPool\DefaultAppPool نام دارد.



حداقل‌های یک هاست ویندوزی که می‌خواهد برنامه‌های ASP.NET Core را ارائه دهد

پس از نصب IIS، نیاز است ASP.NET Core Module نیز نصب گردد. برای این‌کار اگر بسته‌ی NET Core Windows Server Hosting. را نصب کنید، کافی است:
https://go.microsoft.com/fwlink/?LinkId=817246

این بسته به همراه NET Core Runtime, .NET Core Library. و ASP.NET Core Module است. همچنین همانطور که عنوان شد، برنامه‌های ASP.NET Core باید به AppPool ایی تنظیم شوند که NET CLR Version. آن‌ها No Managed Code است. این‌ها حداقل‌های راه اندازی یک برنامه‌ی ASP.NET Core بر روی سرورهای ویندوزی هستند.


هنوز فایل app_offline.htm نیز در اینجا معتبر است

یکی از خواص ASP.NET Core Module، پردازش فایل خاصی است به نام app_offline.htm. اگر این فایل را در ریشه‌ی سایت قرار دهید، برنامه پردازش تمام درخواست‌های رسیده را قطع خواهد کرد و سپس پروسه‌ی برنامه خاتمه می‌یابد. هر زمانیکه این فایل حذف شد، مجددا با درخواست بعدی رسیده، برنامه آماده‌ی پاسخگویی می‌شود.