我心中的核心組件(可插拔的AOP)~第二回 緩存攔截器


回到目錄

AOP面向切面的編程,也稱面向方面的編程,我更青睞於前面的叫法,將一個大系統切成多個獨立的部分,而這個獨立的部分又可以方便的插拔在其它領域的系統之中,這種編程的方式我們叫它面向切面,而這些獨立的部分,我們很早之前叫它部件,在SOA里,它叫做服務,而我認為叫它模塊更加貼切,確實,這些與領域無關的東西,是像是一個個的功能模塊。

之前講過一個日志組件,有興趣的同學可以查看:第一回 日志記錄組件

今天主要說一下緩存組件,就是緩存模塊,這些模塊可以很方便的為每個方法添加緩存機制,事實上是在方法體執行之前,進行緩存對象的檢索,當檢索到有緩存,就直接加載緩存對象了,這對於數據高並發情況下,尤其有用,呵呵。

實現緩存的武器:Microsoft.Practices.EnterpriseLibrary.Caching

輔助兵器(IOC):Microsoft.Practices.Unity

實現的效果:根據在配置文件中對要緩存的部分進行配置后,使它減少對數據庫的交互,提高程序的相應能力

下面開始我們的Caching之旅

1 使用nuget添加caching和Unity組件,添加好了之后在引用中自己出現

在package.config中有我們的組件的相關說明

<packages> 
  <package id="Unity" version="3.0.1304.0" targetFramework="net45" />
  <package id="Unity.Interception" version="3.0.1304.0" targetFramework="net45" />
  <package id="EnterpriseLibrary.Caching" version="5.0.505.0" targetFramework="net45" />
  <package id="EnterpriseLibrary.Common" version="5.0.505.0" targetFramework="net45" />
  <package id="CommonServiceLocator" version="1.0" targetFramework="net45" />
</packages>

2 在web.config添加相應的unity注入信息和攔截信息的配置

<configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
    <section name="cachingConfiguration" type="Microsoft.Practices.EnterpriseLibrary.Caching.Configuration.CacheManagerSettings, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
    <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration" />
    <container>
      <extension type="Interception" />
      <register type="Infrastructure.Caching.ICacheProvider,  DDD_AOP_WCF" mapTo="Infrastructure.Caching.EntLibCacheProvider,  DDD_AOP_WCF" />
      <!--Repository Context & Repositories-->

      <register type="DDD_AOP_WCF.Repository.IProductRepository, DDD_AOP_WCF" mapTo="DDD_AOP_WCF.Repository.ProductRepository, DDD_AOP_WCF">
       <!--  <interceptor type="VirtualMethodInterceptor" />-->
        <interceptor type="InterfaceInterceptor"/>
        <interceptionBehavior type="Infrastructure.InterceptionBehaviors.CachingBehavior,DDD_AOP_WCF" />
        <interceptionBehavior type="Infrastructure.InterceptionBehaviors.ExceptionLoggingBehavior, DDD_AOP_WCF" />
      </register>
    </container>
  </unity>

下面是緩存組件的配置:

  <!--BEGIN: Caching-->
  <cachingConfiguration defaultCacheManager="ByteartRetailCacheManager">
    <cacheManagers>
      <add name="ByteartRetailCacheManager" type="Microsoft.Practices.EnterpriseLibrary.Caching.CacheManager, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
          expirationPollFrequencyInSeconds="600" maximumElementsInCacheBeforeScavenging="1000"
          numberToRemoveWhenScavenging="10" backingStoreName="NullBackingStore" />
    </cacheManagers>
    <backingStores>
      <add type="Microsoft.Practices.EnterpriseLibrary.Caching.BackingStoreImplementations.NullBackingStore, Microsoft.Practices.EnterpriseLibrary.Caching, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
          name="NullBackingStore" />
    </backingStores>
  </cachingConfiguration>
  <!--END: Caching-->

3 建立一個測試用的IRepository接口和一個個性化操作的接口IProductRepository

  public interface IRepository<TEntity> where TEntity : class
    {
        void Insert(TEntity entity);
        string Hello();
        IQueryable<TEntity> GetEntities();
    }
  public interface IProductRepository : IRepository<Product>
    {
        /// <summary>
        /// 獲取產品列表
        /// </summary>
        /// <returns></returns>
        [Caching(CachingMethod.Get)]
        List<Product> GetProduct();

        /// <summary>
        /// 建立產品
        /// </summary>
        [Caching(CachingMethod.Remove, "GetProduct")]
        void AddProduct(Product entity);

        /// <summary>
        /// 修改產品
        /// </summary>
        [Caching(CachingMethod.Remove, "GetProduct")]
        void ModifyProduct(Product entity);

    }

對這個接口進行實現,當然,它可以有多個實現版本,這也是IoC出現的原因

4 建立一個本地服務器,它是與IoC實現松耦合的前提,而IoC是我們實現程序代碼松耦合的前提,呵呵。

  /// <summary>
    /// Represents the Service Locator.
    /// </summary>
    public sealed class ServiceLocator : IServiceProvider
    {
        #region Private Fields
        private readonly IUnityContainer container;
        #endregion

        #region Private Static Fields
        private static readonly ServiceLocator instance = new ServiceLocator();
        #endregion

        #region Ctor
        /// <summary>
        /// Initializes a new instance of ServiceLocator class.
        /// </summary>
        private ServiceLocator()
        {
            UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            container = new UnityContainer();
            section.Configure(container);
        }
        #endregion

        #region Public Static Properties
        /// <summary>
        /// Gets the singleton instance of the ServiceLocator class.
        /// </summary>
        public static ServiceLocator Instance
        {
            get { return instance; }
        }
        #endregion

        #region Private Methods
        private IEnumerable<ParameterOverride> GetParameterOverrides(object overridedArguments)
        {
            List<ParameterOverride> overrides = new List<ParameterOverride>();
            Type argumentsType = overridedArguments.GetType();
            argumentsType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .ToList()
                .ForEach(property =>
                {
                    var propertyValue = property.GetValue(overridedArguments, null);
                    var propertyName = property.Name;
                    overrides.Add(new ParameterOverride(propertyName, propertyValue));
                });
            return overrides;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Gets the service instance with the given type.
        /// </summary>
        /// <typeparam name="T">The type of the service.</typeparam>
        /// <returns>The service instance.</returns>
        public T GetService<T>()
        {
            return container.Resolve<T>();
        }
        /// <summary>
        /// Gets the service instance with the given type by using the overrided arguments.
        /// </summary>
        /// <typeparam name="T">The type of the service.</typeparam>
        /// <param name="overridedArguments">The overrided arguments.</param>
        /// <returns>The service instance.</returns>
        public T GetService<T>(object overridedArguments)
        {
            var overrides = GetParameterOverrides(overridedArguments);
            return container.Resolve<T>(overrides.ToArray());
        }
        /// <summary>
        /// Gets the service instance with the given type by using the overrided arguments.
        /// </summary>
        /// <param name="serviceType">The type of the service.</param>
        /// <param name="overridedArguments">The overrided arguments.</param>
        /// <returns>The service instance.</returns>
        public object GetService(Type serviceType, object overridedArguments)
        {
            var overrides = GetParameterOverrides(overridedArguments);
            return container.Resolve(serviceType, overrides.ToArray());
        }
        #endregion

        #region IServiceProvider Members
        /// <summary>
        /// Gets the service instance with the given type.
        /// </summary>
        /// <param name="serviceType">The type of the service.</param>
        /// <returns>The service instance.</returns>
        public object GetService(Type serviceType)
        {
            return container.Resolve(serviceType);
        }

        #endregion
    }
View Code

5 建立一個緩存攔截器,它是與具體領域沒有關系的,我們的攔截器Interception,可以有兩個,如緩存攔截,日志攔截,異常攔截等等,我會在后面的文章中進

行介紹

  /// <summary>
    /// 表示用於方法緩存功能的攔截行為。
    /// </summary>
    public class CachingBehavior : IInterceptionBehavior
    {
        #region Private Methods
        /// <summary>
        /// 根據指定的<see cref="CachingAttribute"/>以及<see cref="IMethodInvocation"/>實例,
        /// 獲取與某一特定參數值相關的鍵名。
        /// </summary>
        /// <param name="cachingAttribute"><see cref="CachingAttribute"/>實例。</param>
        /// <param name="input"><see cref="IMethodInvocation"/>實例。</param>
        /// <returns>與某一特定參數值相關的鍵名。</returns>
        private string GetValueKey(CachingAttribute cachingAttribute, IMethodInvocation input)
        {
            switch (cachingAttribute.Method)
            {
                // 如果是Remove,則不存在特定值鍵名,所有的以該方法名稱相關的緩存都需要清除
                case CachingMethod.Remove:
                    return null;
                // 如果是Get或者Put,則需要產生一個針對特定參數值的鍵名
                case CachingMethod.Get:
                case CachingMethod.Put:
                    if (input.Arguments != null &&
                        input.Arguments.Count > 0)
                    {
                        var sb = new StringBuilder();
                        for (int i = 0; i < input.Arguments.Count; i++)
                        {
                            sb.Append(input.Arguments[i].ToString());
                            if (i != input.Arguments.Count - 1)
                                sb.Append("_");
                        }
                        return sb.ToString();
                    }
                    else
                        return "NULL";
                default:
                    throw new InvalidOperationException("無效的緩存方式。");
            }
        }
        #endregion

        #region IInterceptionBehavior Members
        /// <summary>
        /// 獲取當前行為需要攔截的對象類型接口。
        /// </summary>
        /// <returns>所有需要攔截的對象類型接口。</returns>
        public IEnumerable<Type> GetRequiredInterfaces()
        {
            return Type.EmptyTypes;
        }

        /// <summary>
        /// 通過實現此方法來攔截調用並執行所需的攔截行為。
        /// </summary>
        /// <param name="input">調用攔截目標時的輸入信息。</param>
        /// <param name="getNext">通過行為鏈來獲取下一個攔截行為的委托。</param>
        /// <returns>從攔截目標獲得的返回信息。</returns>
        public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext)
        {
            var method = input.MethodBase;
            var key = method.Name;
            if (method.IsDefined(typeof(CachingAttribute), false))
            {
                var cachingAttribute = (CachingAttribute)method.GetCustomAttributes(typeof(CachingAttribute), false)[0];
                var valKey = GetValueKey(cachingAttribute, input);
                switch (cachingAttribute.Method)
                {
                    case CachingMethod.Get:
                        try
                        {
                            if (CacheManager.Instance.Exists(key, valKey))
                            {
                                var obj = CacheManager.Instance.Get(key, valKey);
                                var arguments = new object[input.Arguments.Count];
                                input.Arguments.CopyTo(arguments, 0);
                                return new VirtualMethodReturn(input, obj, arguments);
                            }
                            else
                            {
                                var methodReturn = getNext().Invoke(input, getNext);
                                CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue);
                                return methodReturn;
                            }
                        }
                        catch (Exception ex)
                        {
                            return new VirtualMethodReturn(input, ex);
                        }
                    case CachingMethod.Put:
                        try
                        {
                            var methodReturn = getNext().Invoke(input, getNext);
                            if (CacheManager.Instance.Exists(key))
                            {
                                if (cachingAttribute.Force)
                                {
                                    CacheManager.Instance.Remove(key);
                                    CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue);
                                }
                                else
                                    CacheManager.Instance.Put(key, valKey, methodReturn.ReturnValue);
                            }
                            else
                                CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue);
                            return methodReturn;
                        }
                        catch (Exception ex)
                        {
                            return new VirtualMethodReturn(input, ex);
                        }
                    case CachingMethod.Remove:
                        try
                        {
                            var removeKeys = cachingAttribute.CorrespondingMethodNames;
                            foreach (var removeKey in removeKeys)
                            {
                                if (CacheManager.Instance.Exists(removeKey))
                                    CacheManager.Instance.Remove(removeKey);
                            }
                            var methodReturn = getNext().Invoke(input, getNext);
                            return methodReturn;
                        }
                        catch (Exception ex)
                        {
                            return new VirtualMethodReturn(input, ex);
                        }
                    default: break;
                }
            }

            return getNext().Invoke(input, getNext);
        }

        /// <summary>
        /// 獲取一個<see cref="Boolean"/>值,該值表示當前攔截行為被調用時,是否真的需要執行
        /// 某些操作。
        /// </summary>
        public bool WillExecute
        {
            get { return true; }
        }

        #endregion
    }
View Code

6 下面是前台程序的調用方法

   IProductRepository productRepository = ServiceLocator.Instance.GetService<IProductRepository>();
   ViewBag.Product = productRepository.GetProduct();
@{var Model = ViewBag.Product as List<學習陳晴陽的DDD_AOP_WCF.Product>; }
@if (Model != null && Model.Count > 0)
{
    foreach (var item in Model)
    {
    <p>@item.ProductName</p>
    }
}

好了,當我們為程序加上緩存攔截器之后,當它的數據沒有發生變化時,會直接從緩存中讀取對象,而不會與數據庫發生訪問!

回到目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM