【輪子狂魔】打造簡易無配置的IoC


如何指定Business Event和Command之間的關系?

既然是基於慣例優先原則,那么我們首先需要定義一個慣例:

1.調度事件和調度處理器之間是一對多關系(多對多的話,相信你看完了以后應該會知道怎么改的)。

2.所有業務事件(Event)要以調度事件為基類,業務指令(Command)的調度處理器特性需要指定可處理的調度事件。

1     /// <summary>
2     /// 調度事件
3     /// </summary>
4     public class DispatchEvent
5     {
6 
7     }
View Code

 

 1     /// <summary>
 2     /// 調度處理器
 3     /// </summary>
 4     [AttributeUsage(AttributeTargets.Method)]
 5     public class DispatchHandlerAttribute : Attribute
 6     {
 7         /// <summary>
 8         /// 觀察的調度事件類型
 9         /// </summary>
10         public Type ObservedDispatchEventType { get; set; }
11 
12         /// <summary>
13         /// 調度實例
14         /// </summary>
15         public object DispatchInstance { get; set; }
16 
17         /// <summary>
18         /// 動作方法信息
19         /// </summary>
20         public MethodInfo ActionMethodInfo { get; set; }
21 
22         /// <summary>
23         /// 構造函數
24         /// </summary>
25         /// <param name="observedDispatchEventType">觀察的調度事件類型</param>
26         public DispatchHandlerAttribute(Type observedDispatchEventType)
27         {
28             this.ObservedDispatchEventType = observedDispatchEventType;
29         }
30     }
View Code

 

如何創建一個業務事件?

 我們基於獲取AccessToken作為第一個業務事件,先創建一個獲取AccessToken的事件

 當然,具體交互微信細節就省略了,如果感興趣可留言,看多少人關注我考慮下是否把微信相關的東西也一起加進來

 1     /// <summary>
 2     /// 獲取微信交互接口憑證事件
 3     /// </summary>
 4     public class GetAccessTokenEvent : DispatchEvent
 5     {
 6         /// <summary>
 7         /// 微信交互接口憑證信息
 8         /// </summary>
 9         public AccessTokenInfo AccessTokenInfo { get; set; }
10     }
View Code

此時有人會問了,不是獲取AccessToken嗎?傳的不應該是一些APPID、APPSecurity之類的嗎,為什么是AccessTokenInfo?
嗯,偉大的值類型和引用類型就派上用場了,因為Event會作為參數傳遞給Command,由Command自行填充。既然GetAccessTokenEvent是引用類型,那么在Command內修改AccessToken是不需要返回一個新的Event或者對象,直接在Event內的AccessTokenInfo上修改就好了,調用者就會得到他想要的東西。

雖然這只是個小知識點,大多數人都知道,但是有人喜歡用,因為可以偷懶。

有人不喜歡,覺得這樣會讓一些人不明白內部到底做了些什么,調用者該如何使用這個事件。

具體怎么做,因人而異吧,這不是重點,關鍵是一開始就提了:慣例優先原則。而這,不就是一個慣例嗎? ^_^

如何創建一個業務指令,與上一個業務事件關聯起來?

 這里有個小小的業務,就是AccessToken獲取后有失效時間,過了要重新獲取。又不能每次都獲取,因為又有獲取次數限制。

 為了處理這個問題,我做了個自動更新緩存類,這個在AccessTokenCommand的靜態構造函數里設置一次獲取邏輯。

 之后其他功能在使用到以后都是從緩存里拿的。那么這個稍微有點邏輯的業務我們看看Command該怎么寫。

 1     /// <summary>
 2     /// 微信交互接口憑證命令
 3     /// </summary>
 4     public class AccessTokenCommand
 5     {
 6         #region 靜態構造函數
 7 
 8         static AccessTokenCommand()
 9         {
10             AutoUpdateCache.Add(CacheKeySet.AccessToken.ToString(), new AutoUpdateItem()
11             {
12                 UpdateValue = (AutoUpdateItem autoUpdateItem) =>
13                 {
14                     var accessTokenInfo = CommandHelper.GetWeChatResponseObject<AccessTokenInfo>(new AccessTokenCommandRequest());
15 
16                     autoUpdateItem.ExpiredSeconds = accessTokenInfo.ExpiresIn - 30;//預留過期時效,防止提前過期
17                     autoUpdateItem.Value = accessTokenInfo;
18                 }
19             });
20         }
21 
22         #endregion
23 
24         /// <summary>
25         /// 獲取微信交互接口憑證
26         /// </summary>
27         /// <param name="e"></param>
28         [DispatchHandler(typeof(GetAccessTokenEvent))]
29         public void GetAccessToken(GetAccessTokenEvent e)
30         {
31             e.AccessTokenInfo = AutoUpdateCache.GetValue<AccessTokenInfo>(CacheKeySet.AccessToken.ToString());
32         }
33     }
View Code

從 GetAccessToken 這個方法我們可以看出他有幾個地方是需要注意的。
1.有一個特性 [DispatchHandler(typeof(GetAccessTokenEvent))] ,這個意思是標識當前方法是一個調度處理器,所處理的事件是 GetAccessTokenEvent

2.沒有任何返回值

3.直接把AccessTokenInfo賦值到 GetAccessTokenEvent 這個傳入參數里

如何把Event和Command關聯起來呢?

 我先聲明,我本人是不太喜歡寫一坨坨的配置文件的,雖然配置更靈活,但配置太多反而維護起來極度不方便。

 那么,不使用配置就相當於要有一定的規則,否則我們是梳理不出來如何自動創建關聯關系。我們就叫他“調度器”好了。

 這個調度器應該具備以下幾個功能:

 1.解析任意一個程序集

 2.掃描所有的DispatchHandler,即調度處理器

 3.緩存調度關系網,降低反射帶來的性能損耗

 4.傳一個調度事件參數,自行調用所有的調度處理器

  1     /// <summary>
  2     /// 調度器
  3     /// </summary>
  4     public class Dispatcher
  5     {
  6         /// <summary>
  7         /// 調度關系網
  8         /// </summary>
  9         private static Dictionary<Type, List<DispatchHandlerAttribute>> _dicDispatchRelativeNetwork = new Dictionary<Type, List<DispatchHandlerAttribute>>();
 10 
 11         /// <summary>
 12         /// 建立調度關系網
 13         /// </summary>
 14         /// <param name="assembly"></param>
 15         public static void BuildDispatchRelationship(Assembly assembly)
 16         {
 17             Logger.Info("調度器:開始建立調度關系網...");
 18 
 19             var types = assembly.GetTypes();
 20 
 21             foreach (var type in types)
 22             {
 23                 var methods = type.GetMethods();
 24 
 25                 foreach (var method in methods)
 26                 {
 27                     var attribute = method.GetCustomAttributes(typeof(DispatchHandlerAttribute), true).FirstOrDefault();
 28                     if (attribute != null)
 29                     {
 30                         CheckParameterRule(method);
 31 
 32                         var handler = attribute as DispatchHandlerAttribute;
 33                         handler.DispatchInstance = Activator.CreateInstance(type);
 34                         handler.ActionMethodInfo = method;
 35                         AddDispatchRelation(handler);
 36                     }
 37                 }
 38             }
 39         }
 40 
 41         /// <summary>
 42         /// 添加調度關系
 43         /// </summary>
 44         /// <param name="handler">調度處理器</param>
 45         private static void AddDispatchRelation(DispatchHandlerAttribute handler)
 46         {
 47             var eventType = handler.ObservedDispatchEventType;
 48 
 49             if (!_dicDispatchRelativeNetwork.ContainsKey(eventType))
 50             {
 51                 _dicDispatchRelativeNetwork.Add(eventType, new List<DispatchHandler>());
 52             }
 53 
 54             _dicDispatchRelativeNetwork[eventType].Add(handler);
 55 
 56             Logger.Info(string.Format("調度器:建立新的關系網 [{0}]-[{1}.{2}]", eventType.Name, handler.DispatchInstance.GetType().Name, handler.ActionMethodInfo.Name));
 57         }
 58 
 59         /// <summary>
 60         /// 檢查參數規則
 61         /// </summary>
 62         /// <param name="method"></param>
 63         private static void CheckParameterRule(MethodInfo method)
 64         {
 65             var parameters = method.GetParameters();
 66             if (parameters == null ||
 67                 parameters.Length != 1 ||
 68                 (!parameters.FirstOrDefault().ParameterType.Equals(typeof(DispatchEvent)) &&
 69                  !parameters.FirstOrDefault().ParameterType.BaseType.Equals(typeof(DispatchEvent))
 70                 ))
 71             {
 72                 throw new Exception(string.Format("DispatchHandler - [{0}]的參數必須是只有一個且繼承自DispatchEvent", method.Name));
 73             }
 74         }
 75 
 76         /// <summary>
 77         /// 激活事件
 78         /// </summary>
 79         /// <param name="dispatchEvent">調度事件</param>
 80         public static void ActiveEvent(DispatchEvent dispatchEvent)
 81         {
 82             var type = dispatchEvent.GetType();
 83 
 84             if (!_dicDispatchRelativeNetwork.ContainsKey(type))
 85             {
 86                 Logger.Error(string.Format("調度器:當前事件[{0}]沒有找到綁定的Handler", type.FullName));
 87                 return;
 88             }
 89 
 90             _dicDispatchRelativeNetwork[type].ForEach(action =>
 91             {
 92                 ActiveAction(action, dispatchEvent);
 93             });
 94         }
 95 
 96         private static void ActiveAction(DispatchHandlerAttribute action, DispatchEvent dispatchEvent)
 97         {
 98             try
 99             {
100                 action.ActionMethodInfo.Invoke(action.DispatchInstance, new object[] { dispatchEvent });
101             }
102             catch (Exception ex)
103             {
104                 if (ex.InnerException != null && ex.InnerException.GetType().Equals(typeof(WCFLib.ExceptionExtension.GException)))
105                 {
106                     throw ex.InnerException;
107                 }
108                 else
109                 {
110                     throw;
111                 }
112             }
113         }
114     }
View Code

 

如何激活Event?

 創建一個事件,使用調度器激活事件,最后從事件中的屬性獲取你需要的返回值。

1             var getAccessTokenEvent = new GetAccessTokenEvent();
2             Dispatcher.ActiveEvent(getAccessTokenEvent);
3             var accessTokenInfo = getAccessTokenEvent.AccessTokenInfo;    

此時又有人會問了,為什么不直接用  new AccessTokenCommand().GetAccessToken(new GetAccessTokenEvent());
因為Event所在類庫是沒有添加Command引用的。通過調度者動態加載的。

那么問題又來了,這么勞民傷財為的是什么?

賣個關子,下篇繼續造輪子。 ^_^

別急別急,還沒完

這年頭不應該有圖有真相嗎?

 

 

感謝所有耐心看完的人,歡迎提出你們的寶貴意見和批評。讓我們一起進步吧。

 

下一篇預告:【輪子狂魔】調度器的擴展能力

 

如果你喜歡這篇博文,或者期待下一篇博文,麻煩點下推薦,你們的支持是我的動力 ^_^


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM