如何指定Business Event和Command之間的關系? |
既然是基於慣例優先原則,那么我們首先需要定義一個慣例:
1.調度事件和調度處理器之間是一對多關系(多對多的話,相信你看完了以后應該會知道怎么改的)。
2.所有業務事件(Event)要以調度事件為基類,業務指令(Command)的調度處理器特性需要指定可處理的調度事件。

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

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 }
如何創建一個業務事件? |
我們基於獲取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 }
此時有人會問了,不是獲取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 }
從 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 }
如何激活Event? |
創建一個事件,使用調度器激活事件,最后從事件中的屬性獲取你需要的返回值。
1 var getAccessTokenEvent = new GetAccessTokenEvent(); 2 Dispatcher.ActiveEvent(getAccessTokenEvent); 3 var accessTokenInfo = getAccessTokenEvent.AccessTokenInfo;
此時又有人會問了,為什么不直接用 new AccessTokenCommand().GetAccessToken(new GetAccessTokenEvent());
因為Event所在類庫是沒有添加Command引用的。通過調度者動態加載的。
那么問題又來了,這么勞民傷財為的是什么?
賣個關子,下篇繼續造輪子。 ^_^
別急別急,還沒完
這年頭不應該有圖有真相嗎? |
感謝所有耐心看完的人,歡迎提出你們的寶貴意見和批評。讓我們一起進步吧。
下一篇預告:【輪子狂魔】調度器的擴展能力
如果你喜歡這篇博文,或者期待下一篇博文,麻煩點下推薦,你們的支持是我的動力 ^_^