معرفی ماژول ASP.Net Async OutputCache

در این مقاله ، نحوه استفاده و پیاده سازی ماژول ASP.Net Async OutputCache را خواهیم آموخت. این ویژگی در نسخه ی .NET Framework 4.6.2 اضافه شده است و امکانات زیادی را برای کاربران فراهم می کند.

معرفی ماژول ASP.Net Async OutputCache

OutputCacheModule ، یکی از handler های پیشفرض ASP.NET است که برای ذخیره سازی خروجی های تولید شده از صفحات، کنترل ها و درخواست های HTTP به کار می رود. این محتویات ، سپس می توانند دوباره برای بهبود کارایی مورد استفاده قرار بگیرند. قبلا در .NET Framework 4.6.2 ، پشتیبانی برای  async read/write در OutputCache Module وجود نداشت. 

با انتشار نسخه ی  .NET Framework 4.6.2 ، شرکت Microsoft یک OutputCache async provider abstract class به نام OutputCacheProviderAsync ارائه کرد که interface هایی را برای async OutputCache provider فراهم می کند که دسترسی asynchronous  را به یک shared OutputCache فعال می کند. ماژول Async OutputCache که این interface ها را پشتیبانی می کند، بر روی NuGet package قرار گرفته است که شما می توانید از آن استفاده نمایید. 

مزایای Async OutputCache Module

تمامی مزایادر قابلیت مقیاس پذیری نهفته هستند. cloud  محاسبه ی computing resource ها را برای پشتیبانی از فضاهای وسیع در درخواست های برنامه مهیا می کند. زمانی که شما قابلیت مقیاس پذیری یک OutputCache برایتان مهم است، نمی توانید از in-memory provider  استفاده کنید زیرا in-memory provider  به شما اجازه نمی دهد که داده ها را بین چندین وب سرور به اشتراک بگذارید. 

شما نیاز خواهید داشت که داده های OutputCache  را در یک رسانه ی ذخیره سازی مانند Microsoft Azure SQL Database, NoSQL,  و یا Redis Cache ذخیره سازی کنید. در حال حاضر، تعامل OutputCache  با این گونه رسانه های ذخیره سازی محدود به اجرای همزمان آن ها است. با این به روز رسانی، ماژول جدید async OutputCache شما را قادر می سازد تا داده ها را به صورت غیرهمزمان از provider های ذخیره سازی بخوانید و بنویسید. عملیات Async I/O به شما کمک می کنند تا thread ها را سریع تر از حالت همزمان اجرا کنید ، که این کار سبب می شود سایر درخواست ها سریع تر توسط ASP.NET مدیریت شوند. 

چگونه از ماژول Async OutputCache  استفاده کنیم؟

1-نسخه ی برنامه را بر روی 4.6.2 قرار دهید.

اینترفیس OutputCacheProviderAsync  در .NET Framework 4.6.2 ارائه شد ، بنابراین شما نیاز دارید تا در برنامه تان از  .NET Framework 4.6.2 و یا نسخه های بالاتر استفاده کنید تا بتوانید این ماژول را در احتیار داشته باشید. برای تعیین نسخه ی NET Framework. پس از نصب آن ، لازم است به فایل Web Config بروید و کدهای آن را مطابق زیر ویرایش کنید. 

<system.web>
  <compilation debug="true" targetFramework="4.6.2"/>
  <httpRuntime targetFramework="4.6.2"/>
</system.web>

2-Microsoft.AspNet.OutputCache.OutputCacheModuleAsync را از طریق NuGet package نصب کنید.

از NuGet package manager برای نصب Microsoft.AspNet.OutputCache.OutputCacheModuleAsync استفاده کنید. این کار ، یک reference به Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.dll  خواهد بود. سپس کدهای زیر را در فایل web.config  وارد نمایید. 

<system.webServer>
  <modules>
    <remove name="OutputCache"/>
    <add name="OutputCache" type="Microsoft.AspNet.OutputCache.OutputCacheModuleAsync, Microsoft.AspNet.OutputCache.OutputCacheModuleAsync" preCondition="integratedMode"/>
  </modules>
</system.webServer>

حالا برنامه شما با استفاده از ماژول Async OutputCache شروع خواهد شد. اگر هیچ گونه outputcacheprovider  ای در فایل web.config نباشد، ماژول از یک synchronous in-memory provider پیش فرض استفاده خواهد کرد، اما با این کار از مزایای async بهره مند نخواهید بود. شرکت Microsoft هنوز یک  Async OutputCache provider منتشر نکرده است اما به زودی این کار انجام خواهد شد. 

چگونه  async OutputCache Provider را پیاده سازی کنیم؟

یک async OutputCache Provider فقط باید OutputCacheProviderAsync interface را پیاده سازی کند. 

اگر بخواهیم به صورت دقیق تر بگوییم، async provider باید 8 مورد API های زیر را پیاده سازی کند. 

اگرمی خواهیدProvider شما از Cache Dependencyو callback functionality پشتیبانی کند، نیاز خواهید داشت که  اینترفیس ICacheDependencyHandler را پیاده سازی کنید، که در داخل Microsoft.AspNet.OutputCache.OutputCacheModuleAsync.dll قرار داده شده است. شما می توانید این refrence را از Nuget Package Manager اضافه کنید. 

نسخه فعلی Async OutputCache Module از egistry Key  و SQL dependencies پشتیبانی نمی کند. 

زمانی که فرآیند implement  کردن به اتمام رسید، شما می توانید در یک برنامه از آن استفاده کنید. سپس باید پیکربندی های زیر را در فایل Web.config انجام دهید:

<system.web>
  <caching>
    <outputCache defaultProvider="CustomOutputCacheProvider">
    <providers>
      <add name="CustomOutputCacheProvider" type="CustomOutputCacheProvider.CustomOutputCacheProvider, CustomOutputCacheProvider" />
    </providers>
    </outputCache>
  </caching>
</system.web>

تا اینجا کدهای ما باید به درستی کار کنند. اگر به مثال بیشتری در این مورد احتیاج داشتید، می توانید به مثالی که در زیر آورده شده است، دقت کنید. 

namespace Microsoft.AspNet.OutputCache.CustomOutputCacheProvider {
    using System;
    using System.Runtime.Caching;
    using System.Threading.Tasks;
    using System.Web.Caching;

    /// <summary>
    /// This is just a proof of concept Async OutputCache Provider. It is used for testing purpose.
    /// </summary>
    public class CustomOutputCacheProvider : OutputCacheProviderAsync {
        private readonly static MemoryCache _cache = new MemoryCache("CustomOutputCacheProvider");
        
        /// <summary>
        /// Asynchronously inserts the specified entry into the output cache.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="entry"></param>
        /// <param name="utcExpiry"></param>
        /// <returns></returns>
        public override Task<object> AddAsync(string key, object entry, DateTime utcExpiry) {
            //TODO:
            //Replace with your own async data insertion mechanism.
            DateTimeOffset expiration = (utcExpiry == Cache.NoAbsoluteExpiration) ? ObjectCache.InfiniteAbsoluteExpiration : utcExpiry;
            return Task.FromResult(_cache.AddOrGetExisting(key, entry, expiration));
        }

        /// <summary>
        /// Asynchronously returns a reference to the specified entry in the output cache.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public override Task<object> GetAsync(string key) {
            //TODO:
            //Replace with your own aysnc data retrieve mechanism.
            return Task.FromResult(_cache.Get(key));
        }

        /// <summary>
        /// Asynchronously Inserts the specified entry into the output cache, overwriting the entry if it is already cached.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="entry"></param>
        /// <param name="utcExpiry"></param>
        /// <returns></returns>
        public override Task SetAsync(string key, object entry, DateTime utcExpiry) {
            //TODO:
            //Replace with your own async insertion/overwriting mechanism.
            DateTimeOffset expiration = (utcExpiry == Cache.NoAbsoluteExpiration) ? ObjectCache.InfiniteAbsoluteExpiration : utcExpiry;
            _cache.Set(key, entry, expiration);
            return Task.CompletedTask;
        }

        /// <summary>
        /// Asynchronously removes the specified entry from the output cache.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public override Task RemoveAsync(string key) {
            //TODO:
            //Replace with your own async data removal mechanism.
            _cache.Remove(key);
            return Task.CompletedTask;
        }

        /// <summary>
        /// Returns a reference to the specified entry in the output cache.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public override object Get(string key) {
            //TODO:
            //Replace with your own data retrieve mechanism.
            return _cache.Get(key);
        }

        /// <summary>
        /// Inserts the specified entry into the output cache.
        /// </summary>
        /// <param name="key"></param>
        /// <param name="entry"></param>
        /// <param name="utcExpiry"></param>
        /// <returns></returns>
        public override object Add(string key, object entry, DateTime utcExpiry) {
            //TODO:
            //Replace with your own data insertion mechanism.
            DateTimeOffset expiration = (utcExpiry == Cache.NoAbsoluteExpiration) ? ObjectCache.InfiniteAbsoluteExpiration : utcExpiry;
            return _cache.AddOrGetExisting(key, entry, expiration);
        }

        /// <summary>
        /// Inserts the specified entry into the output cache, overwriting the entry if it is already cached
        /// </summary>
        /// <param name="key"></param>
        /// <param name="entry"></param>
        /// <param name="utcExpiry"></param>
        public override void Set(string key, object entry, DateTime utcExpiry) {
            //TODO:
            //Replace with your own insertion/overwriting mechanism.
            DateTimeOffset expiration = (utcExpiry == Cache.NoAbsoluteExpiration) ? ObjectCache.InfiniteAbsoluteExpiration : utcExpiry;
            _cache.Set(key, entry, expiration);
        }

        /// <summary>
        /// Removes the specified entry from the output cache.
        /// </summary>
        /// <param name="key"></param>
        public override void Remove(string key) {
            //TODO:
            //Replace with your own data removal mechanism.
            _cache.Remove(key);
        }
    }
}