回到目錄
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
}
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>
}
}
好了,當我們為程式加上緩存攔截器之後,當它的資料沒有發生變化時,會直接從緩存中讀取對象,而不會與資料庫發生通路!
作者:倉儲大叔,張占嶺,
榮譽:微軟MVP
QQ:853066980
支付寶掃一掃,為大叔打賞!