مطالب
آشنایی با ساختار IIS قسمت اول
در مقاله قبل در مورد نحوه ذخیره سازی در حافظه نوشتیم و به user mode و kernel mode اشاراتی کردیم که می‌توانید به آن رجوع کنید.
در این سری مقالات قصد داریم به بررسی اجزا و روند کاری موجود در IIS بپردازیم که چگونه IIS کار می‌کند و شامل چه بخش هایی می‌شود. مطمئنا آشنایی با این بخش‌ها در روند شناسایی رفتارهای وب اپلیکیشن‌ها و واکنش‌های سرور، کمک زیادی به ما خواهد کرد. در اینجا نسخه IIS7 را به عنوان مرجع در نظر گرفته‌ایم.
وب سرور IIS در عبارت مخفف Internet information services به معنی سرویس‌های اطلاعاتی اینترنت می‌باشد. IIS شامل کامپوننت‌های زیادی است که هر کدام ازآن‌ها کار خاصی را انجام میدهند؛ برای مثال گوش دادن به درخواست‌های ارسال شده به سرور، مدیریت فرآیندها Process و خواندن فایل‌های پیکربندی Configuration؛ این اجزا شامل protocol listener ،Http.sys و WSA و .. می‌شوند.
Protocol Listeners
این پروتکل‌ها به درخواست‌های رسیده گوش کرده و آن‌ها را مورد پردازش قرار می‌دهند و پاسخی را به درخواست کننده، ارسال می‌کنند. هر listener بر اساس نوع پروتکل متفاوت هست. به عنوان مثال کلاینتی، درخواست صفحه‌ای را می‌کند و http listener که به آن Http.sys می‌گویند به آن پاسخ می‌دهد. به طور پیش فرض http.sys به درخواست‌های http و https گوش فرا می‌دهد، این کامپوننت از IIS6 اضافه شده است ولی در نسخه 7 از SSL نیز پشتیبانی می‌کند.
Http.sys یا Hypertext transfer protocol stack
کار این واحد در سه مرحله دریافت درخواست، ارسال آن به واحد پردازش IIS و ارسال پاسخ به کلاینت است؛ قبل از نسخه 6 از Winsock یا windows socket api  که یک کامپوننت user-mod بود استفاده می‌شد ولی Http.sys یک کامپوننت Kernel-mod هست.

Http.sys مزایای زیر را به همراه دارد:

  • صف درخواست مد کرنل: به خاطر اینکه کرنل مستقیما درخواست‌ها را به پروسه‌های مربوطه میفرستد و اگر پروسه موجود نباشد، درخواست را در صف گذاشته تا بعدا پروسه مورد نظر آن را از صف بیرون بکشد.
  • برای درخواست‌ها یک پیش پردازش و همچنین اعمال فیلترهای امنیتی اعمال می‌گردد. 
  • عملیات کش کردن تماما در محیط کرنل مد صورت می‌گیرد؛ بدون اینکه به حالت یوزرمد سوییچ کند. مد کرنل دسترسی بسیار راحت و مستقیمی را برای استفاده از منابع دارد و لازم نیست مانند مد کاربر به لایه‌های زیرین، درخواست کاری را بدهد؛ چرا که خود مستقیما وارد عمل می‌شود و برداشته شدن واسط در سر راه، موجب افزایش عمل caching می‌شود. همچنین دسترسی به کش باعث می‌شود که مستقیما پاسخ از کش به کاربر برسد و توابع پردازشی در حافظه بارگذاری نشوند. البته این کش کردن محدودیت هایی را هم به همراه دارد:
    1. کش کرنل به صورت پیش فرض بر روی صفحات ایستا فعال شده است؛ نه برای صفحاتی با محتوای پویا که البته این مورد قابل تغییر است که نحوه این تغییر را پایینتر توضیح خواهیم داد.
    2. اگر آدرس درخواستی شامل کوئری باشد صفحه کش نخواهد شد:    http://www.site.info/postarchive.htm?id=25 
    3. برای پاسخ ازمکانیزم‌های فشرده سازی پویا استفاده شده باشد مثل gzip کش نخواهد شد
    4. صفحه درخواست شده صفحه اصلی سایت باشد کش نخواهد شد :   http://www.dotnettip.info ولی اگر درخواست بدین صورت باشه http://www.domain.com/default.htm  کش خواهد کرد.
    5. درخواست به صورت ناشناس anonymous نباشد  و نیاز به authentication داشته باشد کش نخواهد شد (یعنی در هدر شامل گزینه authorization می‌باشد).
    6. درخواست باید از نوع نسخه http1 به بعد باشد.
    7. اگر درخواست شامل Entity-body باشد کش نخواهد کرد.
    8. درخواست شامل If-Range/Range header باشد کش نمی‌شود.
    9. کل حجم response بییشتر از اندازه تعیین شده باشد کش نخواهد گردید، این اندازه در کلید ریجستری UriMaxUriBytes قرار دارد. اطلاعات بیشتر
    10. اندازه هدر بیشتر از اندازه تعیین شده باشد که عموما اندازه تعیین شده یک کیلو بایت است.
    11. کش پر باشد، کش انجام نخواهد گرفت.
    برای فعال سازی کش کرنل راهنمای زیر را دنبال کنید:
    گزینه output cache را در IIS، فعال کنید و سپس گزینه Add را بزنید. کادر add cache rule که باز شود، از شما میخواهد یکی از دو نوع کش مد کاربر و مد کرنل را انتخاب کنید و  مشخص کنید چه نوع فایل‌هایی (مثلا aspx) از این قوانین پیروری کنند و مکانیزم کش کردن به سه روش جلوگیری از کش کردن، کش زمان دار و کش بر اساس آخرین تغییر فایل انجام گردد.


    برای تعیین مقدار سایز کش response که در بالا اشاره کردیم می‌توانید در همان پنجره، گزینه edit feature settings را انتخاب کنید.


    این قسمت از مطلب که به نقل از مقاله  آقای Karol Jarkovsky در این آدرس است یک سری تست هایی با نرم افزار(Web Capacity Analysis Tool (WCAT  گرفته است که به نتایج زیر دست پیدا کرده است:
    Kernel Cache Disabled    4 clients/160 threads/30 sec      257 req/sec
    Kernel Cache Enabled     4 clients/160 threads/30 sec      553 req/sec 
    همانطور که می‌بینید نتیجه فعال سازی کش کرنل پاسخ به بیش از دو برابر درخواست در حالت غیرفعال آن است که یک عدد فوق العاده به حساب میاد.
    برای اینکه خودتان هم تست کرده باشید در این آدرس  برنامه را دانلود کنید و به دنبال فایل request.cfg بگردید و از صحت پارامترهای server و url اطمینان پیدا کنید. در گام بعدی 5 پنجره خط فرمان باز کرده و در یکی از آن‌ها دستور netsh http show cachestate را بنویسید تا تمامی وروردی‌های entry که در کش کرنل ذخیره شده اند لیست شوند. البته در اولین تست کش را غیرفعال کنید و به این ترتیب نباید چیزی نمایش داده شود. در همان پنجره فرمان wcctl –a localhost –c config.cfg –s request.cfg  را زده تا کنترلر برنامه در وضعیت listening قرار بگیرد. در 4 پنجره دیگر فرمان wcclient localhost از شاخه کلاینت را نوشته تا تست آغاز شود. بعد از انجام تست به شاخه نصب کنترلر WCAT رفته و فایل log را بخوانید و اگر دوباره دستور نمایش کش کرنل را بزنید باید خالی باشد. حالا کش را فعال کنید و دوباره عملیات تست را از سر بگیرید و اگر دستور netsh را ارسال کنید باید کش کرنل دارای ورودی باشد.
    برای تغییرات در سطح http.sys می‌توانید از ریجستری کمک بگیرید. در اینجا تعداد زیادی از تنظیمات ذخیره شده در ریجستری برای http.sys لیست شده است.
    مسیرراه‌ها
    SQL Server
    آخرین تاریخ بروزرسانی 93/10/21


    SQL Server 2005

    SQL Server 2008

    SQL Server 2012

    SQL Serve 2014


    مطالب
    مروری بر کتابخانه ReactJS - قسمت سوم - کامپوننت‌های React

    همانطور که در قسمت اول گفته شد، اجزای رابط کاربری (تگ‌های HTML) در کتابخانه‌ی React به عنوان کامپوننت‌ها (مؤلفه‌های جزء)  شناخته میشوند. React تگ‌ها را به عنوان اجزایی مستقل و با وضعیتی مشخص در حافظه میشناسد. دلایل ارزشمند بودن این روش در ادامه بررسی میشود.


    خوانایی بهتر (Readability) 

    React میتواند تگ‌های یگانه یا مخلوطی از تگ‌های به هم مرتبط را در پس زمینه ساخته و با یک نام واحد (کامپوننت) به HTML DOM ارسال کند. یعنی اگر جایی یک کامپوننت صدا زده شود، تگ یا تگ‌های مرتبط به آن کامپوننت را به عنوان خروجی خواهیم داشت. همانطور که میشود تگ‌های مختلف را به صورت تو در تو استفاده کرد، کامپوننت‌ها را هم میشود به همین روش فراخوانی کرد. در مثال زیر روش صدا زدن چند کامپوننت و تگ‌هایی را که ارائه میدهد، داریم. 

         // Components in a JavaScript file.
        <clickableImage href="http://google.com" src="google.png" />
        <LinksContainer>
            <LinksList>
                <clickableImage href="http://yahoo.com" src="yahoo.png" />
            </LinksList>
        </LinksContainer>
    
        <!--Output in HTML DOM-->
        <a href="http://google.com">
            <img src="google.png" />
        </a>
        <div>
            <div>
                <ul>
                    <li>
                        <a href="http://google.com">
                            <img src="google.png" />
                        </a>
                    </li>
                </ul>
            </div>
        </div>

    در قسمت کامپوننت‌ها می‌بینیم که چطور کامپوننت‌ها یکبار به صورت تکی و یک بار به صورت تو در تو اجرا میشوند. خروجی در قسمت Output واضح است که با نام کامپوننت‌ها هماهنگی دارد. با این مثال چند مورد مشخص میشود.

    1. به هر کامپوننت قبلا گفته شده چه تگ‌هایی را باید ایجاد کند. در نتیجه با هر بار فراخوانی در هر مکان، تگ یا تگ‌هایی که به آن معرفی شده را می‌سازد. 
    2. هر کامپوننت میتواند مقادیری را به عنوان ورودی دریافت کند و آنها را به تگ‌ها در خروجی اعمال کند. در مثال بالا href و src در فراخوانی‌های مختلف، مقادیر متفاوتی را به خروجی میفرستند.
    3. با انتخاب نام مناسب برای کامپوننت‌ها، بدون آنکه بدانیم چطور ساخته شده‌اند میتوانیم حدس بزنیم چه تگ‌هایی را خواهند ساخت و این دلیلی است که خوانایی برنامه افزایش میابد.
    4. دلیل دیگر که باعث خوانایی برنامه میشود، این است که هر یک از این کامپوننت‌ها میتوانند تگ‌های زیادی را یک جا بسازند که این کار منجر به کم شدن مقدار کد برنامه میشود. برنامه هر چه کم کدتر، با خوانایی بیشتر! 


    قابلیت استفاده مجدد 

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


    نحوه ساخت یک کامپوننت در React 

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


    Stateless function components 

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

    var hotDrinks = [
        { item: "Tea", price: "7000" },
        { item: "Espresso", price: "10000" },
        { item: "Hot Chocolate", price: "12000" }
    ];
    var MenuItem = function (props) {
        return (
            <li className="list-group-item">
                <span className="badge">{props.price}</span>
                <p>{props.item}</p>
            </li>
        )
    };
    var Menu = function (props) {
        return (
            <div className="row">
                <div className="col-md-4">
                    <ul className="list-group">
                        {props.data.map(item => <MenuItem {...item} />)}
                    </ul>
                </div>
            </div>
        )
    };
    
    ReactDOM.render(
        <Menu data={hotDrinks} />,
        document.getElementById("reactTestContainer")
    )

    1. فرض میکنیم که لیست نوشیدنی‌ها و قیمت آنها را به فرمتی که می‌بینید از سرور دریافت کرده‌ایم. (hotDrinks)
    2. شیء MenuItem یک تابع بدون نام را اجرا میکند. از دیدگاه React این تابع یک کامپوننت است. کامپوننت با هر بار فراخوانی مقادیری را برای یک نوشیدنی و قیمت آن، دریافت میکند.کامپوننت به عنوان خروجی یک تگ <li>، پر شده با مقادیر ورودی را بازگشت میدهد. 
    3. شیء Menu یک تابع بدون نام را اجرا میکند. از دید React این تابع یک کامپوننت است. کامپوننت با هر بار فراخوانی، مجموعه‌ای از نوشیدنی‌ها و قیمت آنها را دریافت میکند. متد map به کمک یک Arrow Function آرایه‌ای از کامپوننت MenuItem ایجاد میکند که به ازای هر عضو ایجاد شده، یکبار MenuItem اجرا میشود. هر عضو (item) دارای یک نام نوشیدنی و قیمت آن است. سه نقطه در {…item} برای پر کردن جای خالی نیست! این عبارت یعنی اینکه مقادیر نام و قیمت را به صورت جداگانه (یعنی دو پارامتر مجزا) به کامپوننت MenuItem ارسال میکند. کامپوننت، به عنوان خروجی یک تگ <ul>، پر شده با آرایه‌ای از کامپوننت MenuItem را بازگشت میدهد.
    4. متد render از شیء ReactDOM وظیفه ساخت تگ‌های JSX واقع در کامپوننت‌ها را در HTML DOM به عهده دارد. پارامتر اول render، کامپوننت Menu است با ورودی داده‌های گرفته شده از سرور. همانطور که شرح داده شد، کامپوننت Menu با فراخوانی و به کمک داده‌های ورودی، کامپوننت MenuItem را پیاده‌سازی خواهد کرد. پارامتر دوم render، محلی است که تگ‌ها باید در آن ساخته شوند. مثلا یک تگ <div>
    5. در هر کدام از کامپوننت‌ها و در قسمت ReactDOM.render میشود از کامپوننت‌های دیگر به صورت تو در تو استفاده کرد. 


    React.createClass 

    React یک API درونی برای ایجاد کامپوننت‌ها، به نام createClass دارد. این تابع باید یک شیء پیکربندی درون خود داشته باشد که در آن و  بین دو آکولاد {} خواص و متدها تعریف می‌شوند. تابع createClass برای کار حداقل باید یک متد به نام render داشته باشد که در آن تگ‌های JSX را قرار میدهیم. کامپوننت MenuItem را که به صورت Stateless ساختیم، دوباره با createClass ایجاد میکنیم. 

    var MenuItem = React.createClass({
        render: function () {
            return (
                <li className="list-group-item">
                    <span className="badge">{this.props.price}</span>
                    <p>{this.props.item}</p>
                </li>
            )
        }
    });

    برای خواندن مقادیر ورودی در این روش باید از this استفاده کنیم. بر اساس قواعد شیء گراییِ، MenuItem و Menu کلاس هستند و هر بار در ReactDOM.render کامپوننت Menu را به HTML DOM ارسال میکنیم. یک نمونه از این کلاس ساخته میشود و کلاس Menu، نمونه‌هایی از کلاس MenuItem را میسازد. this به نمونه‌ی ساخته شده از یک کلاس اشاره دارد. 


    React.Component 

    در روش آخر با استفاده از extend، از کلاس React.Component ارث بری میکنیم و کامپوننت را می‌سازیم. مفاهیم کلاس و ارث بری در جاوااسکریپ را میشود از اینجا یاد گرفت. مجددا MenuItem را با  این روش ایجاد میکنیم. 

    class MenuItem extends React.Component {
        render() {
            return (
                <li className="list-group-item">
                    <span className="badge">{this.props.price}</span>
                    <p>{this.props.item}</p>
                </li>
            );
        }
    }

    همانطور که می‌بینید بین دو روش React.Component و React.createClass تفاوتی جز در syntax آنها نیست. در اینجا از سایر امکانات کلاس در جاوااسکریپت مثل سازنده کلاس میشود استفاده کرد. کامپوننت‌ها در React میتوانند کاری بیشتر از ساخت تگ‌ها در HTML DOM را انجام دهند. در قسمت بعد به قابلیت مهم حفظ و دنبال کردن تغییرات در وضعیت کامپوننت‌ها می‌پردازیم.

    نظرات مطالب
    EF Code First #11
    - به همین دلیل در مورد عدم استفاده از Repository توضیح دادم. کار Repository همین warping عملکرد یک ORM است که نه تنها ضرورتی ندارد بلکه در یک پروژه واقعی به شدت جلوی آزادی عمل شما را خواهد گرفت و همچنین پیاده سازی شما هم قابل انتقال نخواهد بود. استفاده از ORMها وابستگی‌های زیادی را به همراه دارند که شاید تنها قسمتی از آن‌ها را بتوانید مخفی کنید. همچنین برای اینکار باید از قابلیت‌های پیشرفته آن‌ها که ممکن است در سایر ORMs موجود نباشد، صرف نظر کرد. آزمون‌های واحد مرتبط با بانک‌های اطلاعاتی نیاز به بانک اطلاعاتی واقعی دارند تا بتواند قیود را اعمال کند. کار با اشیاء درون حافظه در اینجا اصلا توصیه نمی‌شود.
    - بهتره. ضرورتی نداره. در حد یک مدیریت پروژه بهتر است که با یک نگاه بتوان تشخیص داد ... حداقل یک پوشه Models در برنامه هست. تا این حد کفایت می‌کند.
    مطالب دوره‌ها
    افزونه‌ای برای کپسوله سازی نکات ارسال یک فرم ASP.NET MVC به سرور توسط jQuery Ajax
    اگر مطالب سایت جاری را مطالعه و دنبال کرده باشید، تاکنون به صورت پراکنده نکات زیادی را در مورد استفاده از jQuery Ajax تهیه و ارائه کرده‌ایم. در این مطلب قصد داریم تا این نکات را نظم بخشیده و جهت استفاده مجدد، به صورت یک افزونه کپسوله سازی کنیم.

    در کدها و افزونه‌ای که در ادامه ارائه خواهند شد، این مسایل درنظر گرفته شده است:

    - چگونه اعتبار سنجی سمت کاربر را در حین استفاده از Ajax فعال کنیم.
    - چگونه از چندبار کلیک کاربر در حین ارسال فرم به سرور جلوگیری نمائیم.
    - چگونه Complex Types قابل تعریف در EF Code first را نیز در اینجا مدیریت کنیم.
    - نحوه تعریف صحیح آدرس‌های کنترلرها چگونه باید باشد.
    - نحوه اعلام وضعیت لاگین شخص به او، در صورت بروز مشکل.
    - ارسال صحیح anti forgery token در حین اعمال Ajax ایی.
    - بررسی Ajax بودن درخواست رسیده و تهیه یک فیلتر سفارشی مخصوص آن.
    - از کش شدن اطلاعات Ajax ایی جلوگیری شود.


    ابتدا معرفی مدل برنامه
    using System.ComponentModel;
    using System.ComponentModel.DataAnnotations;
    
    namespace jQueryMvcSample01.Models
    {
        public class User
        {
            [Required(ErrorMessage = "(*)"), DisplayName("نام")]
            public string Name { set; get; }
    
            public PhoneInfo PhoneInfo { set; get; }
        }
    
        public class PhoneInfo
        {
            [Required(ErrorMessage = "(*)"), DisplayName("تلفن")]
            public string Phone { get; set; }
    
            [Required(ErrorMessage = "(*)"), DisplayName("پیش شماره")]
            public string Ext { get; set; }
        }
    }
    همانطور که ملاحظه می‌کنید، خاصیت PhoneInfo، تو در تو یا به نوعی Complex است. اگر از ابزارهای Scafolding توکار VS.NET برای تولید View متناظر استفاده کنیم، فیلد تو در توی PhoneInfo را لحاظ نخواهد کرد، اما ... مهم نیست. تعریف دستی آن هم کار می‌کند.


    کدهای کنترلر برنامه

    using System.Web.Mvc;
    using jQueryMvcSample01.Models;
    using jQueryMvcSample01.Security;
    
    namespace jQueryMvcSample01.Controllers
    {
        public class HomeController : Controller
        {
            [HttpGet]
            public ActionResult Index()
            {
                return View(); //نمایش فرم
            }
    
            [HttpPost]
            [AjaxOnly] //فقط در حالت ای‌جکس قابل دسترسی باشد
            [ValidateAntiForgeryToken]
            public ActionResult Index(User user)
            {
                if (this.ModelState.IsValid)
                {
                    // ذخیره سازی در بانک اطلاعاتی ...
                    System.Threading.Thread.Sleep(3000);
    
                    return Content("ok");//اعلام موفقیت آمیز بودن کار
                }
    
                return Content(null);//ارسال خطا
            }
        }
    }
    در اینجا در متد Index، اطلاعات شیء User به صورت Ajaxایی دریافت شده و پس از آن برای مثال قابلیت ذخیره سازی را خواهد داشت.
    چند نکته در اینجا حائز اهمیت هستند:
    الف) استفاده از ویژگی AjaxOnly (که کدهای آن‌را در پروژه پیوست می‌توانید مشاهده نمائید)، جهت صرفا پردازش درخواست‌های Ajaxایی.
    ب) استفاده از ویژگی ValidateAntiForgeryToken در حین اعمال اجکسی. اگر سایت‌های مختلف را در اینباره جستجو کنید، عموما برای پردازش آن در حین استفاده از jQuery Ajax بسیار مشکل دارند.
    ج) استفاده از return Content برای اعلام نتیجه کار. اگر اطلاعات ثبت شد، یک ok یا هر عبارت دیگری که علاقمند بودید ارسال گردیده و در غیراینصورت null بازگشت داده می‌شود.


    کدهای افزونه PostMvcFormAjax

    // <![CDATA[
    (function ($) {
        $.fn.PostMvcFormAjax = function (options) {
            var defaults = {
                postUrl: '/',
                loginUrl: '/login',
                beforePostHandler: null,
                completeHandler: null,
                errorHandler: null
            };
            var options = $.extend(defaults, options);
    
            var validateForm = function (form) {
                //فعال سازی دستی اعتبار سنجی جی‌کوئری
                var val = form.validate();
                val.form();
                return val.valid();
            };
    
            return this.each(function () {
                var form = $(this);
                //اگر فرم اعتبار سنجی نشده، اطلاعات آن ارسال نشود
                if (!validateForm(form)) return;
    
                //در اینجا می‌توان مثلا دکمه‌ای را غیرفعال کرد
                if (options.beforePostHandler)
                    options.beforePostHandler(this);
    
                //اطلاعات نباید کش شوند
                $.ajaxSetup({ cache: false });
    
                $.ajax({
                    type: "POST",
                    url: options.postUrl,
                    data: form.serialize(), //تمام فیلدهای فرم منجمله آنتی فرجری توکن آن‌را ارسال می‌کند
                    complete: function (xhr, status) {
                        var data = xhr.responseText;
                        if (xhr.status == 403) {
                            window.location = options.loginUrl; //در حالت لاگین نبودن شخص اجرا می‌شود
                        }
                        else if (status === 'error' || !data) {
                            if (options.errorHandler)
                                options.errorHandler(this);
                        }
                        else {
                            if (options.completeHandler)
                                options.completeHandler(this);
                        }
                    }
    
                });
            });
        };
    })(jQuery);
    // ]]>
    چند نکته مهم در تهیه این افزونه رعایت شده:
    الف) فعال سازی دستی اعتبار سنجی جی‌کوئری، از این جهت که این نوع اعتبار سنجی به صورت پیش فرض تنها در حالت postback و ارسال کامل صفحه به سرور فعال می‌شود.
    ب) استفاده از متد serialize جهت پردازش یکباره کل اطلاعات و فیلدهای یک فرم.
    نکته مهم این متد ارسال فیلد مخفی anti forgery token نیز می‌باشد. فقط باید دقت داشت که این فیلد در حالتی که dataType به json تنظیم شود و همچنین از متد serialize استفاده گردد، در ASP.NET MVC پردازش نمی‌گردد (خیلی مهم!). به همین جهت در اینجا dataType تنظیمات jQuery Ajax حذف شده است.
    ج) تنظیم cache به false در تنظیمات ابتدایی jQuery Ajax تا اطلاعات ارسالی و دریافتی کش نشوند و مشکل ساز نگردند.
    د) بررسی xhr.status == 403 که توسط SiteAuthorizeAttribute (جایگزین بهتر فیلتر Authorize توکار ASP.NET MVC که کدهای آن در پروژه پیوست قابل دریافت است) و هدایت کاربر به صفحه لاگین


    تعریف View ایی که از اشیاء تو در تو استفاده می‌کند و همچنین از افزونه فوق برای ارسال اطلاعات بهره خواهد برد:

    @model jQueryMvcSample01.Models.User
    @{
        ViewBag.Title = "تعریف کاربر";
        var postUrl = Url.Action(actionName: "Index", controllerName: "Home");
    }
    @using (Html.BeginForm(actionName: "Index", controllerName: "Home",
                           method: FormMethod.Post,
                           htmlAttributes: new { id = "UserForm" }))
    {
        @Html.ValidationSummary(true)
        @Html.AntiForgeryToken()
    
        <fieldset>
            <legend>تعریف کاربر</legend>
            <div class="editor-label">
                @Html.LabelFor(model => model.Name)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.Name)
                @Html.ValidationMessageFor(model => model.Name)
            </div>
            <div class="editor-label">
                @Html.LabelFor(model => model.PhoneInfo.Ext)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.PhoneInfo.Ext)
                @Html.ValidationMessageFor(model => model.PhoneInfo.Ext)
            </div>
            <div class="editor-label">
                @Html.LabelFor(model => model.PhoneInfo.Phone)
            </div>
            <div class="editor-field">
                @Html.EditorFor(model => model.PhoneInfo.Phone)
                @Html.ValidationMessageFor(model => model.PhoneInfo.Phone)
            </div>
            <p>
                <input type="submit" id="btnSave" value="ارسال" />
            </p>
        </fieldset>
    }
    @section JavaScript
    {
        <script type="text/javascript">
            $(document).ready(function () {
                $("#btnSave").click(function (event) {
                    //جلوگیری از پست بک به سرور
                    event.preventDefault();
    
                    var button = $(this);
    
                    $("#UserForm").PostMvcFormAjax({
                        postUrl: '@postUrl',
                        loginUrl: '/login',
                        beforePostHandler: function () {
                            //غیرفعال سازی دکمه ارسال
                            button.attr('disabled', 'disabled');
                            button.val("...");
                        },
                        completeHandler: function () {
                            //فعال سازی مجدد دکمه ارسال
                            alert('انجام شد');
                            button.removeAttr('disabled');
                            button.val("ارسال");
                        },
                        errorHandler: function () {
                            alert('خطایی رخ داده است');
                        }
                    });
                });
            });
        </script>
    }
    همانطور که عنوان شد، مهم نیست که اشیاء تو در تو توسط ابزار Scafolding پشتیبانی نمی‌شود. این نوع خواص را به همان نحو متداول ذکر زنجیره وار خواص می‌توان معرفی و استفاده کرد:
     @Html.EditorFor(model => model.PhoneInfo.Phone)
    هم اعتبار سنجی سمت کلاینت آن کار می‌کند و هم اطلاعات آن به اشیاء و خواص متناظر به خوبی نگاشت خواهد شد:


    در ادامه نحوه استفاده از افزونه PostMvcFormAjax را مشاهده می‌کنید. چند نکته نیز در اینجا حائز اهمیت هستند:
    الف) توسط htmlAttributes یک id برای فرم تعریف کرده‌ایم تا در افزونه PostMvcFormAjax مورد استفاده قرار گیرد.
    ب) postUrl و loginUrl را همانند متغیر تعریف شده در ابتدای View توسط Url.Action باید تعریف کرد تا در صورتیکه سایت ما در ریشه اصلی قرار نداشت، باز هم به صورت خودکار مسیر صحیحی محاسبه و ارائه گردد.
    ج) نحوه غیرفعال سازی و فعال سازی دکمه submit را در روال‌های beforePostHandler و completeHandler ملاحظه می‌کنید. این مساله برای جلوگیری از کلیک‌های مجدد یک کاربر ناشکیبا و جلوگیری از ثبت اطلاعات تکراری بسیار مهم است.
    د) کل این اطلاعات، در یک section به نام JavaScript ثبت شده است. این section در فایل layout برنامه به صورت زیر مورد استفاده قرار خواهد گرفت و به این ترتیب مقدار دهی خواهد شد:
    <head>
        <title>@ViewBag.Title</title>    
        <link href="@Url.Content("Content/Site.css")" rel="stylesheet" type="text/css" />
        <script src="@Url.Content("~/Scripts/jquery-1.9.1.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
        <script src="@Url.Content("~/Scripts/jquery.PostMvcFormAjax.js")" type="text/javascript"></script>
        @RenderSection("JavaScript", required: false)
    </head>

    دریافت کدهای کامل این قسمت
    jQueryMvcSample01.zip

     
    مطالب
    سفارشی کردن قابلیت طراحی ریپورت توسط END USER - بخش اول
    در قسمتی از پروژه تجاری، برای طراحی ریپورت توسط کاربر نیاز به موضوع مطرح شده این پست داشتم و به نتایج مطلوبی در این زمینه توسط کامپوننت Stimulsoft Report.Net دست یافتم.
    پروژه بنده به صورتی هست که در آن قرار است یک سری اطلاعات توسط DataTable به Report ارسال و ریپورت هم Field‌ها را که از قبل طراحی شده، روی فرم داشته باشد و کاربر فقط این امکان را داشته باشد که مکان فیلد‌ها ( یعنی مختصات ) روی صفحه A4 را با توجه به سلیقه خودش تنظیم و ذخیره نماید.

    اهداف :
    1.غیر فعال کردن بعضی از امکانات صفحه Design
    2.اعمال یکسری تنظیمات از طریق کدنویسی (Code Behind) بر روی ریپورت
    مانند : اینکه ShowGrid فعال باشه یا نه -- یا Toolbox.visible=false باشه
    3.فارسی سازی محیط طراحی برای کاربر
    4.ذخیره و ...

    اسکرین شات : ( حالت پیشفرض بدون اعمال تغییرات Runtime )


    رفرنس‌های مورد نیاز:
    using Stimulsoft.Base.Services;
    using Stimulsoft.Report;
    using Stimulsoft.Report.Components;
    using Stimulsoft.Report.Design;
    using Stimulsoft.Report.Design.Toolbars;
    using Stimulsoft.Report.Render;
    using Stimulsoft.Report.Units;

    //ایجاد یک شی از ریپورت            
    StiReport report = new StiReport();
    //ریست کردن تنظیمات به حالت پیشفرض
     report.Reset();
    //ریست کردن تنظیمات سرویس‌های StiConfig.Reset();
    //ریست کردن تنظیمات چاپ
     StiSettings.Clear();
    
                
    
                //تنظیم عنوان ریپورت به صورت دلخواه
                StiOptions.Designer.DesignerTitle = title + " طراحی فرم ";
                StiOptions.Designer.DesignerTitleText = title + " طراحی فرم ";
                //غیرفعال شدن نمایش تب کدنویسی
                StiOptions.Designer.CodeTabVisible = false;
                //فعال کردن امکان RightToLeft
                StiOptions.Designer.UseRightToLeftGlobalizationEditor = true;
                //غیرفعال شدن قابلیت تغییر نام ریپورت توسط کاربر
                StiOptions.Designer.CanDesignerChangeReportFileName = false;
                //غیر فعال کردن تشخیص اتوماتیک زبان پیش فرض UI طراحی
                StiOptions.Designer.UseSimpleGlobalizationEditor = false;
    
                //تنظیم تم ریپورت از حالت استاندارد به ریبون
                StiOptions.Windows.GlobalGuiStyle = StiGlobalGuiStyle.Office2010Blue;
                //فعال سازی تم ریبون
                StiOptions.Designer.IsRibbonGuiEnabled = true;
    برای دسترسی به بخش Dictionary که به اطلاعات DataSoruce‌ها دسترسی می‌دهد البته برای قابلیت Design :
    یک شی از StiOptions ایجاد کنید. سپس Designer و فعال و غیرفعال کردن نمایش هر بخش را و حتی تغییر آیتم‌های موجود در منوی راست کلیک هر شی، قابل تغییر خواهند بود.

    پنل Dictionary از سه شاخه اصلی تشکیل می‌شود : که با دستورات زیر می‌توان نمایش این بخش‌ها را در صورت خالی بودن از داده غیر فعال نمود
    BusinessObjectsCategory - DataSourcesCategory -VariablesCategory
    StiOptions.Designer.Panels.Dictionary.ShowEmptyBusinessObjectsCategory = false;
    StiOptions.Designer.Panels.Dictionary.ShowEmptyDataSourcesCategory = false;
    StiOptions.Designer.Panels.Dictionary.ShowEmptyVariablesCategory = false;
    در صفحه طراحی، 3 پنل وجود دارد :
    1- Dictionary
    2- Properties
    3- Report Tree

    که Properties نسبت به هر شیء ایی که از صفحه ریپورت انتخاب می‌کنید، تنظیمات مربوط به آن را برای ویرایش در اختیار کاربر قرار می‌دهد.
    پنل Report Tree نیز از داده‌های موجود از دیتاسورس بخش Dictionary که به صورت شیء در صفحه ریپورت قرار داده شده‌اند نمایش درخت واره‌ای را در اختیار کاربر قرار می‌دهد و می‌تواند از اشیاء این درخت واره در ریپورت به صورت متعدد استفاده نماید.

    می‌توان هر کدام از این پنل‌ها را ( که به صورت سرویس در Stimulsoft تعریف شده) از دید کاربر مخفی نمود یا به صورت محدود یکسری از قابلیت‌ها را در اختیار کاربر قرار داد:
    //غیر فعال کردن سرویس‌های پنل
                Stimulsoft.Report.Design.Panels.StiPropertiesPanelService propPanel = Stimulsoft.Report.Design.Panels.StiPropertiesPanelService.GetService();
                propPanel.ServiceEnabled = false;
    
                Stimulsoft.Report.Design.Panels.StiDictionaryPanelService dictPanel = Stimulsoft.Report.Design.Panels.StiDictionaryPanelService.GetService();
                dictPanel.ServiceEnabled = true;
                
                Stimulsoft.Report.Design.Panels.StiReportTreePanelService treePanel = Stimulsoft.Report.Design.Panels.StiReportTreePanelService.GetService();
                treePanel.ServiceEnabled = false;
    
                Stimulsoft.Report.Design.Toolbars.StiToolsToolbarService cpanel = Stimulsoft.Report.Design.Toolbars.StiToolsToolbarService.GetService();
                cpanel.ServiceEnabled = false;
                StiOptions.Dictionary.BusinessObjects.AddBusinessObjectAssemblyToReferencedAssembliesAutomatically = false;
                StiOptions.Dictionary.BusinessObjects.AllowProcessNullItemsInEnumerables = false;
                StiOptions.Dictionary.BusinessObjects.AllowUseDataColumn = false;
                StiOptions.Dictionary.BusinessObjects.AllowUseFields = false;
                StiOptions.Dictionary.BusinessObjects.AllowUseProperties = false;
                StiOptions.Dictionary.BusinessObjects.CheckTableDuplication = false;
               
                
    
                StiOptions.Dictionary.ShowOnlyAliasForDataSource = true;
                StiOptions.Dictionary.ShowOnlyAliasForDataColumn = true;
                StiOptions.Dictionary.ShowOnlyAliasForTotal = true;
                dictPanel.ShowNewButton = false;
                dictPanel.ShowActionsButton = false;
                dictPanel.ShowBusinessObjectNewMenuItem = false;
                dictPanel.ShowCalcColumnNewMenuItem = false;
                dictPanel.ShowCategoryNewMenuItem = false;
                dictPanel.ShowCollapseAllMenuItem = true;
                dictPanel.ShowColumnNewMenuItem = false;
                dictPanel.ShowConnectionNewMenuItem = false;
                dictPanel.ShowContextMenu = false;
                dictPanel.ShowCreateFieldOnDoubleClick = false;
                dictPanel.ShowCreateLabel = false;
                dictPanel.ShowDataParameterNewMenuItem = false;
                dictPanel.ShowDataSourceNewMenuItem = false;
                dictPanel.ShowDataSourcesNewMenuItem = false;
                dictPanel.ShowDeleteButton = false;
                dictPanel.ShowDeleteForBusinessObject = false;
                dictPanel.ShowDeleteForDataColumn = false;
                dictPanel.ShowDeleteForDataConnection = false;
                dictPanel.ShowDeleteForDataParameter = false;
                dictPanel.ShowDeleteForDataRelation = false;
                dictPanel.ShowDeleteForDataSource = false;
                dictPanel.ShowDeleteForVariable = false;
                dictPanel.ShowDeleteMenuItem = false;
                dictPanel.ShowDictMergeMenuItem = false;
                dictPanel.ShowDictNewMenuItem = false;
                dictPanel.ShowDictOpenMenuItem = false;
                dictPanel.ShowDictSaveMenuItem = false;
                dictPanel.ShowDictXmlExportMenuItem = false;
                dictPanel.ShowDictXmlImportMenuItem = false;
                dictPanel.ShowDictXmlMergeMenuItem = false;
                dictPanel.ShowDownButton = false;
                dictPanel.ShowEditButton = false;
                dictPanel.ShowEditForBusinessObject = false;
                dictPanel.ShowEditForDataColumn = false;
                dictPanel.ShowEditForDataConnection = false;
                dictPanel.ShowEditForDataParameter = false;
                dictPanel.ShowEditForDataRelation = false;
                dictPanel.ShowEditForDataSource = false;
                dictPanel.ShowEditForVariable = false;
                dictPanel.ShowEditMenuItem = false;
                
                dictPanel.ShowExpandAllMenuItem = true;
                dictPanel.ShowMarkUsedMenuItem = false;
                dictPanel.ShowNewButton = false;
                dictPanel.ShowPropertiesForBusinessObject = false;
                dictPanel.ShowPropertiesForDataColumn = false;
                dictPanel.ShowPropertiesForDataConnection = false;
                dictPanel.ShowPropertiesForDataParameter = false;
                dictPanel.ShowPropertiesForDataRelation = false;
                dictPanel.ShowPropertiesForDataSource = false;
                dictPanel.ShowPropertiesForVariable = false;
                dictPanel.ShowPropertiesMenuItem = false;
                dictPanel.ShowRelationNewMenuItem = false;
                dictPanel.ShowRelationsImportMenuItem = false;
                dictPanel.ShowRemoveUnusedMenuItem = false;
                dictPanel.ShowSortItemsButton = false;
                dictPanel.ShowSynchronizeMenuItem = false;
                dictPanel.ShowUpButton = false;
                dictPanel.ShowUseAliases = true;
                dictPanel.ShowVariableNewMenuItem = false;
                dictPanel.ShowViewDataMenuItem = false;
    یکی از قابلیت‌های خوب و کاربردی این کامپوننت پشتیبانی کامل حتی منوها از زبان فارسی می‌باشد که استفاده از این کامپوننت را در نرم افزار‌های تجاری در کشور قابل انعطاف پذیر‌تر می‌کند. برای فارسی سازی به یک فایل XML که در مسیر نصب کامپوننت قرار دارد، نیاز است.
    نام فایل fa.xml می‌باشد. آن‌را در مسیر نرم افزار قرار دهید و سپس کد زیر را اضافه نمایید:
    //تنظیم زبان به فارسی
    StiConfig.LoadLocalization("fa.xml");
    مطالب دوره‌ها
    رها سازی منابع IDisposable در StructureMap
    اگر با برنامه‌های وب و StructureMap کار کرده باشید، حتما از متد جدید HttpContextLifecycle.DisposeAndClearAll و متد قدیمی ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects آن نیز برای Dispose خودکار کلیه اشیاء IDisposable در Application_EndRequest استفاده کرده‌اید. البته شرط استفاده از متدهای یاد شده نیز این است که طول عمر اشیاء IDisposable به صورت Http Scoped تعریف شده باشند:
     x.For<IUnitOfWork>().HybridHttpOrThreadLocalScoped().Use<MyContext>();

    سؤال: برای سایر حالات چطور؟ در یک برنامه‌ی ویندوزی کنسول یا سرویس ویندوز که Http Scoped در آن معنا ندارد چکار باید کرد؟
    پاسخ: در اینجا حداقل دو راه حل وجود دارد:
    الف) استفاده از nested containers
     using (var container = ObjectFactory.Container.GetNestedContainer())
    {
        var uow = container.GetInstance<IUnitOfWork>();
    
    }
    قابلیتی از نگارش 2.6 استراکچرمپ به آن اضافه شده‌است به نام nested containers که هدف از آن Dispose خودکار کلیه اشیاء Transient از نوع IDisposable است. در اینجا منظور از Transient این است که طول عمر شیء مدنظر به صورت Singleton، HttpContext scoped و یا ThreadLocal scoped تعریف نشده باشد (هیچ نوع caching خاصی به طول عمر آن اعمال نشده باشد).
    در مثال فوق، پس از پایان کار قطعه‌ی using نوشته شده، به صورت خودکار کلیه اشیاء IDisposable یافت شده و Dispose می‌شوند.

    ب) نگاهی به پشت صحنه‌ی متد DisposeAndClearAll
    اگر اشیاء IDisposable شما با طول عمر HybridHttpOrThreadLocalScoped معرفی شده باشند (و Transient نباشند)، با دستور ذیل چه در برنامه‌های ویندوزی و چه در برنامه‌های وب، کلیه‌ی آن‌ها یافت شده و به صورت خودکار Dispose می‌شوند:
     new HybridLifecycle().FindCache(null).DisposeAndClear();
    متد HttpContextLifecycle.DisposeAndClearAll فقط مختص است به برنامه‌های وب. اگر نیاز به متدی دارید که در هر دو حالت برنامه‌های وب و ویندوزی کار کند، از روش HybridLifecycle فوق استفاده نمائید.


    بنابراین به صورت خلاصه
    اگر طول عمر شیء IDisposable مدنظر به صورت هیبرید تعریف شده‌است، از متد DisposeAndClear موجود در HybridLifecycle می‌توان استفاده کرد. اگر طول عمر شیء IDisposable مورد استفاده، معمولی است و هیچ نوع caching خاصی برای آن درنظر گرفته نشده‌است، می‌توان از روش nested containers برای رها سازی خودکار منابع آن کمک گرفت.
    مطالب
    کنترل دسترسی‌ها در Angular با استفاده از Ng2Permission
    سناریویی را در نظر بگیرید که در آن بعد از احراز هویت کاربر، لیست دسترسی‌هایی را که کاربر به بخش‌های مختلف خواهد داشت، از سرور دریافت می‌کند. به عنوان مثال کل دسترسی‌های موجود در سیستم به شرح زیر است:
    1. ViewUsers 
    2. CreateUser 
    3. EditUser 
    4. DeleteUser 
    حالا فرض کنید، کاربر X بعد از احراز هویت، از لیست دسترسی‌های موجود، تنها دسترسی ViewUsers و EditUser را دریافت می‌کند. یعنی تنها مجاز به مشاهده‌ی لیست کاربران و ویرایش کردن آنها می‌باشد.
    در اینجا جهت جلوگیری از دسترسی به ویرایش کاربر، با استفاده از یک Router guard سفارشی می‌توان مسیر users/edit را برای کاربر غیر قابل استفاده کرد؛ به نحوی که اگر کاربر وارد شده مجوز EditUser را نداشت، این مسیر غیر قابل دسترسی باشد. 
    از طرفی صفحه‌ی ViewUsers، برای کاربری با تمامی دسترسی‌ها، به شکل زیر خواهد بود: 


    همانطور که مشاهده می‌کنید، المنت‌هایی در صفحه وجود دارند که کاربر X نباید آنها را مشاهده کند. از جمله دکمه حذف کاربر و دکمه ایجاد کاربر. برای مخفی کردن آنها چه راه‌حلی را می‌توان ارائه داد؟ شاید بخواهید برای اینکار از ngIf* استفاده کنید. برای اینکار کافی است دست بکار شوید تا مشکلاتی را که در این روش به آنها بر می‌خورید، متوجه شوید. از جمله این مشکلات می‌توان به پیچیدگی بسیار زیاد و وجود کدهای تکراری در هر کامپوننت اشاره کرد (در بهترین حالت هر کامپوننت باید سرویس حاوی دسترسی‌ها را در خود تزریق کرده و با استفاده از توابعی، وجود یا عدم وجود دسترسی را بررسی کند). 

    راه‌حل بهتر، استفاده از یک Directive سفارشی است. همچنین ماژول  Ng2Permission  علاوه بر فراهم کردن Directive جهت مدیریت المنت‌های روی صفحه، امکاناتی را جهت نگهداری و تعریف دسترسی‌های جدید و همچنین محافظت از Routeها فراهم کرده است. این ماژول الهام گرفته از ماژول  angular-permission می‌باشد.

    کافی است با استفاده از دستور زیر این ماژول را نصب کنید: 

    npm install angular2-permission --save

    بعد از نصب، ماژول Ng2Permission را در قسمت imports در ماژول اصلی برنامه، اضافه کنید. 

    import { Ng2Permission } from 'angular2-permission';
    @NgModule({
      imports: [
        Ng2Permission
      ]
    })


    مدیریت دسترسی‌ها

     برای مدیریت دسترسی‌های کاربر، از سرویس PermissionService، در هرجایی از برنامه می‌توانید استفاده کنید. این سرویس دارای متدهای زیر است.:
    توضیحات  امضاء 
     تعریف دسترسی‌های جدید   define(permissions: Array<string>): void
     افزودن دسترسی جدید   add(permission: string ) : void 
     حذف دسترسی مشخص شده   remove(permission: string ) : void 
     برسی اینکه دسترسی قبلا تعریف شده است؟   hasDefined( permission : string ) : boolean 
     برسی اینکه حداقل یکی از دسترسی‌های ورودی قبلا تعریف شده است؟   hasOneDefined(permissions: Array < string > ) : boolean 
     حذف تمامی دسترسی‌ها   clearStore( ) : void 
     دریافت تمامی دسترسی‌های تعریف شده   get store ( ) : Array < string>
     Emitter جهت تغییر در دسترسی‌ها  get permissionStoreChangeEmitter ( ) : EventEmitter< an y>

    برای مثال جهت تعریف دسترسی‌های جدید، کافی است سرویس PermissionService را تزریق کرده و با استفاده از متدهای define اقدام به اینکار کنید: 

    import { PermissionService } from 'angular2-permission';
    @Component({
        […]
    })
    export class LoginComponent implements OnInit {
        constructor(private _permissionService: PermissionService) { 
            this._permissionService.define(['ViewUsers', 'CreateUser', 'EditUser', 'DeleteUser']);
        }
    }

    آنچه که واضح است، این است که لیست دسترسی‌ها می‌توانند از سمت سرور تامین شوند.

     

    محافظت از مسیرهای تعریف شده

     بعد از تعریف دسترسی‌ها، برای محافظت از مسیر‌های غیر مجاز برای کاربر می‌توانید از PermissionGuard به شکل زیر استفاده کنید: 
    import { PermissionGuard, IPermissionGuardModel } from 'angular2-permission';
    […]
    const routes: Routes = [
        {
            path: 'login',
            component: LoginComponent,
            children: []
        },
        {
            path: 'users',
            component: UserListComponent,
            canActivate: [PermissionGuard],
            data: {
                Permission: {
                    Only: ['ViewUsers'],
                    RedirectTo: '403'
                } as IPermissionGuardModel
            },
            children: []
        },
        {
            path: 'users/create',
            component: UserCreateComponent,
            canActivate: [PermissionGuard],
            data: {
                Permission: {
                    Only: ['CreateUser'],
                    RedirectTo: '403'
                } as IPermissionGuardModel
            },
            children: []
        },
        {
            path: '403',
            component: AccessDeniedComponent,
            children: []
        }
    ];
    
    @NgModule({
        imports: [RouterModule.forRoot(routes)],
        exports: [RouterModule]
    })
    export class AppRoutingModule { }
    همانطور که مشاهده می‌کنید کافی است canActivate در هر Route را به PermissionGuard تنظیم کنید. همچنین در data از طریق کلید Permission، تنظیمات این Guard را مشخص کنید. کلید Permission در data یک شیء از نوع IPermissionGuardModel را دریافت خواهد کرد که حاوی خصوصیات زیر است:
     
    توضیحات     خصوصیت 
     فقط دسترسی‌های تعریف شده در این قسمت، امکان پیمایش به این مسیر را خواهند داشت.   Only 
     تمامی دسترسی‌های تعریف شده، به جز دسترسی‌های تعریف شده در این قسمت، امکان پیمایش به این مسیر را خواهند داشت.   Except 
     در صورتیکه تقاضای غیر مجازی جهت پیمایش به این مسیر صادر شد، کاربر را به این مسیر هدایت می‌کند.   RedirectTo 

    نکته ۱: مقدار دهی همزمان Only و Except مجاز نیست.
    نکته ۲: ذکر چند دسترسی در هر یک از خصوصیت‌های Only و Except معنی «یا منطقی» دارد. مثلا در قطعه کد زیر، اگر دسترسی Admin باشد «یا» دسترسی CreateUser باشد امکان مشاهده پیمایش صفحه وجود خواهد داشت. 
    {
        path: 'users/create',
        component: UserCreateComponent,
        canActivate: [PermissionGuard],
        data: {
            Permission: {
                Only: ['Admin', 'CreateUser'],
                RedirectTo: '403'
            } as IPermissionGuardModel
        },
        children: []
    }

    محافظت از المنت‌های صفحه

     جهت محافظت از المنت‌های موجود در صفحه، ماژول Ng2Permission دایرکتیوهایی را برای این منظور تدارک دیده است: 
     توضیحات  Input type     Directive 
     فقط دسترسی‌های تعریف شده در این دایرکتیو امکان مشاهده (به صورت پیش فرض) المنت را دارند.   Arrayy<string>   hasPermission 
     تمامی دسترسی‌های موجود، به جز دسترسی‌های تعریف شده در این دایرکتیو امکان مشاهده (به صورت پیش فرض) المنت را دارند.   Array<string>   exceptPermission
     استراتژی برخورد با المنت، هنگامیکه کاربر دسترسی به المنت دارد.   string | Function   onAuthorizedPermission 
     استراتژی برخورد با المنت، هنگامیکه کاربر دسترسی به المنت را ندارد.  string | Function 
     onUnauthorizedPermission 

    در مثال زیر فقط کاربرانی که دسترسی DeleteUser را داشته باشند، امکان مشاهده دکمه حذف را خواهند داشت.
    <button type="button" [hasPermission]="['DeleteUser']">
      <span aria-hidden="true"></span>
      Delete
    </button>

    در صورتیکه بیش از یک دسترسی مد نظر باشد، با کاما از هم جدا خواهند شد.

    <button type="button" [hasPermission]="[ 'Admin', 'DeleteUser']">
      <span aria-hidden="true"></span>
      Delete
    </button>
    در مثال بالا درصورتیکه کاربر دسترسی Admin « یا » دسترسی DeleteUser را داشته باشد، امکان مشاهده (به صورت پیش فرض) المنت را خواهد داشت. مثال زیر یعنی تمامی کاربران با هر دسترسی امکان مشاهده المنت را خواهند داشت؛ بجز دسترسی GeustUser. 
    <button type="button" [exceptPermission]="['GeustUser']">
      <span aria-hidden="true"></span>
      Delete
    </button>
    به صورت پیش فرض هنگامیکه کاربری دسترسی به المنتی را نداشته باشد، دایرکتیو، این المنت را مخفی خواهد کرد (با تنظیم استایل display به none). شاید شما بخواهید بجای مخفی/نمایش المنت، مثلا از فعال/غیرفعال کردن المنت استفاده کنید. برای این کار از خصوصیت onAuthorizedPermission و onUnauthorizedPermission به شکل زیر استفاده کنید:
    <button type="button" 
      [hasPermission]="['GeustUser']"
      onAuthorizedPermission="enable"
      onUnauthorizedPermission="disable">
      <span aria-hidden="true"></span>
      Delete
    </button>
    تمامی استراتژی‌های از قبل تعریف شده و قابل استفاده برای خصوصیت onAuthorizedPermission و onUnauthorizedPermission به شرح زیر است:
     رفتار   مقدار 
     حذف خصوصیت disabled از المنت   enable 
    افزودن خصوصیت disabled به المنت     disable 
     تنظیم استایل display به inherit   show 
     تنظیم استایل display به none   hide 

    در صورتیکه هیچ‌کدام از این استراتژی‌ها، پاسخگوی نیاز شما جهت برخورد با المنت نبود، می‌توانید بجای ارسال یک رشته ثابت به خصوصیت onAuthorizedPermission و onUnauthorizedPermission، یک تابع را ارسال کنید تا عملی که می‌خواهید، بر روی المنت انجام دهد. به عنوان مثال می‌خواهیم در صورتیکه کاربر مجاز به استفاده از المنتی نبود، المنت را از طریق تنظیم استایل visibility به hidden، مخفی کنیم و در صورتیکه کاربر مجاز به استفاده از المنت بود، استایل visibility به inherit تنظیم شود. 
    با این فرض، کدهای کامپوننت به شکل زیر:
    @Component({
        selector: 'app-user-list',
        templateUrl: './user-list.component.html',
        styleUrls: ['./user-list.component.css']
    })
    export class UserListComponent {
    
      constructor() { }
    
      OnAuthorizedPermission(element: ElementRef) {
        element.nativeElement.style.visibility ="inherit";
      }
    
      OnUnauthorizedPermission(element: ElementRef) {
        element.nativeElement.style.visibility = "hidden";    
      }
    }
    و تگهای Html زیر: 
    <button 
      [hasPermission]="['CreateUser']"
      [onAuthorizedPermission]="OnAuthorizedPermission"
      [onUnauthorizedPermission]="OnUnauthorizedPermission">
      <span aria-hidden="true"></span>
      Add New User
    </button>
    نکته:  هر تغییر در لیست دسترسی‌ها در لحظه سبب اجرای دوباره دایرکتیوها خواهد شد و تغییرات، در همان لحظه در لایه نمایش قابل مشاهده خواهند بود. 
    مطالب
    قابلیت های جدید VisualStudio.NET 2012 - قسمت یکم
    شاید خیلی از برنامه نویسان با نظر من هم عقیده باشند، Visual Studio.NET بهترین ابزار و IDE جهت توسعه نرم افزار است. در این سری از مطالب قصد داریم نگاهی اجمالی به قابلیت‌های جدید VS.NET 2012 بیاندازیم.

    پنجره New Project:
    شاید بارزترین تغییراتی که در این پنجره به چشم می‌خورد اضافه شدن Template‌های جدید باشد. البته نباید از ظاهر گرافیکی به سبک Metro چشم پوشی کرد. یکی از این Template‌ها Portable Class Library است. توسط این گرینه می‌توان dll‌های قابل استفاده در Windows، Windows Phone،  Silverlight و البته  XBox 360 ایجاد کرد.
      
     
     
    Template جدید دیگری که در VS.NET 2012 اضافه شده است گروه مربوط به Visual Studio Light Switch است. توسط این گزینه امکان ایجاد برنامه‌های Light Switch، بدون نیاز به نصب Visual Studio Light Switch بصورت جداگانه، فراهم است و جهت توسعه این دسته از برنامه‌ها می‌توانید از VS.NET 2012 استفاده کنید.
     


     
    پیش نمایش تصاویر در Solution Explorer
    یکی دیگر از قابلیت‌های اضافه شده در این برنامه امکان پیش نمایش تصاویر در Solution Explorer است.