一分鐘教你引用MrAdvice.dll文件實(shí)現(xiàn)AOP攔截

近日工作中,要實(shí)現(xiàn)一個(gè)功能,那就是業(yè)務(wù)層方法里面實(shí)現(xiàn)自動(dòng)緩存。編寫業(yè)務(wù)的C#開發(fā)人員只關(guān)注如何將業(yè)務(wù)代碼編寫正確就可以了,而緩存的代碼,大多類似,無非就是判斷是否有緩存,有就取出返回,沒有就調(diào)用數(shù)據(jù)庫代碼獲取數(shù)據(jù)再緩存起來而已,于是這部分代碼通過使用AOP的方式自動(dòng)接管掉這種重復(fù)性代碼。

創(chuàng)新互聯(lián)企業(yè)建站,10年網(wǎng)站建設(shè)經(jīng)驗(yàn),專注于網(wǎng)站建設(shè)技術(shù),精于網(wǎng)頁設(shè)計(jì),有多年建站和網(wǎng)站代運(yùn)營經(jīng)驗(yàn),設(shè)計(jì)師為客戶打造網(wǎng)絡(luò)企業(yè)風(fēng)格,提供周到的建站售前咨詢和貼心的售后服務(wù)。對于網(wǎng)站制作、成都網(wǎng)站建設(shè)中不同領(lǐng)域進(jìn)行深入了解和探索,創(chuàng)新互聯(lián)在網(wǎng)站建設(shè)中充分了解客戶行業(yè)的需求,以靈動(dòng)的思維在網(wǎng)頁中充分展現(xiàn),通過對客戶行業(yè)精準(zhǔn)市場調(diào)研,為客戶提供的解決方案。

MrAdvice開源項(xiàng)目github地址:https://github.com/ArxOne/MrAdvice

直接引用MrAdvice.dll文件不能實(shí)現(xiàn)AOP攔截功能

因項(xiàng)目原因內(nèi)外網(wǎng)隔離,且是斷網(wǎng)開發(fā)的,就只能在外網(wǎng)寫個(gè)測試程序,然后將MrAdvice.dll文件復(fù)制到內(nèi)網(wǎng)電腦,內(nèi)網(wǎng)電腦通過引用dll的方式來使用該組件,結(jié)果是不會(huì)進(jìn)入到攔截方法的。
一分鐘教你引用MrAdvice.dll文件實(shí)現(xiàn)AOP攔截

通過下圖可以看到,成功解決后,可以實(shí)現(xiàn)自動(dòng)緩存了。

一分鐘教你引用MrAdvice.dll文件實(shí)現(xiàn)AOP攔截
下面是全部的演示程序源碼。

演示程序解決方案目錄一覽

該項(xiàng)目是一個(gè)控制臺(tái)項(xiàng)目,解決方案如下圖所示:
一分鐘教你引用MrAdvice.dll文件實(shí)現(xiàn)AOP攔截

MrAdvice.dll是直接引用的,不是通過nuget安裝的,至于這個(gè)dll文件的獲取,你可以通過nuget獲取了找到它即可。

演示程序的源碼

控制臺(tái)入口的代碼比較簡單,單純的調(diào)用接口。

程序入口代碼
一分鐘教你引用MrAdvice.dll文件實(shí)現(xiàn)AOP攔截

程序接口代碼

程序接口代碼主要是模擬業(yè)務(wù)方法里面的一些類,定義了一個(gè)接口,一個(gè)實(shí)現(xiàn)類,另外實(shí)現(xiàn)類上面是標(biāo)注了一個(gè)自動(dòng)緩存的特性(AutoCache),該特性的實(shí)現(xiàn)代碼即為下面所述的核心的AOP攔截代碼,具體下面會(huì)給出的;另外還有一個(gè)輸出結(jié)果(響應(yīng)消息)的類。整個(gè)源碼是放到一個(gè)文件里面的,如下所示:

public interface IJhrscom

{

ResponseResult GetResult(string a, DateTime dateTime, int id);

ResponseResult GetPatient(Guid id, ResponseResult t);

}

public class Jhrscom : IJhrscom

{

[AutoCache(10)]

public ResponseResult GetPatient(Guid id, ResponseResult t)

{

string key = GetKey(new object[] { id, t });

ResponseResult result = new ResponseResult() { Code = 4444, Message = "第2個(gè)方法" };

return result;

}

[AutoCache(cacheMinutes: 12, enableSliding: true)]

public ResponseResult GetResult(string a, DateTime dateTime, int id)

{

ResponseResult result = new ResponseResult() { Code = 1122, Message = "緩存測試消息" };

string key = GetKey(new object[] { a, dateTime, id });

return result;

}

/// <summary>

/// 緩存key

/// </summary>

/// <param name="pars"></param>

/// <returns></returns>

private string GetKey(params object[] pars)

{

var method = new StackFrame(1).GetMethod();

var array = method.GetParameters();

var key = array.Select(x => { return pars[x.Position].ToJson(); }).ToArray();

var cacheKey = $"{method.DeclaringType.ToString()}|{method.Name.Replace("′", "")}|{string.Join("", array.Select(x => x.Name))}|{string.Join("", key)}".GetMd5();

Console.WriteLine($"【{method.Name.Replace("′", "")}】實(shí)現(xiàn)類里面的緩存Key:" + cacheKey);

return cacheKey;

}

}

/// <summary>

/// 輸出結(jié)果

/// </summary>

public class ResponseResult

{

public int Code { get; set; }

public string Message { get; set; }

//.....其它屬性

}

核心的AOP攔截代碼

該代碼是用于實(shí)現(xiàn)自動(dòng)緩存功能,思路就是在調(diào)用業(yè)務(wù)方法前,根據(jù)緩存key,緩存key按一定規(guī)則生成,保證唯一就可以了,具體源碼中有說明,從緩存里面取出數(shù)據(jù),如果存在緩存就直接返回給調(diào)用者即可,并終止業(yè)務(wù)方法的執(zhí)行(體現(xiàn)在不調(diào)用context.Proceed()方法上);如果不存在緩存數(shù)據(jù)或者緩存過期了,則調(diào)用業(yè)務(wù)方法獲取數(shù)據(jù)后并緩存就可以了。

/// <summary>

/// 用AOP來實(shí)現(xiàn)自動(dòng)緩存

/// </summary>

public class AutoCacheAttribute : Attribute, IMethodAdvice

{

/// <summary>

/// 滑動(dòng)過期

/// </summary>

public bool EnableSliding { get; set; }

/// <summary>

/// 緩存時(shí)間,分鐘

/// </summary>

public int CacheMinutes { get; set; }

/// <summary>

/// 構(gòu)造函數(shù)

/// </summary>

/// <param name="cacheMinutes">緩存時(shí)間,分鐘,默認(rèn)5分鐘,小于等于0永久緩存</param>

/// <param name="enableSliding">使用滑動(dòng)過期緩存控制策略</param>

public AutoCacheAttribute(int cacheMinutes = 5, bool enableSliding = false)

{

EnableSliding = enableSliding;

CacheMinutes = cacheMinutes;

}

/// <summary>

/// AOP組件攔截方法,用于實(shí)現(xiàn)自動(dòng)緩存,有緩存時(shí)直接返回;

/// 沒有緩存時(shí),調(diào)用被攔截方法后,有返回值則將數(shù)據(jù)自動(dòng)緩存起來

/// </summary>

/// <param name="context"></param>

public void Advise(MethodAdviceContext context)

{

var key = GetKey(context);

if (context.HasReturnValue && key.TryGetCache(out object m))

{

var r = m as ResponseResult;

r.Message = "在攔截方法里面改了緩存里面取出來的數(shù)據(jù)!";

context.ReturnValue = r;

//context.ReturnValue = m;

//context.Proceed(); //直接取出緩存返回,不用執(zhí)行原來取數(shù)據(jù)方法。

}

else

{

context.Proceed();//執(zhí)行被攔截的方法

if (context.HasReturnValue && context.ReturnValue != null)

{

//被攔截方法有返回值,并且返回值不為null

if (EnableSliding && CacheMinutes > 0)

context.ReturnValue.SetCache(key, TimeSpan.FromMinutes(CacheMinutes));

else if (CacheMinutes > 0)

context.ReturnValue.SetCache(key, DateTime.Now.AddMinutes(CacheMinutes));

else

context.ReturnValue.SetCache(key);

}

}

}

/// <summary>

/// 獲取緩存key,key的規(guī)則為: md5(類全名|方法名|參數(shù)列表拆分?jǐn)?shù)組|參數(shù)值的json數(shù)組),這樣可以保證唯一

/// </summary>

/// <param name="context"></param>

/// <returns></returns>

private string GetKey(MethodAdviceContext context)

{

var array = context.TargetMethod.GetParameters();

var key = array.Select(x => { return context.Arguments[x.Position].ToJson(); }).ToArray();

var cacheKey = $"{context.Target.ToString()}|{context.TargetName}|{string.Join("", array.Select(x => x.Name))}|{string.Join("", key)}".GetMd5();

return cacheKey;

}

}

/// <summary>

/// 緩存擴(kuò)展方法,可使用其它緩存替代

/// </summary>

public static class CacheExtensions

{

private static MemoryCache cache = new MemoryCache("https://jhrs.com");

/// <summary>

/// 設(shè)置緩存,一直不過期

/// </summary>

/// <typeparam name="T"></typeparam>

/// <param name="value"></param>

/// <param name="key"></param>

public static void SetCache<T>(this T value, string key)

{

if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException($"緩存鍵參數(shù){nameof(key)}不能為null或空");

if (value == null) throw new ArgumentException($"緩存值參數(shù){nameof(value)}不能為null");

CacheItemPolicy policy = new CacheItemPolicy();

cache.Set(key, value, policy);

}

/// <summary>

/// 設(shè)置緩存,固定過期時(shí)間

/// </summary>

/// <typeparam name="T"></typeparam>

/// <param name="value"></param>

/// <param name="key"></param>

/// <param name="absoluteExpiration"></param>

public static void SetCache<T>(this T value, string key, DateTimeOffset? absoluteExpiration)

{

if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException($"緩存鍵參數(shù){nameof(key)}不能為null或空");

if (value == null) throw new ArgumentException($"緩存值參數(shù){nameof(value)}不能為null");

CacheItemPolicy policy = new CacheItemPolicy() { AbsoluteExpiration = (DateTimeOffset)absoluteExpiration };

cache.Set(key, value, policy);

}

/// <summary>

/// 設(shè)置緩存,滑動(dòng)過期

/// </summary>

/// <typeparam name="T"></typeparam>

/// <param name="value"></param>

/// <param name="key"></param>

/// <param name="slidingExpiration"></param>

public static void SetCache<T>(this T value, string key, TimeSpan? slidingExpiration)

{

if (string.IsNullOrWhiteSpace(key)) throw new ArgumentException($"緩存鍵參數(shù){nameof(key)}不能為null或空");

if (value == null) throw new ArgumentException($"緩存值參數(shù){nameof(value)}不能為null");

CacheItemPolicy policy = new CacheItemPolicy() { SlidingExpiration = (TimeSpan)slidingExpiration };

cache.Set(key, value, policy);

}

/// <summary>

/// 獲取緩存數(shù)據(jù)

/// </summary>

/// <typeparam name="T">對象類型</typeparam>

/// <param name="key"><緩存key/param>

/// <param name="value">返回的緩存數(shù)據(jù)對名</param>

/// <returns></returns>

public static bool TryGetCache<T>(this string key, out T value)

{

value = default(T);

if (cache.Contains(key))

{

value = (T)cache.Get(key);

return true;

}

return false;

}

/// <summary>

/// 獲取字符串MD5值

/// </summary>

/// <param name="value"></param>

/// <returns></returns>

public static string GetMd5(this string value)

{

byte[] bytes = Encoding.UTF8.GetBytes(value);

StringBuilder sb = new StringBuilder();

MD5 hash = new MD5CryptoServiceProvider();

bytes = hash.ComputeHash(bytes);

foreach (byte b in bytes)

{

sb.AppendFormat("{0:x2}", b);

}

return sb.ToString();

}

}

附加的JSON擴(kuò)展類

該擴(kuò)展類只是方便將對象轉(zhuǎn)為JSON而已,代碼不復(fù)如,如下所示:

public static class JsonExtensions

{

/// <summary>

/// 將對象轉(zhuǎn)換為JSON字符串

/// </summary>

/// <param name="obj">要轉(zhuǎn)換的對象</param>

/// <param name="camelCase">是否小寫名稱</param>

/// <param name="indented"></param>

/// <returns></returns>

public static string ToJson(this object obj, bool camelCase = false, bool indented = false)

{

JsonSerializerSettings settings = new JsonSerializerSettings();

if (camelCase)

{

settings.ContractResolver = new CamelCasePropertyNamesContractResolver();

}

if (indented)

{

settings.Formatting = Formatting.Indented;

}

return JsonConvert.SerializeObject(obj, settings);

}

/// <summary>

/// 把Json字符串轉(zhuǎn)換為強(qiáng)類型對象

/// </summary>

public static T FromJson<T>(string json)

{

if (string.IsNullOrWhiteSpace(json)) return default(T);

json = JsonDateTimeFormat(json);

return JsonConvert.DeserializeObject<T>(json);

}

/// <summary>

/// 處理Json的時(shí)間格式為正常格式

/// </summary>

private static string JsonDateTimeFormat(string json)

{

json = Regex.Replace(json,

@"\/Date((\d+))\/",

match =>

{

DateTime dt = new DateTime(1970, 1, 1);

dt = dt.AddMilliseconds(long.Parse(match.Groups[1].Value));

dt = dt.ToLocalTime();

return dt.ToString("yyyy-MM-dd HH:mm:ss.fff");

});

return json;

}

}

解決直接引用MrAdvice.dll不能攔截的問題

出現(xiàn)這個(gè)問題的根源是,MrAdvice這個(gè)組件是在編譯時(shí)會(huì)給你的項(xiàng)目源碼編織一些AOP攔截代碼,熟悉PostSharp的應(yīng)該對此了解,這也是在MrAdvice項(xiàng)目地址的issues處得到解答,地址是:https://github.com/ArxOne/MrAdvice/issues/140

所以我們需要在項(xiàng)目文件csproj里面添加一些配置,并且把MrAdvice的目錄復(fù)制到斷網(wǎng)開發(fā)項(xiàng)目的packages目錄。通過完成這兩個(gè)步驟就可以解決了。

You’ve missed the point: Mr Advice is a post-build weaver, which changes the assembly at build-time after the csc compiler has generated it. To achieve this, is inserts a task in the csproj. So if you want to do the same manually, you need to also add the build task in your csproj. If you have a VS2017 solution with a project working, you’ll only need to copy the lines that were added to the csproj into your own project.

解決步驟
一分鐘教你引用MrAdvice.dll文件實(shí)現(xiàn)AOP攔截
聯(lián)網(wǎng)新建一個(gè)項(xiàng)目,通過nuget安裝MrAdvice,然后在解決方案的packages目錄里面將nuget下載的MrAdvice目錄包,復(fù)制到你斷網(wǎng)環(huán)境的解決方案的packages目錄,如下圖所示:

一分鐘教你引用MrAdvice.dll文件實(shí)現(xiàn)AOP攔截
MrAdvice 目錄

修改項(xiàng)目文件,即修改csproj文件,csproj文件可以使用記事本或者其它軟件打開,增加以下節(jié)點(diǎn),如下圖所示:

csproj文件

配置節(jié)點(diǎn)為如下:

<Import Project="..\packages\MrAdvice.2.8.8\build\MrAdvice.targets" Condition="Exists('..\packages\MrAdvice.2.8.8\build\MrAdvice.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>這臺(tái)計(jì)算機(jī)上缺少此項(xiàng)目引用的 NuGet 程序包。使用“NuGet 程序包還原”可下載這些程序包。有關(guān)更多信息,請參見 http://go.microsoft.com/fwlink/?LinkID=322105。缺少的文件是 {0}。</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MrAdvice.2.8.8\build\MrAdvice.targets')" Text="([System.String]::Format('(ErrorText)', '..\packages\MrAdvice.2.8.8\build\MrAdvice.targets'))" />
</Target>

新聞標(biāo)題:一分鐘教你引用MrAdvice.dll文件實(shí)現(xiàn)AOP攔截
標(biāo)題來源:http://www.muchs.cn/article48/geeiep.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供App設(shè)計(jì)、外貿(mào)網(wǎng)站建設(shè)、關(guān)鍵詞優(yōu)化微信小程序、品牌網(wǎng)站建設(shè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司