最近在做项目(其实就是开发一些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,有很多详尽的介绍,这里就不说了。