我心中(zhong)的核心組件(可插拔(ba)的AOP)~第(di)二回 緩(huan)存攔截器
AOP面(mian)(mian)向(xiang)(xiang)切面(mian)(mian)的(de)(de)(de)(de)編程,也稱面(mian)(mian)向(xiang)(xiang)方面(mian)(mian)的(de)(de)(de)(de)編程,我(wo)更青睞于前面(mian)(mian)的(de)(de)(de)(de)叫(jiao)法,將一(yi)個(ge)大系(xi)統切成多個(ge)獨立的(de)(de)(de)(de)部分(fen),而(er)這(zhe)個(ge)獨立的(de)(de)(de)(de)部分(fen)又可以方便的(de)(de)(de)(de)插拔在其它領域的(de)(de)(de)(de)系(xi)統之(zhi)中,這(zhe)種編程的(de)(de)(de)(de)方式我(wo)們叫(jiao)它面(mian)(mian)向(xiang)(xiang)切面(mian)(mian),而(er)這(zhe)些獨立的(de)(de)(de)(de)部分(fen),我(wo)們很(hen)早(zao)之(zhi)前叫(jiao)它部件,在SOA里,它叫(jiao)做服務,而(er)我(wo)認為叫(jiao)它模塊更加貼(tie)切,確實,這(zhe)些與領域無關的(de)(de)(de)(de)東西,是像是一(yi)個(ge)個(ge)的(de)(de)(de)(de)功能模塊。
之前講過一個日志組件,有興趣的同學可以查看:第一回 日志記錄組件
今天主要說一下緩存(cun)(cun)(cun)組(zu)件,就是(shi)緩存(cun)(cun)(cun)模塊,這些(xie)模塊可以很方便的(de)為每個(ge)方法添加緩存(cun)(cun)(cun)機(ji)制,事實(shi)上是(shi)在方法體執(zhi)行之前,進行緩存(cun)(cun)(cun)對(dui)(dui)象(xiang)的(de)檢索,當檢索到(dao)有(you)緩存(cun)(cun)(cun),就直(zhi)接加載緩存(cun)(cun)(cun)對(dui)(dui)象(xiang)了,這對(dui)(dui)于數據高并發情(qing)況(kuang)下,尤其有(you)用,呵呵。
實(shi)現(xian)緩存的武(wu)器:Microsoft.Practices.EnterpriseLibrary.Caching
輔助(zhu)兵器(IOC):Microsoft.Practices.Unity
實現的效果:根據在配置文件中對要(yao)緩存的部(bu)分(fen)進行配置后,使它減少對數據庫(ku)的交互,提高程(cheng)序的相應能力
下面開始我們的Caching之旅
1 使用nuget添(tian)加caching和(he)Unity組件,添(tian)加好了之后在引用中自己出(chu)現
在package.config中有我們的組件(jian)的相關說明(ming)
<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添加相應(ying)的unity注入信(xin)息和攔截信(xin)息的配置
<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="//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>
下面(mian)是緩存組件的(de)配置:
<!--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 建立一個(ge)測試用的IRepository接口和(he)一個(ge)個(ge)性化操作的接口IProductRepository
public interface IRepository<TEntity> where TEntity : class { void Insert(TEntity entity); string Hello(); IQueryable<TEntity> GetEntities(); }
public interface IProductRepository : IRepository<Product> { /// <summary> /// 獲(huo)取產品列(lie)表 /// </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); }
對這個接口進行實(shi)現(xian),當然,它可(ke)以(yi)有多個實(shi)現(xian)版本,這也是IoC出現(xian)的原(yuan)因
4 建立一個(ge)本地服務器,它(ta)是與(yu)IoC實現松耦(ou)合的(de)(de)前(qian)提,而IoC是我們實現程序代碼(ma)松耦(ou)合的(de)(de)前(qian)提,呵呵。
/// <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 }
5 建立一個緩存攔截器(qi),它是與具體領域沒(mei)有關系的(de),我們的(de)攔截器(qi)Interception,可(ke)以有兩(liang)個,如緩存攔截,日志攔截,異常攔截等(deng)等(deng),我會在后面的(de)文章(zhang)中進
行介紹
/// <summary> /// 表示用于方法(fa)緩(huan)存功(gong)能的攔截行(xing)為。 /// </summary> public class CachingBehavior : IInterceptionBehavior { #region Private Methods /// <summary> /// 根據指定的<see cref="CachingAttribute"/>以(yi)及<see cref="IMethodInvocation"/>實(shi)例, /// 獲取與某一特定(ding)參數值(zhi)相(xiang)關的鍵名(ming)。 /// </summary> /// <param name="cachingAttribute"><see cref="CachingAttribute"/>實例。</param> /// <param name="input"><see cref="IMethodInvocation"/>實例(li)。</param> /// <returns>與某一特定參數值相關的(de)鍵名。</returns> private string GetValueKey(CachingAttribute cachingAttribute, IMethodInvocation input) { switch (cachingAttribute.Method) { // 如果(guo)是Remove,則不存(cun)在特定值鍵名,所有的以該(gai)方法名稱(cheng)相(xiang)關的緩存(cun)都需(xu)要清除 case CachingMethod.Remove: return null; // 如(ru)果(guo)是Get或者Put,則需要(yao)產生一個針對(dui)特(te)定參數值的鍵名(ming) 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("無(wu)效的緩存方式。"); } } #endregion #region IInterceptionBehavior Members /// <summary> /// 獲取當前行為(wei)需要攔截的(de)對象類型(xing)接口(kou)。 /// </summary> /// <returns>所(suo)有需要攔截的對象(xiang)類型接(jie)口。</returns> public IEnumerable<Type> GetRequiredInterfaces() { return Type.EmptyTypes; } /// <summary> /// 通過實現此(ci)方法來攔截(jie)調(diao)用(yong)并執行所需的攔截(jie)行為。 /// </summary> /// <param name="input">調用攔截(jie)目標時(shi)的輸(shu)入信(xin)息。</param> /// <param name="getNext">通(tong)過(guo)行為鏈來獲取下一個攔(lan)截行為的委托(tuo)。</param> /// <returns>從(cong)攔截目標獲得(de)的(de)返回信(xin)息。</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> /// 獲取(qu)一(yi)個(ge)<see cref="Boolean"/>值(zhi),該值(zhi)表示(shi)當前攔截行(xing)(xing)為被調用時,是否真的需要執行(xing)(xing) /// 某些操作。 /// </summary> public bool WillExecute { get { return true; } } #endregion }
6 下面是前(qian)臺(tai)程序(xu)的調用方法(fa)
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>
}
}
好了,當(dang)我們為程(cheng)序加上緩(huan)存攔截器之(zhi)后,當(dang)它的數據沒有發(fa)(fa)生變化時,會(hui)直接從緩(huan)存中(zhong)讀取對(dui)象,而不會(hui)與數據庫(ku)發(fa)(fa)生訪問!