AOP面向方面編程---postsharp


PostSharp是一個用於在.NET平台上實現AOP(Aspect-Oriented Programming,面向方面編程)的框架,現通過簡單的示例代碼來演示如何使用postsharp。

1、新建一個控制台應用程序。.net框架是4.6.1版本的。

2、創建一個類CoreBusiness,表示用於完成項目的核心業務。

class CoreBusiness
    {
        [Log(ActionName = "Work_1")]
        public void Work_1()
        {
            Console.WriteLine("執行Work_1核心業務");
        }
    }

3、在Program中調用CoreBusiness的對象來完成業務功能。

class Program
    {
        static CoreBusiness cb = new CoreBusiness();
        static void Main(string[] args)
        {
            cb.Work_1();

            Console.Read();
        }
    }

4、假設現在項目已經開發完成了。但是現在要求給項目添加日志,記錄每個核心業務的執行情況。按照以前的老辦法(不用篩選器的情況下),需要定義一個日志操作類:

class LoggingHelper
    {
        public static void Writelog(String message)
        {
            Console.WriteLine(message);
        }
    }

然后在需要記錄日志的地方,實例化LoggingHelper的對象,然后寫入日志。這樣一來,就必須對原來已經開發的項目進行修改,這就違反了開閉原則了。而且添加日志並不是業務的需求變動,不應該去修改業務項目。

5、現在通過AOP面向方面的編程思想來解決這個日志的問題。要實現AOP,有很多框架,比如:Encase ,NKalore,PostSharp,AspectDNG,SetPoint等等。現在通過PostSharp來演示一下。

6、要使用PostSharp,首先必須要安裝它,在NuGet中收索PostSharp,安裝的是6.0.27版本的,這個版本只有45天的試用期

7、定義一個LogAttribute類,繼承OnMethodBoundaryAspect,這個Aspect提供了進入、退出函數等連接點方法。另外,Aspect上必須設置“[Serializable] ”,這與PostSharp內部對Aspect的生命周期管理有關

 [Serializable]
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class LogAttribute : OnMethodBoundaryAspect
    {
        public string ActionName { get; set; }
        public override void OnEntry(MethodExecutionArgs eventArgs)
        {
            LoggingHelper.Writelog(ActionName + "開始執行");
        }

        public override void OnExit(MethodExecutionArgs eventArgs)
        {
            LoggingHelper.Writelog(ActionName + "成功完成");
        }

    }

8、然后Log特性應用到Work_1函數,這個也可以應用到類上面,如果要應用到類,在定義LogAttribute的時候,AttributeUsage屬性的值就需要添加一個AttributeTargets.class

整個程序唯一修改的也就這里了。然后運行程序

這樣就完成了日志的添加功能。

 9、現在有這樣一個需求,有個帶參數的函數,希望在記錄日志的時候,將這個函數的參數及參數值也記錄下來。如下圖,在記錄日志的時候,需要知道調用Work_2時,具體傳入的參數值。

要實現這個功能,需要修改LogAttribute的OnEntry函數:

public override void OnEntry(MethodExecutionArgs eventArgs)
        {
            Arguments arguments = eventArgs.Arguments;
            StringBuilder sb = new StringBuilder();
            ParameterInfo[] parameters = eventArgs.Method.GetParameters();
            for (int i = 0; arguments != null && i < arguments.Count; i++)
            {//進入的參數的值               
                sb.Append( parameters[i].Name + "=" + arguments[i] + ";");
            }
            LoggingHelper.Writelog(ActionName + "開始執行");
            LoggingHelper.Writelog(ActionName + "的參數:" + sb.ToString());
        }

運行后:

10、現在有一個有返回值的函數,要求在日志中記錄這個函數的返回結果:

public override void OnExit(MethodExecutionArgs eventArgs)
        {
            string name = eventArgs.Method.Name;//用這種方式也能獲取特性修飾的函數的名稱
            string value = eventArgs.ReturnValue.ToString();
            LoggingHelper.Writelog(name + "的返回值:"+value);

            LoggingHelper.Writelog(ActionName + "成功完成");
        }

運行后:

如果返回結果不是字符串,而是一個類對象,那么eventArgs.ReturnValue就是那個返回的對象,至於怎么將這個對象序列化,這里就不用說了。

 比如我定義一個類:

 class Modelsf
    {
        public string name { set; get; }
        public string IdNo { set; get; }
    }

添加一個核心業務函數:

[Log(ActionName = "Work_4")]
        public Modelsf Work_4(Modelsf m)
        {
            m.name = m.name + "FFFF";
            return m;
        }

運行程序后的結果:

所以,對於參數和返回值不是基本類型的函數,如果要記錄參數和返回值的詳細信息,還需要特殊處理。

 

 

PostSharp與ActionFilterAttribute的比較

通過上面的示例可以看出postsharp與篩選器很類似。但是篩選器似乎只能用於web項目,不能用在控制台和winform項目,因為這兩種項目中無法添加System.Web.Mvc的引用。至於其他項目比如webservice,wcf,webapi能不能用,這個沒有

去求證。但是postsharp是可以用在這些項目的。因此postsharp比篩選器的應用范圍廣。

在web項目中自定義一個action篩選器,繼承ActionFilterAttribute。然后在OnActionExecuted和OnActionExecuting這兩個函數中添加日志記錄。這點和postsharp是一樣的。

public class LoggingHelper
    {
        public static void Writelog(String message)
        {
            Debug.WriteLine(message);
        }
    }

    public class MyActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            LoggingHelper.Writelog(filterContext.ActionDescriptor.ActionName + "結束執行");
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            LoggingHelper.Writelog(filterContext.ActionDescriptor.ActionName+ "開始執行");
        }
    }

這個MyActionFilter可以直接放到控制器上,不需要像postsharp那樣需要專門設置AttributeTargets.class。

 對於有參數和返回值的函數,在篩選器中記錄日志:

 public class MyActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (filterContext.Result.GetType().Name == "ContentResult")
            {//只有當函數返回的結果是字符串的時候,這種處理方式才能看到返回的內容。如果是其他類型,比如Dictionary<string, string>,都
                //沒法獲得返回的具體內容
                ContentResult res = (ContentResult)filterContext.Result;
                LoggingHelper.Writelog(filterContext.ActionDescriptor.ActionName + "的返回結果:" + res.Content);
            }
                
            LoggingHelper.Writelog(filterContext.ActionDescriptor.ActionName + "結束執行");
           
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var key in filterContext.ActionParameters.Keys)
            {
                sb.Append(key + "=" + filterContext.ActionParameters[key] + ";");
            }
            LoggingHelper.Writelog(filterContext.ActionDescriptor.ActionName + "的參數:"+sb.ToString());
            LoggingHelper.Writelog(filterContext.ActionDescriptor.ActionName+ "開始執行");
        }
    }

 

 

上面的web項目中沒法獲取返回值為string以外的函數的返回值,這是因為OnActionExecuted的參數沒法獲取返回值。下面在webapi里面試試。因為webapi和web項目中的ActionFilterAttribute是處於不同命名空間的,所以其OnActionExecuting和OnActionExecuted

函數的參數類型也就不同。

還是老規矩,在webapi中定義篩選器,代碼如下,在webapi中ActionFilterAttribute位於System.Web.Http.Filters空間,而web項目中的ActionFilterAttribute位於System.Web.Mvc。而且復寫的兩個函數的參數類型也是不同的。

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Web.Http.Controllers;
//using System.Web.Mvc;
using System.Web.Http.Filters;

namespace WebApi.Loger
{
    public class LoggingHelper
    {
        public static void Writelog(String message)
        {
            Debug.WriteLine(message);
        }
    }
    public class MyActionFilterAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var key in actionContext.ActionArguments.Keys)
            {
                sb.Append(key + "=" + actionContext.ActionArguments[key] + ";");
            }
            LoggingHelper.Writelog(actionContext.ActionDescriptor.ActionName + "的參數:" + sb.ToString());
            LoggingHelper.Writelog(actionContext.ActionDescriptor.ActionName + "開始執行");
        }
        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) {
            string result = actionExecutedContext.Response.Content.ReadAsStringAsync().Result;
            var d = JsonConvert.DeserializeObject<Dictionary<string, string>>(result);
            LoggingHelper.Writelog(actionExecutedContext.ActionContext.ActionDescriptor.ActionName + "執行結果"+result);
            LoggingHelper.Writelog(actionExecutedContext.ActionContext.ActionDescriptor.ActionName + "結束執行");
        }
    }

    public class User
    {
        public string Name { set; get; }
        public string Idno { set; get; }
    }
}

在webapi的value控制器中添加3個函數:

public HttpResponseMessage GetInfo(string name,string idno)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            HttpResponseMessage result = new HttpResponseMessage {
                Content = new StringContent(serializer.Serialize(name + "###" + idno), Encoding.GetEncoding("UTF-8"), "application/json")
            };
            return result;
        }
        public HttpResponseMessage GetInfos(string name,string idno)
        {
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            string str = serializer.Serialize(new Dictionary<string, string>() { { name, idno } ,{ "abc","23223"} });
            HttpResponseMessage result = new HttpResponseMessage {
                Content = new StringContent(str, Encoding.GetEncoding("UTF-8"), "application/json") };
            return result;
        }
        public HttpResponseMessage GetUser(string name,string idno)
        {
            User u = new User() { Name = name, Idno = idno };
            JavaScriptSerializer serializer = new JavaScriptSerializer();
            string str = serializer.Serialize(u);
            HttpResponseMessage result = new HttpResponseMessage
            {
                Content = new StringContent(str, Encoding.GetEncoding("UTF-8"), "application/json")
            };
            return result;
        }

因為webapi默認返回的數據到了客戶端是xml格式的,所以通過代碼中的處理,將其返回的數據先序列化放到HttpResponseMessage,然后在篩選器中再反序列化。

注意,下圖中的反序列化的語句,這個要根據webapi返回的內容的類型來具體指定反序列化的類型。但是result肯定是一個json字符串。

用這種方式,可以獲取函數返回的字符串或者其他類對象的數據。當然可能還有其他的返回數據的方式,這里主要是證明在篩選器中,可以獲得函數的返回值這個事實。


免責聲明!

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



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