zoukankan      html  css  js  c++  java
  • 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字符串。

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

  • 相关阅读:
    Spring Boot @DeleteMapping(转)
    白天和夜晚分类
    使用HSV色彩空间遮罩绿色区域
    HSV转换
    HSV 和 HLS颜色空间
    python 处理图像出现The lower bounary is neither an array of the same size and same type as src, nor a scalar in function inRange
    读取图片上一点的像素
    python 实现A*算法
    uda 4.C++面向对象编程
    uda 3.C++二维向量
  • 原文地址:https://www.cnblogs.com/jin-/p/9708360.html
Copyright © 2011-2022 走看看