مطالب
مشکل IIS6 و دریافت فایل‌های آفیس 2007

IIS6 فایل‌هایی را که نشناسد، سرو نخواهد کرد. بنابراین اگر یکی از کاربران مثلا یک فایل docx آفیس 2007 را آپلود کرده باشد، به محض کلیک بر روی لینک دریافت فایل، با خطای زیر متوقف خواهد شد:

HTTP Error 404 - File or directory not found

فایل بر روی سرور موجود است اما کاربر قادر به دریافت آن نیست.

برای شناساندن فرمت‌های جدید به IIS6 می‌توان به یکی از دو روش زیر عمل کرد:
الف) اضافه کردن mime-type جدید از طریق کنسول IIS
ب) ویرایش کردن فایل MetaBase.xml مربوط به IIS

در هر دو روش فوق نیاز است تا با mime-type فایل‌های جدید آشنا بود. برای مثال لیست کامل mime-types مربوط به فایل‌های آفیس 2007 به صورت زیر است:

.docm,application/vnd.ms-word.document.macroEnabled.12
.docx,application/vnd.openxmlformats-officedocument.wordprocessingml.document
.dotm,application/vnd.ms-word.template.macroEnabled.12
.dotx,application/vnd.openxmlformats-officedocument.wordprocessingml.template
.potm,application/vnd.ms-powerpoint.template.macroEnabled.12
.potx,application/vnd.openxmlformats-officedocument.presentationml.template
.ppam,application/vnd.ms-powerpoint.addin.macroEnabled.12
.ppsm,application/vnd.ms-powerpoint.slideshow.macroEnabled.12
.ppsx,application/vnd.openxmlformats-officedocument.presentationml.slideshow
.pptm,application/vnd.ms-powerpoint.presentation.macroEnabled.12
.pptx,application/vnd.openxmlformats-officedocument.presentationml.presentation
.xlam,application/vnd.ms-excel.addin.macroEnabled.12
.xlsb,application/vnd.ms-excel.sheet.binary.macroEnabled.12
.xlsm,application/vnd.ms-excel.sheet.macroEnabled.12
.xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.xltm,application/vnd.ms-excel.template.macroEnabled.12
.xltx,application/vnd.openxmlformats-officedocument.spreadsheetml.template

روش ب)
ابتدا IIS6 را stop کنید (در غیر اینصورت قادر به ذخیره سازی تغییرات نخواهید بود):
iisreset /stop
سپس فایل متابیس آن‌را در یک ادیتور متنی باز کنید. این فایل در آدرس زیر قرار دارد:
C:\WINDOWS\system32\inetsrv\MetaBase.xml

تگ مربوط به IIsMimeMap را یافته و خطوط فوق را دقیقا به همین صورتیکه ملاحظه می‌کنید به آن اضافه نمائید.



و در آخر IIS را راه اندازی کنید:
iisreset /start

روش الف)
این روش نیازی به stop و start وب سرور ندارد و به محض اضافه شدن، به صورت خودکار اعمال خواهد شد اما کمی طولانی‌تر است:
کنسول IIS را باز کنید
بر روی web sites کلیک راست کنید. (منظور بالاترین سطح ممکن است)
گزینه properties‌ را انتخاب کرده و سپس به برگه http headers‌ مراجعه نمائید.
در اینجا بر روی دکمه mime-types کلیک کرده و در صفحه باز شده باید تک تک موارد جدید را به صورت دستی وارد نمائید (در اینجا نیازی به ذکر نقطه مربوط به پسوند فایل نیست)



لازم به ذکر است که این نوع mime-types به IIS7 اضافه شده‌اند.

نظرات اشتراک‌ها
فراخوانی بیشتر از یک بار "window.onload"
- اگر از MVC استفاده می‌کنید، در فایل layout
...
    اینجا اسکریپت‌های عمومی قرار گیرند قبل از پایان فایل
    @RenderSection("JavaScript", false)
</body>
</html>
به کمک RenderSection کلیه اسکریپت‌های inline فقط در همین مکان فراخوانی آن قرار می‌گیرند. به این ترتیب در Viewها فقط کافی است یک section به نام JavaScript اضافه کرد تا در این مکان درج شود.
- در ASP.NET Web forms هم می‌شود این نظم رو پدید آورد. از ContentPlaceHolder استفاده کنید. یکی در master page تعریف شود دیگری در وب فرم به ارث رسیده از آن برای قرار دادن اسکریپت‌های خاص همان صفحه.
    ....  سایر قسمت‌های فایل مستر پیج
    <asp:ContentPlaceHolder id='PageScriptPlaceHolder' runat='server'>
    </asp:ContentPlaceHolder>
</body>
حالا وب فرم شما فقط کافی است اسکریپت‌های خاص خودش را با ID فوق درج کنه
<asp:Content ID='ScriptIncludes' runat='server'  ContentPlaceHolderID='PageScriptPlaceHolder'>
  اسکریپت‌های صفحه در اینجا
</asp:Content>
مطالب
آشنایی با ساختار IIS قسمت دوم
در قسمت قبلی گفتیم که IIS از تعدادی کامپوننت تشکیل شده است و به یکی از آن‌ها به نام Http.sys پرداختیم. در این قسمت قصد داریم به WWW Services بپردازیم.

اجازه بدهید قبل از هر چیزی به دو مفهوم اصلی در IIS بپردزیم :

1. Worker Process

2. Application Pool

پروسه‌های کارگر  w3wp.exe  وظیفه‌ی اجرای برنامه‌های asp.net را در IIS ، به عهده دارند. این پروسه‌ها مسئولیت پردازش تمامی درخواست و پاسخ‌ها از/به کلاینت را دارند. هر کاری که باید در asp.net انجام بشود، توسط این‌ها صورت می‌گیرد. به بیان ساده‌تر این پروسه‌ها قلب برنامه‌های ASP.Net بر روی IIS هستند .

Application Pool:این پول‌ها در واقع ظرفی یا در برگیرنده ای برای پروسه‌های کارگر به حساب می‌آیند. این پول‌ها پروسه‌های کارگر را از هم جدا و دسته بندی می‌کنند تا قابلیت اعتماد، امنیت و در دسترس بودن بدهند. موقعی که یک پروسه یا حتی یک پول دچار مشکل می‌شود، این اطمینان داده می‌شود که تاثیری بر دیگر پول‌ها یا پروسه‌های کارگر، ندارد. یعنی موقعی که یک web application  دچار مشکل شود، هیچ تاثیری بر اجرای  web application‌های دیگر ندارد. به یک application pool با چند پروسه کارگر  web garden  میگویند. 


World Wide WebPublishing Services
یکی از قدیمی‌ترین امکانات موجود در IIS هست که از نسخه 7 به بعد، کار خود را با یک سروریس جدید به اسم Windows Process Activation Service یا به اختصار WAS که به صورت local system بر روی پروسه Svchost.exe با یک کد باینری یکسان اجرا می‌شود، شریک شده است. ممکن است در بعضی جاها WWW Service به صورت W3SVC هم نوشته شود.

اصلا این WWW Service چه کاری انجام می‌دهد و به چه دردی می‌خورد؟
این سرویس در سه بخش مهم  IIS 6 به فعالیت می‌پردازد:
  • HTTP administration and configuration
  •  Performance monitoring
  • Process management

HTTP Administration and Configuration 

سرویس WWW وظیفه خواندن اطلاعات پیکربندی IIS از متابیس را بر عهده دارد و از این اطلاعات خوانده شده برای پیکربندی و به روز کردن Http.sys استفاده می‌کند. به غیر از این کار، وظیفه آغاز و توقف و نظارت یا مانیتورینگ و همچنین مدیریت کامل پروسه‌های کارگر در زمینه http request را هم عهده دار است.

Performance Monitoring 
سرویس WWW بر کارآیی وب سایت‌ها و کش IIS نظارت می‌کند و البته یک شمارنده کارآیی performance counter هم ایجاد می‌کند. کار شمارنده کارآیی این است که اطلاعات یک سرویس یا سیستم عامل یا یک برنامه کاربردی را جمع آوری می‌کند تا به ما بگوید که این بخش‌ها به چه میزانی بهینه کار خود را انجام می‌دهند و به ما کمک می‌کنند که سیستم را به بهترین کارآیی برسانیم. سیستم عامل، شبکه و درایورها، داده‌های شمارشی را تهیه و در قالب یک سیستم نظارتی گرافیکی به کارشناس سیستم یا شبکه نشان می‌دهند. برنامه نویس‌ها هم از این طریق می‌توانند برنامه‌های خود را بنویسند که در اینجا لیستی از شمارنده‌ها در دانت نت را می‌توانید ببینید و بیشتر آن‌ها از طریق فضای نام system.diagnostic در دسترس هستند.

Process Management 
سرویس www مدیریت application pool و پروسه‌های کارگر را هم به عهده دارد. این مدیریت شامل شروع و توقف و بازیابی پروسه‌های کارگر می‌شود. به علاوه اینکه این سرویس کار نظارت بر صحت انجام عملیات پروسه‌های کارگر را هم جز وظایف خود می‌داند. وقتی که چندین بار کار پروسه‌های کارگر در یک دوره زمانی که در فایل پیکربندی مشخص شده با مشکل مواجه شود، از شروع یک پروسه کارگر دیگر جلوگیری می‌کند.

در نسخه‌های جدیدتر IIS چکاری بر عهده WWW Service است؟
در IIS7 به بعد، دیگر مدیریت پروسه‌های کارگر را به عهده ندارد؛ ولی به جای آن سمتی جدید را به اسم listener adapter، دریافت کرده است که یک listener adapter برای http listener یعنی Http.sys است. اصلی‌ترین وظیفه فعلی را که انجام می‌دهد پیکربندی Http.sys می‌باشد. موقعی که اطلاعات پیکربندی به روز می‌شوند باید این تغییرات بر روی Http.sys اعمال شوند. دومین وظیفه آن این است موقعی که درخواست جدیدی وارد صف درخواست‌ها می‌شود این مورد را به اطلاع WAS برساند.
WAS در قسمت سوم این مقاله توضیح داده خواهد شد.
مطالب
ذخیره تنظیمات متغیر مربوط به یک وب اپلیکیشن ASP.NET MVC با استفاده از EF
طی این  مقاله، نحوه‌ی ذخیره سازی تنظیمات متغیر و پویای یک برنامه را به صورت Strongly Typed ارائه خواهم داد. برای این منظور، یک API را که از Lazy Loading ، Cache ، Reflection و Entity Framework بهره میگیرد، خواهیم ساخت.
برنامه‌ی هدف ما که از این API استفاده می‌کند، یک اپلیکیشن Asp.net MVC است. قبل از شروع به ساخت API مورد نظر، یک دید کلی در مورد آنچه که قرار است در نهایت توسعه یابد، در زیر مشاهده میکنید:
public SettingsController(ISettings settings)
{
  // example of saving 
  _settings.General.SiteName = "دات نت تیپس";
  _settings.Seo.HomeMetaTitle = ".Net Tips";
  _settings.Seo.HomeMetaKeywords = "َAsp.net MVC,Entity Framework,Reflection";
  _settings.Seo.HomeMetaDescription = "ذخیره تنظیمات برنامه";
  _settings.Save();
}

همانطور که در کدهای بالا مشاهده میکنید، شی setting_ ما دارای دو پراپرتی فقط خواندنی بنام‌های General و Seo است که شامل  تنظیمات مورد نظر ما هستند و این دو کلاس از کلاس پایه‌ی SettingBase ارث بری کرده‌اند. دو دلیل برای انجام این کار وجود دارد:
  1. تنظیمات به صورت گروه بندی شده در کنار  هم قرار گرفته‌اند و یافتن تنظیمات برای زمانی که نیاز به دسترسی  به آنها داریم، راحت‌تر و ساده‌تر خواهد بود. 
  2. به این شکل تنظیمات قابل دسترس در یک گروه، از دیتابیس بازیابی خواهند شد.

اصلا چرا باید این تنظیمات را در دیتابیس ذخیره کنیم؟ 

شاید فکر کنید چرا باید تنظیمات را در دیتابیس ذخیره کنیم در حالی که فایل web.config در درسترس است و می‌توان توسط کلاس ConfigurationManager به اطلاعات آن دسترسی داشت.
جواب: دلیل این است که با تغییر فایل web.config، برنامه‌ی وب شما ری استارت خواهد شد (چه زمان‌هایی یک برنامه Asp.net ری استارت میشود).
برای جلوگیری از این مساله، راه حل مناسب برای ذخیره سازی اطلاعاتی که نیاز به تغییر در زمان اجرا دارند، استفاده از از دیتابیس می‌باشد. در این مقاله از Entity Framework و پایگاه داده Sql Sever استفاده می‌کنم.

مراحل ساخت Setting API مورد نظر به شرح زیر است:
  1. ساخت یک Asp.net Web Application 
  2. ساخت مدل Setting و افزودن آن به کانتکست Entity Framework 
  3. ساخت کلاس SettingBase برای بازیابی و ذخیره سازی تنظیمات با رفلکشن
  4. ساخت کلاس GenralSettins و SeoSettings که از کلاس SettingBase ارث بری کرده‌اند.
  5. ساخت کلاس Settings به منظور مدیریت تمام انواع تنظیمات 

یک برنامه‌ی Asp.Net Web Application را از نوع MVC ایجاد کنید. تا اینجا مرحله‌ی اول ما به پایان رسید؛ چرا که ویژوال استودیو کار‌های مورد نیاز ما را انجام خواهد داد.
 لازم است مدل خود را به ApplicationDbContext موجود در فایل IdentityModels.cs معرفی کنیم. به شکل زیر:
namespace DynamicSettingAPI.Models
{
    public interface IUnitOfWork
    {
        DbSet<Setting> Settings { get; set; }
        int SaveChanges();
    }
} 

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>,IUnitOfWork
    {
        public DbSet<Setting> Settings { get; set; }
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
        }

        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
    }


namespace DynamicSettingAPI.Models
{
    public class Setting
    {
        public string Name { get; set; }
        public string Type { get; set; }
        public string Value { get; set; }
    }
}
مدل تنظیمات ما خیلی ساده است و دارای سه پراپرتی به نام‌های Name ، Type ، Value هست که به ترتیب برای دریافت مقدار تنظیمات، نام کلاسی که از کلاس SettingBase ارث برده و نام تنظیمی که لازم داریم ذخیره کنیم، در نظر گرفته شده‌اند. 
لازم است تا متد OnModelCreating مربوط به ApplicationDbContext را نیز تحریف کنیم تا کانفیگ مربوط به مدل خود را نیز اعمال نمائیم.
 protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Setting>()
                    .HasKey(x => new { x.Name, x.Type });

            modelBuilder.Entity<Setting>()
                        .Property(x => x.Value)
                        .IsOptional();

            base.OnModelCreating(modelBuilder);
        }
ساختاری به شکل زیر مد نظر ماست:

  کلاس SettingBase ما همچین ساختاری را خواهد داشت:
namespace DynamicSettingAPI.Service
{
    public abstract class SettingsBase
    {
        //1
        private readonly string _name;
        private readonly PropertyInfo[] _properties;

        protected SettingsBase()
        {
            //2
            var type = GetType();
            _name = type.Name;
            _properties = type.GetProperties();
        }

        public virtual void Load(IUnitOfWork unitOfWork)
        {
            //3 get setting for this type name
            var settings = unitOfWork.Settings.Where(w => w.Type == _name).ToList();

            foreach (var propertyInfo in _properties)
            {
                //get the setting from setting list
                var setting = settings.SingleOrDefault(s => s.Name == propertyInfo.Name);
                if (setting != null)
                {
                    //4 set 
                    propertyInfo.SetValue(this, Convert.ChangeType(setting.Value, propertyInfo.PropertyType));
                }
            }
        }
        public virtual void Save(IUnitOfWork unitOfWork)
        {
            //5 get all setting for this type name
            var settings = unitOfWork.Settings.Where(w => w.Type == _name).ToList();

            foreach (var propertyInfo in _properties)
            {
                var propertyValue = propertyInfo.GetValue(this, null);
                var value = (propertyValue == null) ? null : propertyValue.ToString();

                var setting = settings.SingleOrDefault(s => s.Name == propertyInfo.Name);
                if (setting != null)
                {
                    // 6 update existing value
                    setting.Value = value;
                }
                else
                {
                    // 7 create new setting
                    var newSetting = new Setting()
                    {
                        Name = propertyInfo.Name,
                        Type = _name,
                        Value = value,
                    };
                    unitOfWork.Settings.Add(newSetting);
                }
            }
        }
    }
}
این کلاس قرار است توسط کلاس‌های تنظیمات ما به ارث برده شود و در واقع کارهای مربوط به رفلکشن را در این کلاس کپسوله کرده‌ایم. همانطور که مشخص است ما دو فیلد را به نام‌های name_ و properties_ به صورت فقط خواندنی در نظر گرفته ایم که نام کلاس مورد نظر ما که از این کلاس به ارث خواهد برد، به همراه پراپرتی‌های آن، در این ظرف‌ها قرار خواهند گرفت.
متد Load وظیفه‌ی واکشی تمام تنظیمات مربوط به Type و ست کردن مقادیر به دست آمده را به خصوصیات کلاس ما، برعهده دارد. کد زیر مقدار دریافتی از دیتابیس را به نوع داده پراپرتی مورد نظر تبدیل کرده و نتیجه را به عنوان Value پراپرتی ست میکند. 
propertyInfo.SetValue(this, Convert.ChangeType(setting.Value, propertyInfo.PropertyType));
متد Save نیز وظیفه‌ی ذخیره سازی مقادیر موجود در خصوصیات کلاس تنظیماتی را که از کلاس SettingBase ما به ارث برده است، به عهده دارد. 
این متد دیتا‌های موجود دردیتابیس را که متعلق به کلاس ارث برده مورد نظر ما هستند، واکشی میکند و در یک حلقه، اگر خصوصیتی در دیتابیس موجود بود، آن را ویرایش کرده وگرنه یک رکورد جدید را ثبت میکند.

  کلاس‌های تنظیمات شخصی سازی شده خود را به شکل زیر تعریف میکنیم :
  public class GeneralSettings : SettingsBase
    {
        public string SiteName { get; set; }
        public string AdminEmail { get; set; }
        public bool RegisterUsersEnabled { get; set; }
    }

 public class GeneralSettings : SettingsBase
    {
        public string SiteName { get; set; }
        public string AdminEmail { get; set; }
    }
نیازی به توضیح ندارد.
برای اینکه تنظیمات را به صورت یکجا داشته باشیم و Abstraction ای را برای استفاده از این API ارائه دهیم، یک اینترفیس و یک کلاس که اینترفیس مذکور را پیاده کرده است در نظر میگیریم: 
public interface ISettings
{
    GeneralSettings General { get; }
    SeoSettings Seo { get; }
    void Save();
}

public class Settings : ISettings
{
    // 1
    private readonly Lazy<GeneralSettings> _generalSettings;
    // 2
    public GeneralSettings General { get { return _generalSettings.Value; } }

    private readonly Lazy<SeoSettings> _seoSettings;
    public SeoSettings Seo { get { return _seoSettings.Value; } }

    private readonly IUnitOfWork _unitOfWork;
    public Settings(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
        // 3
        _generalSettings = new Lazy<GeneralSettings>(CreateSettings<GeneralSettings>);
        _seoSettings = new Lazy<SeoSettings>(CreateSettings<SeoSettings>);
    }

    public void Save()
    {
        // only save changes to settings that have been loaded
        if (_generalSettings.IsValueCreated)
            _generalSettings.Value.Save(_unitOfWork);

        if (_seoSettings.IsValueCreated)
            _seoSettings.Value.Save(_unitOfWork);

        _unitOfWork.SaveChanges();
    }
    // 4
    private T CreateSettings<T>() where T : SettingsBase, new()
    {
        var settings = new T();
        settings.Load(_unitOfWork);
        return settings;
    }
}
این اینترفیس مشخص می‌کند که ما به چه نوع تنظیماتی، دسترسی داریم و متد Save آن برای آپدیت کردن تنظیمات، در نظر گرفته شده است. هر کلاسی که از کلاس SettingBase ارث بری کرده را به صورت فیلد فقط خواندنی و با استفاده از کلاس Lazy درون آن ذکر میکنیم و به این صورت کلاس تنظیمات ما زمانی ساخته خواهد شد که برای اولین بار به آن دسترسی داشته باشیم.
متد CreateSetting وظیفه‌ی لود دیتا را از دیتابیس، بر عهده دارد که برای این منظور، متد لود Type مورد نظر را فراخوانی میکند. این متد وقتی به کلاس تنظیمات مورد نظر برای اولین بار دسترسی پیدا کنیم، فراخوانی خواهد شد.

 حتما امکان این وجود دارد که شما از امکان Caching هم بهره ببرید برای مثال همچین متد و سازنده‌ای را در کلاس Settings در نظر بگیرید:
private readonly ICache _cache;
public Settings(IUnitOfWork unitOfWork, ICache cache)
{
    // ARGUMENT CHECKING SKIPPED FOR BREVITY
    _unitOfWork = unitOfWork;
    _cache = cache;
    _generalSettings = new Lazy<GeneralSettings>(CreateSettingsWithCache<GeneralSettings>);
    _seoSettings = new Lazy<SeoSettings>(CreateSettingsWithCache<SeoSettings>);
}

private T CreateSettingsWithCache<T>() where T : SettingsBase, new()
{
    // this is where you would implement loading from ICache
    throw new NotImplementedException();
}
در آخر هم به شکل زیر میتوان (به عنوان دمو فقط ) از این API استفاده کرد.
   public ActionResult Index()
        {
            using (var uow = new ApplicationDbContext())
            {
                var _settings = new Settings(uow);
                _settings.General.SiteName = "دات نت تیپس";
                _settings.General.AdminEmail = "admin@gmail.com";
                _settings.General.RegisterUsersEnabled = true;
                _settings.Seo.HomeMetaTitle = ".Net Tips";
                _settings.Seo.MetaKeywords = "Asp.net MVC,Entity Framework,Reflection";
                _settings.Seo.HomeMetaDescription = "ذخیره تنظیمات برنامه";

                var settings2 = new Settings(uow);
                var output = string.Format("SiteName: {0} HomeMetaDescription: {1}  MetaKeywords:  {2}  MetaTitle:  {3}  RegisterEnable:  {4}",
                    settings2.General.SiteName,
                    settings2.Seo.HomeMetaDescription,
                    settings2.Seo.MetaKeywords,
                    settings2.Seo.HomeMetaTitle,
                    settings2.General.RegisterUsersEnabled.ToString()
                    );
                return Content(output);
            }

        }

خروجی :

نکته: در پروژه ای که جدیدا در سایت ارائه داده‌ام و در حال تکمیل آن هستم، از بهبود یافته‌ی این مقاله استفاده می‌شود. حتی برای اسلاید شو‌های سایت هم میشود از این روش استفاده کرد و از فرمت json بهره برد برای این منظور. حتما در پروژه‌ی مذکور همچین امکانی را هم در نظر خواهم گرفتم.
پیشنها میکنم سورس SmartStore را بررسی کنید. آن هم به شکل مشابهی ولی پیشرفته‌تر از این مقاله، همچین امکانی را دارد.
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 14 - فعال سازی اعتبارسنجی ورودی‌های کاربران
مباحث پایه‌ای اعتبارسنجی کاربران در ASP.NET Core با نگارش‌های پیشین ASP.NET MVC، آنچنان تفاوت ساختاری مهمی ندارند و یکی هستند. عمده‌ی تفاوت‌ها در نحوه‌ی برپایی تنظیمات اولیه‌ی اسکریپت‌های آن‌ها و همچنین یک سری Tag Helper جدید معادل با HTML Helperهای متداول اعتبارسنجی هستند.


دریافت وابستگی‌های سمت کاربر مباحث اعتبارسنجی

زمانیکه گزینه‌ی ایجاد یک پروژه‌ی جدید ASP.NET Core را در VS.NET انتخاب می‌کنیم، علاوه بر قالب empty آن، قالب دیگری به نام web application نیز در آن موجود است. با انتخاب این قالب، فایلی را به نام bower.json نیز با این محتوا مشاهده می‌کنید:
 {
  "name": "asp.net",
  "private": true,
  "dependencies": {
"bootstrap": "3.3.6",
"jquery": "2.2.0",
"jquery-validation": "1.14.0",
"jquery-validation-unobtrusive": "3.2.6"
  }
}
این مداخل تمام پیشنیازهای مباحث اعتبارسنجی کاربران را به صورت خودکار به پروژه اضافه می‌کنند. بنابراین افزودن فایل جدید bower.json به پروژه با محتوای فوق و سپس مراجعه به solution explorer و کلیک راست بر روی گره dependencies و در آخر انتخاب restore packages، سبب دریافت خودکار این بسته‌ها می‌شود.


این بسته‌ها را پس از دریافت، در پوشه‌ی bower_components خواهید یافت:


البته باید دقت داشت که استفاده از bower در اینجا الزامی نیست. اگر علاقمند بودید از npm و node.js استفاده کنید.


افزودن وابستگی‌های سمت کاربر مباحث اعتبارسنجی و عمومی کردن آن‌ها

پس از دریافت وابستگی‌های مورد نیاز توسط bower، به فایل layout برنامه مراجعه کرده و سپس آن‌ها را به ترتیب ذیل اضافه می‌کنیم:
    <script src="~/bower_components/jquery/dist/jquery.min.js"></script>
    <script src="~/bower_components/jquery-validation/dist/jquery.validate.min.js"></script>
    <script src="~/bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
 
    @RenderSection("scripts", required: false)
</body>
</html>
هرچند این ترتیب درست است، اما ... کار نمی‌کند. علت را هم در مبحث «ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایل‌های استاتیک» بررسی کردیم: تمام پوشه‌های موجود در ریشه‌ی یک برنامه‌ی ASP.NET Core، غیرعمومی هستند (و توسط کاربران قابل درخواست نیستند)، مگر اینکه آن‌ها‌را توسط میان افزار static files، عمومی کنیم. برای این منظور به کلاس آغازین برنامه مراجعه کرده و سپس به متد Configure آن، تنظیمات ذیل را اضافه کنید:
// Serve wwwroot as root
app.UseFileServer();
 
// Serve /bower_components as a separate root
app.UseFileServer(new FileServerOptions
{
    // Set root of file server
    FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "bower_components")),
    // Only react to requests that match this path
    RequestPath = "/bower_components",
    // Don't expose file system
    EnableDirectoryBrowsing = false
});
به این ترتیب فایل‌های موجود در پوشه‌ی bower_components به مسیر bower_components/ نگاشت می‌شوند. البته انتخاب مقدار RequestPath در اینجا اختیاری است و برای مثال می‌توانید مسیر scripts/ را نیز معرفی کنید (نگاشت مسیر فیزیکی و واقعی فایل‌ها به URL خاص دیگری که توسط وب سرور ارائه خواهد شد).
اگر RequestPath را به مسیر دیگری تنظیم کردید، نیاز است ابتدای سه مدخل ذکر شده را بر این اساس اصلاح کنید، تا فایل‌ها توسط وب سرور قابل ارائه شوند.


استفاده از CDN برای توزیع اسکریپت‌های اعتبارسنجی مورد نیاز

در مورد environment tag helper در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 12 - معرفی Tag Helpers» پیشتر بحث شد. در اینجا نیز می‌توان برای مثال در حال توسعه، از اسکریپت‌های محلی
<environment name="Development">
   <script src="~/bower_components/jquery/dist/jquery.min.js"></script>
   <script src="~/bower_components/jquery-validation/dist/jquery.validate.min.js"></script>
   <script src="~/bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"></script>
</environment>
و در حالت نهایی توزیع برنامه، از CDNهای ارائه‌ی اسکریپت‌ها، جهت کاهش بار سرور استفاده کرد:
<environment names="Staging, Production">
   <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.4.min.js"
asp-fallback-src="/bower_components/jquery/dist/jquery.min.js"
asp-fallback-test="window.jQuery">
  </script>
   <script src="https://ajax.aspnetcdn.com/ajax/jquery.validate/1.14.0/jquery.validate.min.js"
asp-fallback-src="bower_components/jquery-validation/dist/jquery.validate.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator">
  </script>
   <script src="https://ajax.aspnetcdn.com/ajax/mvc/5.2.3/jquery.validate.unobtrusive.min.js"
asp-fallback-src="/bower_components/jquery-validation-unobtrusive/jquery.validate.unobtrusive.min.js"
asp-fallback-test="window.jQuery && window.jQuery.validator && window.jQuery.validator.unobtrusive">
  </script>
</environment>
در اینجا fallback به آدرس محلی دریافت اسکریپت‌های مدنظر، در صورت در دسترس نبودن CDN، تنظیم شده‌است.
روش عملکرد fallback هم به این صورت است که بررسی می‌شود آیا عبارت ذکر شده‌ی در قسمت asp-fallback-test قابل اجرا است یا خیر؟ اگر خیر، یعنی CDN قابل دسترسی نیست و از نمونه‌ی محلی استفاده می‌کند.


خلاصه‌ای از Tag helpers اعتبارسنجی

در جدول «راهنمای تبدیل HTML Helpers به Tag Helpers» مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 12 - معرفی Tag Helpers»، معادل‌های HTML Helpers مباحث اعتبارسنجی را نیز ملاحظه کردید. خلاصه‌ی تکمیلی آن به صورت ذیل است:

ValidationSummary.All سبب نمایش خطاهای اعتبارسنجی خواص و همچنین کل مدل می‌شود:
@Html.ValidationSummary(false)
معادل است با
<div asp-validation-summary="All"></div>

ValidationSummary.ModelOnly صرفا خطاهای اعتبارسنجی در سطح مدل را نمایش می‌دهد:
@Html.ValidationSummary(true)
معادل است با
<div asp-validation-summary="ModelOnly"></div>

و برای تعیین نمایش خطاهای اعتبارسنجی یک خاصیت از مدل:
@Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" })
معادل است با
<span asp-validation-for="UserName" class="text-danger"></span>
مطالب
آشنایی با CLR: قسمت نهم
net framework. شامل Framework Class Library یا به اختصار FCL است. FCL مجموعه‌ای از dll اسمبلی‌هایی است که صدها و هزاران نوع در آن تعریف شده‌اند و هر نوع تعدادی کار انجام می‌دهد. همچنین مایکروسافت کتابخانه‌های اضافه‌تری را چون azure و Directx نیز ارائه کرده است که باز هر کدام شامل نوع‌های زیادی می‌شوند. این کتابخانه به طور شگفت آوری باعث سرعت و راحتی توسعه دهندگان در زمینه فناوری‌های مایکروسافت گشته است.

تعدادی از فناوری‌هایی که توسط این کتابخانه پشتیبانی می‌شوند در زیر آمده است:

Web Service: این فناوری اجازه‌ی ارسال و دریافت پیام‌های تحت شبکه را به خصوص بر روی اینترنت، فراهم می‌کند و باعث ارتباط جامع‌تر بین برنامه‌ها و فناوری‌های مختلف می‌گردد. در انواع جدیدتر WCF و Web Api نیز به بازار ارائه شده‌اند.

webform و MVC : فناوری‌های تحت وب که باعث سهولت در ساخت وب سایت‌ها می‌شوند که وب فرم رفته رفته به سمت منسوخ شدن پیش می‌رود و در صورتی که قصد دارید طراحی وب را آغاز کنید توصیه میکنم از همان اول به سمت MVC بروید.

Rich Windows GUI Application : برای سهولت در ایجاد برنامه‌های تحت وب حالا چه با فناوری WPF یا فناوری قدیمی و البته منسوخ شده Windows Form.
Windows Console Application: برای ایجاد برنامه‌های ساده و بدون رابط گرافیکی.
Windows Services: شما می‌توانید یک یا چند سرویس تحت ویندوز را که توسط Service Control Manager یا به اختصار SCM کنترل می‌شوند، تولید کنید.
Database stored Procedure: نوشتن stored procedure بر روی دیتابیس‌هایی چون sql server و اوراکل و ... توسط فریم ورک دات نت مهیاست.
Component Libraray: ساخت اسمبلی‌های واحدی که می‌توانند با انواع مختلفی از موارد بالا ارتباط برقرار کنند.
Portable Class Libary : این نوع پروژه‌ها شما را قادر می‌سازد تا کلاس‌هایی با قابلیت انتقال پذیری برای استفاده در سیلور لایت، ویندوز فون و ایکس باکس و فروشگاه ویندوز و ... تولید کنید.

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

در CLR مفهومی به نام Common Type System یا CTS وجود دارد که توضیح می‌دهد نوع‌ها باید چگونه تعریف شوند و چگونه باید رفتار کنند که این قوانین از آنجایی که در ریشه‌ی CLR نهفته است، بین تمامی زبان‌های دات نت مشترک می‌باشد. تعدادی از مشخصات این CTS در زیر آورده شده است ولی در آینده بررسی بیشتری روی آنان خواهیم داشت:

  • فیلد
  • متد
  • پراپرتی
  • رویدادها

CTS همچنین شامل قوانین زیادی در مورد وضعیت کپسوله سازی برای اعضای یک نوع دارد:

  • private
  • public
  • Family یا در زبان‌هایی مثل سی ++ و سی شارپ با نام protected شناخته می‌شود.
  • family and assembly: این هم مثل بالایی است ولی کلاس مشتق شده باید در همان اسمبلی باشد. در زبا‌ن‌هایی چون سی شارپ و ویژوال بیسیک، چنین امکانی پیاده سازی نشده‌است و دسترسی به آن ممکن نیست ولی در IL Assembly چنین قابلیتی وجود دارد.
  • Assembly یا در بعضی زبان‌ها به نام internal شناخته می‌شود.
  • Family Or Assembly: که در سی شارپ با نوع Protected internal شناخته می‌شود. در این وضعیت هر عضوی در هر اسمبلی قابل ارث بری است و یک عضو فقط می‌تواند در همان اسمبلی مورد استفاده قرار بگیرد.

موارد دیگری که تحت قوانین CTS هستند مفاهیم ارث بری، متدهای مجازی، عمر اشیاء و .. است.

یکی دیگر از ویژگی‌های CTS این است که همه‌ی نوع‌ها از نوع شیء Object که در فضای نام system قرار دارد ارث بری کرده‌اند. به همین دلیل همه‌ی نوع‌ها حداقل قابلیت‌هایی را که یک نوع object ارئه میدهد، دارند که به شرح زیر هستند:

  • مقایسه‌ی دو شیء از لحاظ برابری.
  • به دست آوردن هش کد برای هر نمونه از یک شیء
  • ارائه‌ای از وضعیت شیء به صورت رشته ای
  • دریافت نوع شیء جاری
CLS
وجود COM‌ها به دلیل ایجاد اشیاء در یک زبان متفاوت بود تا با زبان دیگر ارتباط برقرار کنند. در طرف دیگر CLR هم بین زبان‌های برنامه نویسی یکپارچگی ایجاد کرده است. یکپارچگی زبان‌های برنامه نویسی علل زیادی دارند. اول اینکه رسیدن به هدف یا یک الگوریتم خاص در زبان دیگر راحت‌تر از زبان پایه پروژه است. دوم در یک کار تیمی که افراد مختلف با دانش متفاوتی حضور دارند و ممکن است زیان هر یک متفاوت باشند.
برای ایجاد این یکپارچگی، مایکروسافت سیستم CLS یا Common Language Specification را راه اندازی کرد. این سیستم برای تولیدکنندگان کامپایلرها جزئیاتی را تعریف می‌کند که کامپایلر آن‌ها را باید با حداقل ویژگی‌های تعریف شده‌ی CLR، پشتیبانی کند.


CLR/CTS مجموعه‌ای از ویژگی‌ها را شامل می‌شود و گفتیم که هر زبانی بسیاری از این ویژگی‌ها را پشتیبانی می‌کند ولی نه کامل. به عنوان مثال برنامه نویسی که قصد کرده از IL Assembly استفاده کند، قادر است از تمامی این ویژگی‌هایی که CLR/CTS ارائه می‌دهند، استفاده کند ولی تعدادی دیگر از زبان‌ها مثل سی شارپ و فورترن و ویژوال بیسیک تنها بخشی از آن را استفاده می‌کنند و CLS حداقل ویژگی که بین همه این زبان‌ها مشترک است را ارائه می‌کند.
شکل زیر را نگاه کنید:

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

به کد زیر دقت کنید. تعدادی از کدها سازگاری کامل با CLS دارند که به آن‌ها CLS Compliant گویند و تعدادی از آن‌ها non-CLS-Compliant هستند یعنی با CLS سازگاری ندارند ولی استفاده از خاصیت [(assembly: CLSCompliant(true]  باعث می‌شود که تا کامپایلر از پشتیبانی و سازگاری این کدها اطمینان کسب کند و در صورت وجود، از اجرای آن جلوگیری کند. با کمپایل کد زیر دو اخطار به ما میرسد.
using System;

// Tell compiler to check for CLS compliance
[assembly: CLSCompliant(true)]

namespace SomeLibrary {

// Warnings appear because the class is public
public sealed class SomeLibraryType {

// Warning: Return type of 'SomeLibrary.SomeLibraryType.Abc()'
// is not CLS­compliant
public UInt32 Abc() { return 0; }

// Warning: Identifier 'SomeLibrary.SomeLibraryType.abc()'
// differing only in case is not CLS­compliant
public void abc() { }

// No warning: this method is private
private UInt32 ABC() { return 0; }
}
}

اولین اخطار اینکه یکی از متدها یک عدد صحیح بدون علامت unsigned integer را بر می‌گرداند که همه‌ی زبان‌ها آن را پشتیبانی نمی‌کنند و خاص بعضی از زبان هاست.
دومین اخطار اینکه دو متد یکسان وجود دارند که در حروف بزرگ و کوچک تفاوت دارند. ولی زبان هایی چون ویژوال بیسیک نمی‌توانند تفاوتی بین دو متد abc و ABC بیابند.

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

سازگاری با کدهای مدیریت نشده
در بالا در مورد یکپارچگی و سازگاری کدهای مدیریت شده توسط CLS صحبت کردیم ولی در مورد ارتباط با کدهای مدیریت نشده چطور؟
مایکروسافت موقعیکه CLR را ارئه کرد، متوجه این قضیه بود که بسیاری از شرکت‌ها توانایی اینکه کدهای خودشون را مجددا طراحی و پیاده سازی کنند، ندارند و خوب، سورس‌های مدیریت نشده‌ی زیادی هم موجود هست که توسعه دهندگان علاقه زیادی به استفاده از آن‌ها دارند. در نتیجه مایکروسافت طرحی را ریخت که CLR هر دو قسمت کدهای مدیریت شده و نشده را پشتیبانی کند. دو نمونه از این پشتیبانی را در زیر بیان می‌کنیم:
یک. کدهای مدیریت شده می‌توانند توابع مدیریت شده را در قالب یک dll صدا زده و از آن‌ها استفاده کنند.
دو. کدهای مدیریت شده می‌توانند از کامپوننت‌های COM استفاده کنند: بسیاری از شرکت‌ها از قبل بسیاری از کامپوننت‌های COM را ایجاد کرده بودند که کدهای مدیریت شده با راحتی با آن‌ها ارتباط برقرار می‌کنند. ولی اگر دوست دارید روی آن‌ها کنترل بیشتری داشته باشید و آن کدها را به معادل CLR تبدیل کنید؛ میتوانید از ابزار کمکی که مایکروسافت همراه فریم ورک دات نت ارائه کرده است استفاده کنید. نام این ابزار TLBIMP.exe می‌باشد که از Type Library Importer گرفته شده است.

سه. اگر کدهای مدیریت نشده‌ی زیادتری دارید شاید راحت‌تر باشد که برعکس کار کنید و کدهای مدیریت شده را در در یک برنامه‌ی مدیریت نشده اجرا کنید. این کدها می‌توانند برای مثال به یک Activex یا shell Extension تبدیل شده و مورد استفاده قرار گیرند. ابزارهای TLBEXP .exe و RegAsm .exe برای این منظور به همراه فریم ورک دات نت عرضه شده اند.
سورس کد Type Library Importer را میتوانید در کدپلکس بیابید.
در ویندوز 8 به بعد مایکروسافت API جدید را تحت عنوان WinsowsRuntime یا winRT ارائه کرده است . این api یک سیستم داخلی را  از طریق کامپوننت‌های com ایجاد کرده و به جای استفاده از فایل‌های کتابخانه‌ای، کامپوننت‌ها api هایشان را از طریق متادیتاهایی بر اساس استاندارد ECMA که توسط تیم دات نت طراحی شده است معرفی می‌کنند.
زیبایی این روش اینست که کد نوشته شده در زبان‌های دات نت  می‌تواند به طور مداوم با api‌های winrt ارتباط برقرار کند. یعنی همه‌ی کارها توسط CLR انجام می‌گیرد بدون اینکه لازم باشد از ابزار اضافی استفاده کنید. در آینده در مورد winRT بیشتر صحبت می‌کنیم.
 
سخن پایانی: ممنون از دوستان عزیز بابت پیگیری مطالب تا بدینجا. تا این قسمت فصل اول کتاب با عنوان اصول اولیه CLR بخش اول مدل اجرای CLR به پایان رسید.
ادامه‌ی مطالب بعد از تکمیل هر بخش در دسترس دوستان قرار خواهد گرفت.
مطالب
چرخه‌ی حیات یک درخواست در ASP.NET MVC

در Asp.net دو چرخه‌ی حیات مهم وجود دارند که اساس چارچوب MVC را تشکیل می‌دهند :

  1. چرخه‌ی حیات برنامه (Application)؛ از لحظه‌ای که برنامه برای اولین بار اجرا می‌شود و تا لحظه‌ی خاتمه‌ی آن را شامل می‌شود.
  2. چرخه‌ی حیات یک درخواست ( Request مسیری که یک درخواست طی می‌کند، اصطلاحا PipeLine نامیده می‌شود که همان چرخه‌ی حیات یک درخواست نیز هست و از لحظه‌ای که درخواست تحویل asp.net شده، تا زمانیکه درخواست ارسال می‌شود را شامل می‌شود.

تمرکز بنده بیشتر بر روی روند و مسیری است که یک درخواست طی می‌کند و قصد دارم با بهره گیری از کتاب Pro Asp.net Mvc 5 و دیگر منابع، چرخه‌ی حیات درخواست را در برنامه‌های Mvc بررسی کرده و در مقالات آتی ماژولها و هندلرها را بررسی کنم.

در asp.net ، برنامه global فایلهای شامل دو فایل Global.asax , Global.asax.cs است.

فایل Global.asax که هیچ گاه نیاز به ویرایش آن نداریم محتویاتی مانند زیر دارد:

<%@ Application Codebehind="Global.asax.cs" Inherits="YourAppName.MvcApplication" Language="C#" %>
و صرفا فایل code behind   مرتبط را برای asp.net مشخص می‌کند. این نوع مشخص سازی را از وب فرمها به یاد داریم.

در این مقاله منظور از فایل global فایل  Global.asax.cs است که مشتق شده از کلاس System.Web.HttpApplication است:

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            ...//
        }
    }
به صورت پیش فرض کدهای بالا ایجاد شده و کلاس MvcApplication شامل متد Application_Start است که نقطه‌ی شروع چرخه‌ی حیات برنامه را مشخص می‌کند. اما متد دیگری به نام Application_End() نیز وج ود دارد که در زمان خاتمه‌ی برنامه فراخوانی خواهد شد و فرصتی را برای آزاد سازی منابع اشغال شده توسط برنامه فراهم می‌آورد .

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

  BeginRequest : به عنوان اولین رویداد، به محض وصول یک درخواست جدید رخ خواهد داد. 

  AuthenticateRequest ,PostAuthenticateRequest : رویداد AuthenticateRequest برای شناسایی کاربر ارسال کننده درخواست، کاربرد دارد و پس از پردازش کلیه‌ی توابع، رویداد PostAuthenticateRequest صدا زده می‌شود.

  AuthorizeRequest :به‌هنگام صدور مجوزهای یک درخواست رخ می‌دهد و مشابه رویداد بالا پس از پردازش کلیه‌ی توابع، رویداد PostAuthorizeRequest صدا زده خواهد شد.  

ResolveRequestCache : پس از صدور مجوزهای یک درخواست در رویداد authorization  زمانیکه ماژولهای کش می‌خواهند اطلاعاتی را از کش سرور مطالبه کنند، رخ می‌دهد و به مانند دو رخداد قبلی، PostResolveRequestCache نیز پس از اتمام پردازش توابع رویداد رخ می‌دهد.

  MapRequestHandler : زمانی که Asp.net می‌خواهد هندلری را برای پاسخگویی به درخواست واصله انتخاب کند رخ می‌دهد و PostMapRequestHandler نیز پس از این انتخاب، تریگر می‌شود.  

AcquireRequestState : جهت بدست آوردن داده‌هایی نظیر سشن و ... مرتبط با درخواست جاری کاربرد داشته و PostAcquireRequestState نیز پس از پردازش توابع رویداد رخ خواهد داد.

  PreRequestHandlerExecute : بالافاصله قبل و همچنین بلافاصله بعد از این که یک هندلر بخواهد درخواستی را پردازش کند، رخ می‌دهد. PostRequestHandlerExecute نیز همانند دیگر رویدادهای گذشته، پس از اتمام پردازش توابع، این رویداد رخ خواهد داد. 

ReleaseRequestState : زمانی رخ می‌دهد که داده‌های مرتبط با درخواست جاری، در ادامه‌ی روند پردازش درخواست مورد نیاز نباشند و پس از پردازش توابع رویداد، PostReleaseRequestState رخ خواهد داد .

UpdateRequestCache : به جهت اینکه ماژولهای مسئول کش، توانایی به روز رسانی داده‌های خود، برای پاسخگویی به درخواستهای بعدی را داشته باشند، این رویداد رخ می‌دهد.

  LogRequest : قبل از انجام عملیات لاگین برای درخواست جاری رخ می‌دهد و پس از پردازش توابع رویداد نیز PostLogRequest تریگر می‌شود.  

  EndRequest : پس از پایان کار پردازش درخواست جاری و مهیا شدن پاسخ مرتبط جهت ارسال به مرورگر تریگر خواهد شد. 

PreSendRequestHeaders : قبل از ارسال HTTP headers به مرورگر این رویداد رخ خواهد داد.

  PreSendRequestContent : بعد از ارسال شدن هدرها و قبل از ارسال محتوای صفحه به مرورگر رخ می‌دهد. 

Error : هر زمان و در هر مرحله از پردازش درخواست، چنانچه خطایی صورت پذیرد این رویداد رخ خواهد داد.

فریم ورک Asp.net   جهت مدیریت بهتر یک درخواست، در تمام مسیر پردازش، رویدادهای بالا را مهیا کرده است. در ادامه نحوه‌ی هندل کردن رویدادهای چرخه‌ی حیات درخواست را در فایل global توضیح می‌دهم. هر چند که استفاده از این فایل بدین منظور، صرفا برای مدیریت مسائل ابتدایی مناسب بوده و در یک پروژه‌ی بزرگ موجب به هم ریختگی فایل global با کدهای زیاد و خوانایی پایین بوده که قابلیت استفاده مجدد در دیگر پروژه‌ها را نیز ندارد.

Asp.net این مشکل را با معرفی ماژولها که در مقالات آتی توضیح خواهم داد، مرتفع کرده است.

در فایل global هر گاه متدی را با پیشوند Application_ و نام یکی از رویدادهای بالا بنویسید Asp.net آن را به عنوان هندلری برای رویداد مذکور می‌شناسد. به عنوان مثال متدی با نام Application_BeginRequest  متد رویداد BeginRequest می‌باشد.

ابتدا یک پروژه‌ی MVC جدید را به نام SimpleApp ایجاد کرده و فایل global آن را مطابق ذیل تغییر می‌دهیم:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace SimpleApp
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        { 
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
        protected void Application_BeginRequest() 
        {
            RecordEvent("BeginRequest");
        }
        protected void Application_AuthenticateRequest() 
        {
            RecordEvent("AuthenticateRequest");
        }
        protected void Application_PostAuthenticateRequest()
        { 
            RecordEvent("PostAuthenticateRequest");
        }
        private void RecordEvent(string name)
        {
            List<string> eventList = Application["events"] as List<string>;
            if (eventList == null) 
            { 
                Application["events"] = eventList = new List<string>(); 
            } 
            eventList.Add(name);
        }
    }
}

در اینجا متدی به نام RecordEvent را در کدهای ذکر شده مشاهده می‌کنید که نام یک رویداد را دریافت و جهت در دسترس قرار دادن در کل برنامه به خاصیت Application از کلاس HttpApplication نسبت داده و متد مذکور را از سه متد دیگر فراخوانی کرده‌ایم. این متدها در زمان رخ دادن رویدادهای BeginRequest, AuthenticateRequest, PostAuthenticateRequest صدا زده خواهند شد.

حال جهت نمایش اطلاعات رویداد نیاز است تغییراتی مشابه ذیل در کنترلر Home ایجاد نماییم.

using System.Web.Mvc;
namespace SimpleApp.Controllers
{
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(HttpContext.Application["events"]);
        }

    }
}

ویوی مرتبط با اکشن متد index را مطابق کدهای ذیل بازنویسی می‌کنیم:

@model List<string>
@{
    ViewBag.Title = "Events List";
 
}
<h5>Events</h5>
<table>
    @foreach (string eventName in Model)
    {    
        <tr>
            <td>@eventName</td>
        </tr>      
    }

</table>
خروجی آن مطابق ذیل خواهد بود :

در این مقاله سعی کردیم ابتدا چرخه‌ی حیات یک Request را فرا گرفته و سپس از طریق فایل global و توسط متدهایی با پیشوند Application_ +نام رویداد (اصطلاحا متدهای ویژه نامیده می‌شوند) چرخه حیات یک درخواست را مدیریت کنیم.
اشتراک‌ها
بومی سازی در MVC 6

This article shows some of the ways in which localization can be used in a MVC 6 ASP.NET 5 application.

بومی سازی در MVC 6