zoukankan      html  css  js  c++  java
  • 反射的妙用_项目中的时间配置问题

    内容摘要

    1:阐述问题

    2:分析问题,解决问题

    3:演示解决方案

    1:阐述问题

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

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

     1 <ScheduleDefinition xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     2   <StartDateTime xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/03/01/ReportServer">2012-07-21T22:00:00.000+08:00</StartDateTime>
     3   <MonthlyDOWRecurrence xmlns="http://schemas.microsoft.com/sqlserver/reporting/2010/03/01/ReportServer">
     4     <WhichWeek>FirstWeek</WhichWeek>
     5     <DaysOfWeek>
     6       <Monday>true</Monday>
     7     </DaysOfWeek>
     8     <MonthsOfYear>
     9       <January>true</January>
    10       <February>true</February>
    11       <March>true</March>
    12       <April>true</April>
    13       <May>true</May>
    14       <June>true</June>
    15       <July>true</July>
    16       <August>true</August>
    17       <September>true</September>
    18       <October>true</October>
    19       <November>true</November>
    20       <December>true</December>
    21     </MonthsOfYear>
    22   </MonthlyDOWRecurrence>
    23 </ScheduleDefinition>
    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                 else
    11                 {
    12                     result+=false;
    13                 }
    14             }
    15 
    16             if(condition2 in matchData)
    17             {
    18                 if(nowDt is match matchData by condition2)
    19                 {
    20                     result+=true
    21                 }
    22                 else
    23                 {
    24                     result+=false;
    25                 }
    26             }
    27             //.
    28             //.条件3到9
    29             //.
    30             if(condition10 in matchData)
    31             {
    32                 if(nowDt is match matchData by condition10)
    33                 {
    34                     result+=true
    35                 }
    36                 else
    37                 {
    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         /// <summary>
     2         /// the api to get the email address of  match all conditions 
     3         /// </summary>
     4         /// <returns></returns>
     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         /// <summary>
    24         /// judge whether the current time match all conditions in the xml string 'matchData'
    25         /// </summary>
    26         /// <param name="matchData"></param>
    27         /// <param name="nowDt"></param>
    28         /// <returns></returns>
    29         private bool IsMatchWithMatchData(string matchData, DateTime nowDt)
    30         {
    31             bool result = true;
    32             xmldoc.LoadXml(matchData);
    33             try
    34             {
    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                     else
    45                         break;
    46                 }
    47                 return result;
    48             }
    49             catch
    50             {
    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     /// <summary>
     11     /// determine whether the current now match with the condition of the xml string 
     12     /// </summary>
     13     internal class MatchWithMatchData
     14     {
     15         DateTime startDt;
     16         DateTime nowDt;
     17         /// <summary>
     18         /// initialize data
     19         /// </summary>
     20         /// <param name="startDt">start datetime</param>
     21         /// <param name="nowDt">now datetime</param>
     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         /// <summary>
     94         /// whether current datetime accord with the condition of every month's days
     95         /// </summary>
     96         /// <param name="xml"></param>
     97         /// <returns></returns>
     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                 else
    113                 {
    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             else
    148                 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             else
    160                 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");测试用例下面有数据,总体跟踪一遍,就能理解这个代码
       最后祝大家好运,希望在能够帮助大家,也希望大家能多提意见,觉得有收获的帮助顶一下。
    代码下载:http://files.cnblogs.com/zhangxl/MatchData.zip
    文章出处:http://www.cnblogs.com/zhangxl/p/reflection.html 

     
    
    
    
    
  • 相关阅读:
    远程获取图片尺寸
    python httplib get和post获取数据
    python 之 logging
    php artisan常用方法
    html页面制作css基本设置
    zlib-1.2.7/libpng-1.5.9 instead of zlib-1.2.8/libpng-1.6.6
    shell脚本自动拉起启动程序
    php截取中文无乱码
    路飞学城1之课程与课程详细
    vuex(数据商店实现思想)day06
  • 原文地址:https://www.cnblogs.com/zhangxl/p/reflection.html
Copyright © 2011-2022 走看看