最近在做项目(其实就是开发一些WebService接口)的时候,遇到了这么一个问题:就是每个接口都要去实现日志记录的功能,当然记录的内容尽可能的详细,以便根据这些信息还原现场,排除故障。这样,每个接口方法为了实现记录日志的功能,会有很多重复的功能代码。插入下面函数形式说明,可能会更清楚的理解我想要表达的意思:

        public object MothodName(string strValide, string p1, string p2, string p3)
        {
            //1、如果有权限,则可以进行操作。
            if (strValide == "true")
            {
                //log 权限验证结果
                //log 形参名称极其对应的值
                //2、一次检查p1,p2,p3……等参数,确定他们都符合条件
                if (p1 == "true" && p2 == "true" && p3 == "true")
                {
                    try
                    {
                        //do action;
                        //log  result;
                    }
                    catch (Exception e)
                    {
                        //log exception;
                    }
                }
                else 
                {
                    //log error
                }

                //3、
            }
            else
            {
                //log
            }
            return new object();//返回一个东西,知识象征性的返回一个对象
        }

 

大家注意:上面的代码中只有do action 才是这个接口真正需要完成的动作,其它的都是和其他接口一样,必须实现的功能部分。如果每写一个接口都去实现这写具有共性的功能,无疑是一件令人枯燥的重复性工作。我们是搞程序的,而程序就是为了解决重复性的工作产生的。所以如果程序本身是重复性的,那该是一个多么好笑的笑话。是时候该终结它了。

给出我的解决方案:delegate + generic ,也就是委托+泛型。

首先定义一个泛型类,这个泛型类主要用于记录接口的入参类型极其对应的值。因为我们知道,每个接口的参数列表是不一样的,也就是参数类型和参数个数是不确定的。这里为了解决参数类型未知的问题,用泛型。泛型类的定义如下:

    public class ProxyParam<TT>
    {
        private string pName;

        /// <summary>
        /// parameter name
        /// </summary>
        public string PName
        {
            get { return pName; }
            set { pName = value; }
        }
        private TT pval;

        /// <summary>
        /// parameter value
        /// </summary>
        public TT Pval
        {
            get { return pval; }
            set { pval = value; }
        }

        public ProxyParam(string strName,TT val)
        {
            pName = strName;
            pval = val;
        }
    }

至于接口的参数个数未知的问题:我们定义一个ArrayList。参数有多少个,我们就往ArrayList实例里增加多少个元素就完事了,注意这里元素的类型可能是不一样的。而这个实例呢,要作为完成接口共性功能的代理类的一个数据成员。

下面来构造这个完成接口共性功能的代理类:

由于代理类要完成共性功能,因此需要一个处理这些共性问题的方法。

由于代理类要满足各个接口个性需要,也就是上面说的do action。因此要暴漏一个delegate,供接口注册方法。

又由于每个接口的返回值类型是不一样的,这个代理类又必须是一个泛型类。

该代理类的结构定义如下:

  1     /// <summary>
  2     /// the proxy to write log, and invoke the main function of the interface. 
  3     /// </summary>
  4     /// <typeparam name="T">the type of the returned model</typeparam>
  5     public class ProxyInterface<T>
  6     {
  7         /// <summary>
  8         /// the static object to write log to txt files.
  9         /// </summary>
 10         private static ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
 11 
 12         private static JavaScriptSerializer jss = new JavaScriptSerializer();
 13 
 14 
 15         private ArrayList arParams = null;//记录接口的参数列表
 16 
 17         public ArrayList ArParams
 18         {
 19             get
 20             {
 21                 if (arParams == null)
 22                 {
 23                     arParams = new ArrayList();
 24                 }
 25                 return arParams;
 26             }
 27         }
 28 
 29         /// <summary>
 30         /// return a Model<T>
 31         /// </summary>
 32         /// <param name="arParams"></param>
 33         /// <returns></returns>
 34         public delegate Mod_T<T> DelFuncModel(ArrayList arParams);
 35         private event DelFuncModel OnDelFuncModel;
 36         /// <summary>
 37         /// registe the delegate function
 38         /// </summary>
 39         /// <param name="d"></param>
 40         public void RegFuncModel(DelFuncModel d)
 41         {
 42             if (OnDelFuncModel == null)
 43                 OnDelFuncModel = d;
 44         }
 45 
 46         /// <summary>
 47         /// unregiste the delegate function
 48         /// </summary>
 49         /// <param name="d"></param>
 50         public void UnRegFuncModel(DelFuncModel d)
 51         {
 52             OnDelFuncModel -= d;
 53         }
 54         /// <summary>
 55         /// return a Model List<T>
 56         /// </summary>
 57         /// <param name="arParams"></param>
 58         /// <returns></returns>
 59         public delegate Mod_ListT<T> DelFuncList(ArrayList arParams);
 60         private event DelFuncList OnFuncList;
 61         /// <summary>
 62         /// registe the delegate function
 63         /// </summary>
 64         /// <param name="d"></param>
 65         public void RegFuncList(DelFuncList d)
 66         {
 67             if (OnFuncList == null)
 68                 OnFuncList = d;
 69         }
 70 
 71         /// <summary>
 72         /// unregiste the delegate function
 73         /// </summary>
 74         /// <param name="d"></param>
 75         public void UnRegFuncList(DelFuncList d)
 76         {
 77             OnFuncList -= d;
 78         }
 79 
 80         /// <summary>
 81         /// excute the delegate
 82         /// </summary>
 83         /// <param name="param">给调用方的一个编码</param>
 84         /// <param name="intInterfaceCode">接口的编号</param>
 85         /// <param name="strInterfaceDescript">the interface description</param>
 86         /// <param name="alParamList">the interface parameter list</param>
 87         /// <returns></returns>
 88         public Mod_T<T> InvokeTheFunction(string param, int intInterfaceCode, string strInterfaceDescript)
 89         {
 90             Mod_T<T> Mod_obj = new Mod_T<T>();
 91             StringBuilder StrbParams = new StringBuilder(100,5000);//最多记录5000个字
 92             StrbParams.Append("--------------------------begin-------------------------\r\n");
 93             StrbParams.Append(strInterfaceDescript + " " + intInterfaceCode + " 输入参数列表:\r\n");
 94             //序列化
 95             string strParams = jss.Serialize(this.ArParams);
 96             StrbParams.Append(strParams + "\r\n");//注意:这时候记录的参数列表是一个将形参名称极其对应值序列化成Json串。
 97             Authentication auth = CheckUser(param, intInterfaceCode);//判断权限
 98             StrbParams.Append("身份验证结果:" + auth.LoginCode + "\r\n");
 99             if (auth == true)
100             {
101                 try
102                 {
103                     if (OnDelFuncModel != null)
104                     {//执行代理
105                         Mod_obj = OnDelFuncModel(this.ArParams);// 这里处理do action,处理每个接口真正要完成的功能
106                         StrbParams.Append(string.Format("处理成功。\r\n返回的编码:{0}\r\n返回的说明信息:{1}\r\n返回的结果信息:{2}\r\n", Mod_obj.ReturnCode, Mod_obj.ReturnMessage == null ? "" : Mod_obj.ReturnMessage, Mod_obj.T_obj == null ? "" : Mod_obj.T_obj.ToString()));
107                     }
108                 }
109                 catch (Exception e)
110                 {
111                     StrbParams.Append("处理异常:" + e.Message + "\r\n" + e.Source + "\r\n" + e.StackTrace);
112                 }
113             }
114             else
115             {
116             }
117             StrbParams.Append("--------------------------end-------------------------\r\n");
118             log.Info(StrbParams.ToString());
119             return Mod_obj;
120         }
121     }

注意:上面的类只能完成一部分功能。细心的同学会发现我并没有实现DelFuncList的函数。其实只是返回值不同而已大体功能一样。一个返回值是一个对象,另一个返回值的一个对象的集合。

代理类有了,那么如何使用呢?

定义一个WebService Interface接口如下:

 1         public Mod_T<string> exampleFunction(string param, string strUserID, string strUserRealName, string strUserGender, string strUserTel, string strUserEmail, string strUserCertificateType, string strUserCertificateNumber)
 2         {
 3             ProxyInterface<string> pi = new ProxyInterface<string>();
 4             pi.ArParams.Add(new ProxyParam<string>("strUserID", strUserID));
 5             pi.ArParams.Add(new ProxyParam<string>("strUserRealName", strUserRealName));
 6             pi.ArParams.Add(new ProxyParam<string>("strUserGender", strUserGender));
 7             pi.ArParams.Add(new ProxyParam<string>("strUserTel", strUserTel));
 8             pi.ArParams.Add(new ProxyParam<string>("strUserEmail", strUserEmail));
 9             pi.ArParams.Add(new ProxyParam<string>("strUserCertificateType", strUserCertificateType));
10             pi.ArParams.Add(new ProxyParam<string>("strUserCertificateNumber", strUserCertificateNumber));
11             pi.RegFuncModel(UUI);//为代理注册方法。
12             return pi.InvokeTheFunction(param,123456,"测试例子");
13         }
14 
15         private Mod_T<string> UUI(ArrayList ar)
16         {
17             Mod_T<string> mod = new Mod_T<string>();
18             t_Users Model = BLL_Users.Instance.GetById(((ProxyParam<string>)ar[0]).Pval);
19             if (Model != null)
20             {
21                 Model.c_realName = ((ProxyParam<string>)ar[1]).Pval;
22                 Model.c_gender = ((ProxyParam<string>)ar[2]).Pval; ;
23                 Model.c_mobile = ((ProxyParam<string>)ar[3]).Pval ;
24                 Model.c_email = ((ProxyParam<string>)ar[4]).Pval;
25                 Model.c_IDType = ((ProxyParam<string>)ar[5]).Pval;
26                 Model.c_IDNo = ((ProxyParam<string>)ar[6]).Pval;
27                 if (BLL_Users.Instance.Modify(Model))
28                 {
29                     mod.ReturnCode = 200;
30                     mod.T_obj = "true";
31                 }
32                 else
33                 {
34                     mod.ReturnCode = 212;
35                     mod.ReturnMessage = "请重试!";
36                     mod.T_obj = "false";
37                 }
38             }
39             return mod;
40         }

大致就是:如果按照原来的方法,一个接口就是一个方法。现在呢,我将其拆分成两个方法,一个是定义接口,并在这个接口中实例化代理类的一个对象,为这个对象的代理注册第二个方法。而第二个方法才是实现接口功能的地方,不需要考虑日志记录,权限验证,参数验证等问题,单纯的实现就好。 

通过这种方式实现的日志记录日志的记录形式是一致的,方便用户查阅。日志的记录信息也是比较完整的。当然对于一些复杂的接口,可能还需要添加额外的日志记录来描述更为详细的信息。

日志记录使用了log4net组件,搜索log4net,有很多详尽的介绍,这里就不说了。