public override bool TryGetMember(GetMemberBinder binder, out object result) { if (!_dictionary.TryGetValue(binder.Name, out result)) { result = null; return true; } var dictionary = result as IDictionary<string, object>; if (dictionary != null) { result = new DynamicJsonObject(dictionary); return true; } var arrayList = result as ArrayList; if (arrayList != null && arrayList.Count > 0) { if (arrayList[0] is IDictionary<string, object>) result = new List<object>(arrayList.Cast<IDictionary<string, object>>().Select(x => new DynamicJsonObject(x))); else result = new List<object>(arrayList.Cast<object>()); } return true; } } #endregion }
接下来是GetContent要领,此要领的目标很简朴,就是要依据客户通报的模板变量参数键值对和短信模板内容,拼装成末了的短信发送内容,之前此要领内里是硬编码的,如今我们须要变成动态猎取。
短信模板的内容示例:
【一应生活】您有一件单号为expressNumbers company,已到communityName收发室,请打开一应生活APP“收发室”猎取取件码举行取件。 点击下载http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life
我发明如许的模板内容有题目,模板中的变量参数是直接用的英文单词示意的,而我们的短信内容中能够有时刻也会存在英文单词,那末我就给一切的变量参数加上{}。修正后以下:
【一应生活】您有一件单号为{expressNumbers} {company},已到{communityName}收发室, 请打开一应生活APP“收发室”猎取取件码举行取件。点击下载http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life
我们须要依据客户通报过来的对象,将短信模板中的变量参数,替代成变量参数对应的值。那末我们起首就要剖析这个对象中的键值对信息。
/// 把object对象的属性反射猎取到字典列表中 /// </summary> /// <param name="data">object对象</param> /// <returns>返回Dictionary(属性名,属性值)列表</returns> static Dictionary<string, string> GetProperties(object data) { Dictionary<string, string> dict = new Dictionary<string, string>(); Type type = data.GetType(); string[] propertyNames = type.GetProperties().Select(p => p.Name).ToArray(); foreach (var prop in propertyNames) { object propValue = type.GetProperty(prop).GetValue(data, null); string value = (propValue != null) ? propValue.ToString() : ""; if (!dict.ContainsKey(prop)) { dict.Add(prop, value); } } return dict; }
接下来是经由过程正则表达式来婚配短信模板内容。
/// 多个婚配内容 /// </summary> /// <param name="sInput">输入内容</param> /// <param name="sRegex">表达式字符串</param> /// <param name="sGroupName">分组名, ""代表不分组</param> static List<string> GetList(string sInput, string sRegex, string sGroupName) { List<string> list = new List<string>(); Regex re = new Regex(sRegex, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline); MatchCollection mcs = re.Matches(sInput); foreach (Match mc in mcs) { if (sGroupName != "") { list.Add(mc.Groups[sGroupName].Value); } else { list.Add(mc.Value); } } return list; } public static string ReplaceTemplate(string template, object data) { var regex = @"\{(?<name>.*?)\}"; List<string> itemList = GetList(template, regex, "name"); //猎取模板变量对象 Dictionary<string, string> dict = GetProperties(data); foreach (string item in itemList) { //假如属性存在,则替代模板,并修正模板值 if (dict.ContainsKey(item)) { template = template.Replace("{"+item+"}", dict.First(x => x.Key == item).Value); } } return template; }
如许就讲客户通报的对象和我们的剖析代码举行相识耦,客户通报的对象不再依赖于我们的代码完成,而是依赖于我们数据表中模板内容的设置。
这几个要领我是写好了,趁便弄个单元测试来考证一下是否是我要的结果,不幸的是,这个项目中基础就没用到单元测试,没办法,我本身建立一个单元测试
[TestClass] public class MatchHelperTest { [TestMethod] public void ReplaceTemplate() { //模板文本 var template = "【一应生活】您有一件单号为{expressNumbers} {company},已到{communityName}收发室, 请打开一应生活APP“收发室”猎取取件码举行取件。点击下载http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life"; //数据对象 var data = new { expressNumbers = "2016", company = "长城", communityName = "长怡花圃"}; string str = "【一应生活】您有一件单号为2016 长城,已到长怡花圃收发室, 请打开一应生活APP“收发室”猎取取件码举行取件。点击下载http://a.app.qq.com/o/simple.jsp?pkgname=com.ening.life"; string str1=MatchHelper.ReplaceTemplate(template, data); Assert.AreEqual(str1,str); //反复标签的测试 template = "【一应生活】您有一件单号为{expressNumbers} {company},已到{communityName}收发室,单号:{expressNumbers}"; str = "【一应生活】您有一件单号为2016 长城,已到长怡花圃收发室,单号:2016"; str1=MatchHelper.ReplaceTemplate(template, data); Assert.AreEqual(str1, str); } }
说到单元测试,我置信在很多公司都没有用起来,来由太多。我也以为假如营业简朴的话,基础没必要写单元测试,国内太多创业型公司项目进度都异常赶,假如说写单元测试不费时刻,那相对是哄人的,至于说写单元测试能进步开辟效力,削减返工率,个人感觉这个还真难说,由于即使不写单元测试也照样能够经由过程很多别的手腕来填补的,个人观点,勿喷。
接下来修正GetContent要领以下:
public string GetContent(dynamic messageContext) { string strMsg = ""; string TypeCode = string.IsNullOrEmpty(messageContext.serviceCode) ? "001" : messageContext.serviceCode; string channel = messageContext.channel; try{ var Module = unitOfWork.MessageModule.Get(c => c.Type == channel && c.TypeNo == TypeCode).FirstOrDefault(); if (!string.IsNullOrEmpty(Module.Content)) { var content = Module.Content; strMsg = MatchHelper.ReplaceTemplate(content, messageContext); } return strMsg; } catch (Exception ex) { strMsg = ex.Message; } return strMsg; }
(话外:先吐槽一下之前这个变量定名,MessageContext messageContext 和string messageContent,长得太像了,一开始我重构的时刻害我弄错了,发起不要在同一个要领中运用类似的变量称号,以避免弄殽杂。妈蛋,老司机的我又被坑了,气愤,无可忍耐,坚决重定名。)
本来控制器挪用营业逻辑代码是直接如许的
MessageModuleBusiness message ModuleBusiness = new MessageModuleBusiness()
依赖于详细类的完成,而我们晓得,详细是不稳固的,笼统才是稳固的,我们应当面向接口编程。今天是发送短信,来日诰日能够就是发邮件,又或许要加日记纪录等等等。
public interface IMessageModuleBusiness { /// <summary> /// 组装音讯内容 /// </summary> /// <param name="messageContext">动态参数对象</param> /// <returns>组装后的音讯内容</returns> string GetContent(dynamic messageContext); }
然后挪用的代码修正为:
private IMessageModuleBusiness message ModuleBusiness = new MessageModuleBusiness();
终究的externalMerchantSendMessage代码为:
/// 外部商户发送信息 public ActionResult externalMerchantSendMessage() { try { dynamic param = null; string json = Request.QueryString.ToString(); if (Request.QueryString.Count != 0) //ajax get要求 { //兼容旧的客户挪用写法,临时硬编了 if (json.Contains("param.")) { json = json.Replace("param.", ""); } json = "{" + json.Replace("=", ":'").Replace("&", "',") + "'}"; } else //ajax Post要求 { Request.InputStream.Position = 0;//牢记这里必需设置流的肇端位置为0,不然没法读取到数据 json = new StreamReader(Request.InputStream).ReadToEnd(); } var serializer = new JavaScriptSerializer(); serializer.RegisterConverters(new[] { new DynamicJsonConverter() }); param = serializer.Deserialize(json, typeof(object)); logger.Info("[externalMerchantSendMessage]param:" + param); bool isAuth = authModelBusiness.isAuth(param.channel, param.phone, param.sign); if (!isAuth) { return Json(new Result<string>() { resultCode = ((int)ResultCode.NoPermission).ToString(), resultMsg = "署名或无权限接见" }, JsonRequestBehavior.AllowGet); } var meaage = messageModuleBusiness.GetContent(param); if (string.IsNullOrEmpty(meaage)) { return Json(new Result<string>() { resultCode = ((int)ResultCode.failure).ToString(), resultMsg = "发送失利" }, JsonRequestBehavior.AllowGet); } SMSHelper helper = new SMSHelper(); helper.SendSMS(meaage, param.phone); //发送短信 return Json(new Result<string>() { resultCode = ((int)ResultCode.success).ToString(), resultMsg = "发送胜利" }, JsonRequestBehavior.AllowGet); } catch (Exception ex) { return Json(new Result<string>() { resultCode = ((int)ResultCode.failure).ToString(), resultMsg = "发送失利"+ex.Message }, JsonRequestBehavior.AllowGet); } }
如许的话,即使往后经由过程反射或许IOC来再次解耦也轻易。
好了,经由过程如许一步一步的重构,在不修正原有表构造和不影响客户挪用的情况下,我已将变化点举行了封装,当客户的模板参数变量变化的时刻,再也不须要变动代码,只须要修正表中的模板内容就能够了。
重构时,画类图是一个异常好的习气,代码构造一览无余,这里我附上类图。
以上就是记一次.NET代码重构(下)的内容,更多相关内容请关注ki4网(www.ki4.cn)!