博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
反射的妙用_项目中的时间配置问题
阅读量:6531 次
发布时间:2019-06-24

本文共 17254 字,大约阅读时间需要 57 分钟。

内容摘要

1:阐述问题

2:分析问题,解决问题

3:演示解决方案

1:阐述问题

      有时候,我们会遇上这样一个问题:有很多条件 condition1 、condition2 、condition3、condition4 、condition5......这些条件各不相同,可能同时配置其中几个,这几个条件有一个交集,交集内部就是我们需要的。

  给一个实例吧。用户在系统中配置了一个时间条件集合,用户可以按照年、月、周或者日来配置,按照其中一种来配置,下面有很多条件可以选择,其中开始日期和时间是必须配置的,最后会形成一个xml信息存储在数据库里面,我们会用当前时间判断每个用户的配置条件,如何符合,我们把他的邮箱拿出放到一个字符串尾部,不符合则不管,最后这个字符串就是所有符合用户配置条件的邮箱集合,我们可以把我们的信息推送给这些用户。其中xml按照月配置的如下:

1 
2
2012-07-21T22:00:00.000+08:00
3
4
FirstWeek
5
6
true
7
8
9
true
10
true
11
true
12
true
13
true
14
true
15
true
16
true
17
true
18
true
19
true
20
true
21
22
23
View Code

StartDateTime是开始时间,它是一个基本条件,每个配置信息里面都必须存在。MonthlyDOWRecurrence是代表是按照月来配置的(也可能是WeeklyRecurrence周、DailyRecurrence日等)这个xml节点下面存在很多条件,例如WhichWeek是代表哪一周,DaysOfWeek表示每周的哪一天,又或者MonthsOfYear是每年的哪几个月。

     问题就是这样,我从数据库MatchData得到所有用户的配置的数据集 Id、Email、MatchData(sql :SELECT [Id],[Email],[MatchData]FROM [MatchData].[dbo].[EmailInTime]),MatchData就是我们的配置xml信息,从集合中找出所有符合条件的用户Email。

2:分析问题,解决问题

     如果你遇上了啦,该如何处理呢?下面是我的处理方法,如果你有更好的办法,请给我留意,不吝赐教,谢谢!

     有人可以会总结一下有多少个条件,例如10个condition,然后写成是个if语句从上到下去判断是否存在这个条件,如果存在,判断是否当前时间符合这个条件。

1 private bool IsMatchWithMatchData(string matchData, DateTime nowDt) 2         { 3             bool result=true; 4             if(condition1 in matchData) 5             { 6                 if(nowDt is match matchData by condition1) 7                 { 8                     result+=true 9                 }10                 else11                 {12                     result+=false;13                 }14             }15 16             if(condition2 in matchData)17             {18                 if(nowDt is match matchData by condition2)19                 {20                     result+=true21                 }22                 else23                 {24                     result+=false;25                 }26             }27             //.28             //.条件3到929             //.30             if(condition10 in matchData)31             {32                 if(nowDt is match matchData by condition10)33                 {34                     result+=true35                 }36                 else37                 {38                     result+=false;39                 }40             }41             return result;42         }
View Code

这是一个伪代码,简单模拟了一下,大家都会发现很多问题,

1:判断是否存在这个条件在xml里面,耗性能;

2:有很多条件是不用去判断的,最好是xml配置文件里面有什么条件,我们就去判断什么条件;

3:扩展性差,当我们需要条件更多的条件的时候,需要修改已有的代码。

如何解决这三个问题呢?我们可以去遍历xml配置信息,我们遇到什么条件的时候,我们反射到封装好的的条件方法里面。遍历xml,我们就可以解决第1个问题;遇上什么条件我们判断什么条件,这个可以解决第2个问题;利用每个条件都封装成方法,遇到条件的时候反射到对应的方法,就解决了第3个问题。

3:演示解决方案

      下面我把解决方案的数据库和代码展现给大家,希望能看到你的宝贵意见!

数据库结构如下:

数据库在代码的App_Data文件夹下,下载代码可以得到。

定义好数据库后,我们看一下解决方案:

      工程下面包含三个类SqlHelper、AnalyseMatchData和MatchWithMatchData:SqlHelper类是网上下载的帮助工具类,用于操作数据库;AnalyseMatchData类只包含一个公共的方法GetMailAddressByMatchData,作为对外提供的API接口;MatchWithMatchData类是程序集内部类internal class ,因为我们一个会分层,把这下代码放在一个类库里面,这里面包含的是每个条件反射的方法。然后就是一个web.config配置文件和一个Index页面,Index页面用于测试效果,web.config配置文件里面当然就是连接字符串了啦,如果要用本人的代码就需要附加数据库和修改配置的连接字符串了,相信对大家都是小KS了。

1         private readonly string connectionStr; 2         private readonly string matchDataSqlStr; 3         MatchWithMatchData matchFunction;//use to reflect the xml node to the corresponding function 4         Type matchType;//the MatchWithMatchData class 's type 5         XmlDocument xmldoc; 6         DateTime nowDt; 7  8         public AnalyseMatchData() 9         {10             connectionStr = System.Configuration.ConfigurationManager.ConnectionStrings["MatchData"].ConnectionString;11             matchDataSqlStr = "SELECT [Id],[Email],[MatchData]FROM [MatchData].[dbo].[EmailInTime]";12             matchFunction = new MatchWithMatchData();13             matchType = matchFunction.GetType();14             xmldoc = new XmlDocument();15         }

首先是定义需要的使用的全局变量,然后再构造函数里面初始化变量,细心的朋友会看到当前时间nowDt变量没有初始化,因为应该放在后面的api接口里面,当前时间应该是用户调用接口的时间。

1         ///  2         /// the api to get the email address of  match all conditions  3         ///  4         /// 
5 public string GetMailAddressByMatchData() 6 { 7 StringBuilder emailAddresses = new StringBuilder(); 8 nowDt = Convert.ToDateTime("2013-06-26T22:15:00.000+08:00");//DateTime.Now;// 9 using (SqlDataReader dr = SqlHelper.ExecuteReader(connectionStr, CommandType.Text, matchDataSqlStr))10 {11 while (dr.Read())12 {13 string matchData = dr.GetString(dr.GetOrdinal("MatchData"));14 if (IsMatchWithMatchData(matchData, nowDt))15 {16 string emails = dr.GetString(dr.GetOrdinal("Email"));17 emailAddresses.Append(string.IsNullOrWhiteSpace(emails) ? string.Empty : ";" + emails);18 }19 }20 }21 return emailAddresses.Length > 0 ? emailAddresses.Remove(0, 1).ToString() : string.Empty;22 }23 /// 24 /// judge whether the current time match all conditions in the xml string 'matchData'25 /// 26 /// 27 /// 28 ///
29 private bool IsMatchWithMatchData(string matchData, DateTime nowDt)30 {31 bool result = true;32 xmldoc.LoadXml(matchData);33 try34 {35 XmlNodeList xmllist = xmldoc.SelectSingleNode("ScheduleDefinition").ChildNodes;36 DateTime startDt = Convert.ToDateTime(xmllist[0].InnerText).ToLocalTime();37 matchFunction.InitData(startDt, nowDt);38 foreach (XmlNode xmlnode in xmllist)39 {40 MethodInfo method = matchType.GetMethod(xmlnode.Name + "Function", BindingFlags.Instance | BindingFlags.Public);41 object oj = method.Invoke(matchFunction, new object[] { xmlnode.OuterXml });42 if (result)43 result = result && Convert.ToBoolean(oj);44 else45 break;46 }47 return result;48 }49 catch50 {51 return false;52 }53 }
    GetMailAddressByMatchData方法是对应的 api接口他需要遍历从数据库里面得到的数据集,最后把符合条件的email地址返回给调用方。其中比较关键的是IsMatchWithMatchData方法用于判断xml配置条件和当前的时间nowDt的比较,如何前期时间在配置的时间条件内返回为真true否则为假false。遍历xml节点,反射到对应的方法,方法的名称是很有讲究的,是节点名称后面加一个后缀xmlnode.Name + "Function"。     这样我们达到了业务的分离,每一个方法表示一个条件业务,需要扩展的时候,新增一种xml节点类型,然后新添加一个方法,其他的一概不用管,是不是很方便!     有人说反射消耗性能,何不使用switch想简单工厂一样,遇到什么节点调用什么方法。其实我不太提倡这种方式: 1:扩张性没有反射好,扩展的时候需要修改代码,不符合开闭原则; 2:xml现在是两层条件,如果用switch,需要嵌套地使用switch了,如果以后变成三层的呢?所有灵活度也不如反射。 3:其实现在反射的性能愈来愈好啦,几乎和一般的方式调用很接近了。我曾使用一万六千多条在3秒内完成,相信我们的员工没有这么多吧!也可以做一些其他的优化,例如利用缓冲,还有及时条件中只要一个条件为false,就直接返回为假,不用检测所有的XML里面的条件,相信你有更多的方式优化它。     反射的方法类MatchWithMatchData里面:
1 using System;  2 using System.Collections.Generic;  3 using System.Linq;  4 using System.Reflection;  5 using System.Web;  6 using System.Xml;  7   8 namespace MatchData  9 { 10     ///  11     /// determine whether the current now match with the condition of the xml string  12     ///  13     internal class MatchWithMatchData 14     { 15         DateTime startDt; 16         DateTime nowDt; 17         ///  18         /// initialize data 19         ///  20         /// start datetime 21         /// now datetime 22         public void InitData(DateTime startDt, DateTime nowDt) 23         { 24             this.startDt = startDt; 25             this.nowDt = nowDt; 26         } 27  28         public bool StartDateTimeFunction(string xml) 29         { 30             XmlDocument xmldoc = new XmlDocument(); 31             xmldoc.LoadXml(xml); 32             //whether the date and time is in the range of startDateTime 33             if (nowDt.Date >= startDt.Date && Math.Abs((startDt.TimeOfDay - nowDt.TimeOfDay).TotalSeconds) < 20) 34                 return true; 35             else 36                 return false; 37         } 38  39         public bool EndDateFunction(string xml) 40         { 41             XmlDocument xmldoc = new XmlDocument(); 42             xmldoc.LoadXml(xml); 43             DateTime dt = DateTime.Parse(xmldoc["EndDate"].InnerText).ToLocalTime(); 44             if (nowDt.Date <= dt.Date) 45                 return true; 46             else 47                 return false; 48         } 49         //----------------------------------------------------------------------------- 50         public bool MonthlyDOWRecurrenceFunction(string xml) 51         { 52             bool result = true; 53             XmlDocument xmldoc = new XmlDocument(); 54             xmldoc.LoadXml(xml); 55             foreach (XmlNode xmlnode in xmldoc.ChildNodes[0].ChildNodes) 56             { 57                 MethodInfo method = this.GetType().GetMethod(xmlnode.Name + "Function", BindingFlags.Instance | BindingFlags.Public); 58                 object oj = method.Invoke(this, new object[] { xmlnode.OuterXml }); 59                 if (result) 60                     result = result && Convert.ToBoolean(oj); 61                 else 62                     break; 63             } 64             return result; 65         } 66  67         public bool MonthlyRecurrenceFunction(string xml) 68         { 69             return MonthlyDOWRecurrenceFunction(xml); 70         } 71  72         public bool WeeklyRecurrenceFunction(string xml) 73         { 74             return MonthlyDOWRecurrenceFunction(xml); 75         } 76  77         public bool DailyRecurrenceFunction(string xml) 78         { 79             return MonthlyDOWRecurrenceFunction(xml); 80         } 81         //------------------------------------------------------------------------------- 82         // Sub methods to handle sub options 83         //------------------------------------------------------------------------------- 84         public bool WhichWeekFunction(string xml) 85         { 86             XmlDocument xmldoc = new XmlDocument(); 87             xmldoc.LoadXml(xml); 88             string WhichWeek = xmldoc["WhichWeek"].InnerText; 89             double r = (nowDt.Day - (double)nowDt.DayOfWeek);//the month's day of last week sunday  90             int nowWeekInt = Convert.ToInt32(Math.Ceiling((r < 0 ? 0 : r) / 7)) + 1;//the index of the current week of the current month 91             return WhichWeek == WhichWeekToName(nowWeekInt); 92         } 93         ///  94         /// whether current datetime accord with the condition of every month's days 95         ///  96         ///  97         /// 
98 public bool DaysFunction(string xml) 99 {100 XmlDocument xmldoc = new XmlDocument();101 xmldoc.LoadXml(xml);102 string day = xmldoc["Days"].InnerText;103 string[] days = day.Split(',');104 foreach (string dy in days)105 {106 if (dy.Length >= 3)107 {108 string[] dys = dy.Split('-');109 if (nowDt.Day >= Convert.ToInt32(dys[0]) && nowDt.Day <= Convert.ToInt32(dys[1]))110 return true;111 }112 else113 {114 if (nowDt.Day == Convert.ToInt32(dy))115 return true;116 }117 }118 return false;119 }120 121 public bool WeeksIntervalFunction(string xml)122 {123 XmlDocument xmldoc = new XmlDocument();124 xmldoc.LoadXml(xml);125 int weeksInterval = Convert.ToInt32(xmldoc["WeeksInterval"].InnerText) + 1;126 double ts = (Math.Abs((nowDt - startDt).TotalDays) + Convert.ToInt32(startDt.DayOfWeek) - 7) / 7 % (weeksInterval);127 return ts < 1;128 }129 130 public bool DaysIntervalFunction(string xml)131 {132 XmlDocument xmldoc = new XmlDocument();133 xmldoc.LoadXml(xml);134 int daysInterval = Convert.ToInt32(xmldoc["DaysInterval"].InnerText);135 double ts = Math.Abs((nowDt - startDt).TotalDays) % daysInterval;136 return ts < 1;137 }138 139 public bool MonthsOfYearFunction(string xml)140 {141 XmlDocument xmldoc = new XmlDocument();142 xmldoc.LoadXml(xml);143 string monthStr = MonthToName(nowDt.Month);144 XmlElement nodeElement = xmldoc["MonthsOfYear"][monthStr];145 if (nodeElement != null && Convert.ToBoolean(nodeElement.InnerText))146 return true;147 else148 return false;149 }150 151 public bool DaysOfWeekFunction(string xml)152 {153 XmlDocument xmldoc = new XmlDocument();154 xmldoc.LoadXml(xml);155 string weekStr = WeekToName(Convert.ToInt32(nowDt.DayOfWeek));156 XmlElement nodeElement = xmldoc["DaysOfWeek"][weekStr];157 if (nodeElement != null && Convert.ToBoolean(nodeElement.InnerText))158 return true;159 else160 return false;161 }162 //-------------------------------------------------------------------------------163 private string WhichWeekToName(int weekOfMonth)164 {165 string nowWeekStr = "LastWeek";166 switch (weekOfMonth)167 {168 case 1:169 nowWeekStr = "FirstWeek";170 break;171 case 2:172 nowWeekStr = "SecondWeek";173 break;174 case 3:175 nowWeekStr = "ThreeWeek";176 break;177 case 4:178 nowWeekStr = "FourtWeek";179 break;180 default:181 break;182 }183 return nowWeekStr;184 }185 186 private string MonthToName(int month)187 {188 string monthStr = "December";189 switch (month)190 {191 case 1:192 monthStr = "January";193 break;194 case 2:195 monthStr = "February";196 break;197 case 3:198 monthStr = "March";199 break;200 case 4:201 monthStr = "April";202 break;203 case 5:204 monthStr = "May";205 break;206 case 6:207 monthStr = "June";208 break;209 case 7:210 monthStr = "July";211 break;212 case 8:213 monthStr = "August";214 break;215 case 9:216 monthStr = "September";217 break;218 case 10:219 monthStr = "October";220 break;221 case 11:222 monthStr = "November";223 break;224 default:225 break;226 227 }228 return monthStr;229 }230 231 private string WeekToName(int week)232 {233 string weekStr = "Sunday";234 switch (week)235 {236 case 1:237 weekStr = "Monday";238 break;239 case 2:240 weekStr = "Tuesday";241 break;242 case 3:243 weekStr = "Tuesday";244 break;245 case 4:246 weekStr = "Thursday";247 break;248 case 5:249 weekStr = "Friday";250 break;251 case 6:252 weekStr = "Saturday";253 break;254 default:255 break;256 }257 return weekStr;258 }259 }260 }
View Code

 

比较有意思的是WhichWeekFunction方法,判断当前日期是哪一周,MonthlyDOWRecurrenceFunction等方法下面,还有条件,又使用了反射的方式调用。整体是如此简单,如此容易理解。测试的时候,把DateTime.Now;//Convert.ToDateTime("2013-06-26T22:15:00.000+08:00");//改成Convert.ToDateTime("2013-06-26T22:15:00.000+08:00");//DateTime.Now;//,这是一个小技巧。这个Convert.ToDateTime("2013-06-26T22:15:00.000+08:00");测试用例下面有数据,总体跟踪一遍,就能理解这个代码
最后祝大家好运,希望在能够帮助大家,也希望大家能多提意见,觉得有收获的帮助顶一下。 代码下载: 文章出处: 
 
 
 

转载于:https://www.cnblogs.com/zhangxl/p/reflection.html

你可能感兴趣的文章