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字符串。

用这种方式,可以获取函数返回的字符串或者其他类对象的数据。当然可能还有其他的返回数据的方式,这里主要是证明在筛选器中,可以获得函数的返回值这个事实。

posted @ 2018-09-26 17:36  你爱我像谁  阅读(4789)  评论(0编辑  收藏  举报