Lind.DDD.Events領域事件介紹
閑話多說
領(ling)域事(shi)件(jian)大(da)叔感覺是最不(bu)好講的(de)一(yi)篇文章,所以拖(tuo)欠了(le)(le)很久,但最終還是在(zai)2015年(nian)年(nian)前(qian)(陰歷)把這(zhe)(zhe)個知識點(dian)講一(yi)下,事(shi)件(jian)這(zhe)(zhe)個東(dong)西早在(zai)C#1.0時(shi)代就有了(le)(le),那時(shi)學起來也是一(yi)個費勁,什(shen)么(me)是委(wei)托,哪個是事(shi)件(jian),搞的(de)大(da)家是糊里糊涂,進入C#2.0時(shi)代后(hou),大(da)叔也買了(le)(le)一(yi)本書(shu),對于delegate和event這(zhe)(zhe)兩個知識點(dian)看了(le)(le)至少有20幾遍,感覺稍微有點(dian)明白了(le)(le),明白了(le)(le)其中的(de)真諦和用意。
委托:方(fang)(fang)法的規范,方(fang)(fang)法的模板,可以代表一類(lei)方(fang)(fang)法的集(ji)合
事(shi)(shi)件(jian):委托(tuo)的實例,事(shi)(shi)件(jian)在(zai)使用之前需要(yao)為它賦值,當然賦的就是一個方法;事(shi)(shi)件(jian)可以注(zhu)冊和(he)取消,當你注(zhu)冊一個事(shi)(shi)件(jian)之后,在(zai)事(shi)(shi)件(jian)被(bei)(bei)觸發(fa)后,被(bei)(bei)注(zhu)冊的方法將會被(bei)(bei)執行(xing),這一般(ban)被(bei)(bei)稱為“方法的回(hui)調”,在(zai)設(she)計模(mo)式(shi)里(li),又被(bei)(bei)稱為“pub/sub模(mo)式(shi)”,即發(fa)布(bu)/訂閱(yue)模(mo)式(shi);在(zai)C#語言發(fa)展過(guo)程中,設(she)計得(de)(de)為程序開發(fa)者考慮的很多,有(you)些寫法得(de)(de)到了精簡,如(ru)Action和(he)Func委托(tuo)的出現之后,我們基本上(shang)告別(bie)了delegate,這對程序開發(fa)人員無疑是一件(jian)好事(shi)(shi)。
大叔框架中的事件
在大(da)叔框架(jia)里(li),事件是常客,比如在早期的(de)(de)(de)倉儲代碼里(li),你可(ke)以(yi)傳遞(di)一個Action<string>的(de)(de)(de)委(wei)(wei)托,來(lai)進(jin)行(xing)日志的(de)(de)(de)記錄,這種方(fang)(fang)法在IoC出(chu)現后,被(bei)大(da)叔屏蔽了(le),原因不在這里(li)說(shuo)了(le),還有(you)就是在N層(ceng)(ceng)架(jia)構里(li),WEB層(ceng)(ceng)與BLL層(ceng)(ceng)進(jin)行(xing)通訊時,WEB層(ceng)(ceng)通過(guo)HttpClient請求第(di)三方(fang)(fang)的(de)(de)(de)API獲取數據,而BLL層(ceng)(ceng)的(de)(de)(de)方(fang)(fang)法需要(yao)用到這個第(di)三方(fang)(fang)API的(de)(de)(de)返回(hui)值(zhi),而在BLL層(ceng)(ceng)直接訪問HTTP顯(xian)然(ran)是不合(he)適(shi)的(de)(de)(de),所以(yi),在WEB層(ceng)(ceng)到BLL層(ceng)(ceng)的(de)(de)(de)方(fang)(fang)法參數設計時,需要(yao)有(you)一個委(wei)(wei)托來(lai)接改從WEB層(ceng)(ceng)回(hui)調(diao)(diao)的(de)(de)(de)方(fang)(fang)法返回(hui)值(zhi),這種代碼一般稱為(wei)“方(fang)(fang)法回(hui)調(diao)(diao)”。
web層向BLL層傳入一個(ge)委(wei)托
var entity = rechargeService.RechargeAuto( task, beforeTime, out result, (studentid, money) => { //代碼 });
BLL層接(jie)改這(zhe)個委(wei)托的返回(hui)值(zhi),代(dai)碼(ma)在調用bll層這(zhe)個方法(fa)時,首(shou)先會回(hui)調web層的http的方法(fa)
public Task_xuexiba_Recharge RechargeAuto( Task_Info task, DateTime beforeTime, out bool result, Func<int, decimal, RechargeXuexibaDTO> api) { //代碼(ma) }
var apiEntity = api(task.Task_ParametersForXuexibaRecharge.StudentID, task.Task_ParametersForXuexibaRecharge.Money);
Lind.DDD框架里的領域事件
事件源后綴:Event
事件(jian)處(chu)理方法(fa)后(hou)綴:EventHandler
領域事件(jian)(jian)(jian)一般出(chu)現個領域實(shi)體里(li),在(zai)實(shi)體被建立時(shi),會(hui)訂(ding)閱和(he)(he)自己有(you)關的事件(jian)(jian)(jian),每個事件(jian)(jian)(jian)都(dou)有(you)一個或(huo)者多(duo)個事件(jian)(jian)(jian)處(chu)理方法(fa),事件(jian)(jian)(jian)處(chu)理方法(fa)可以進(jin)行(xing)數據庫操(cao)(cao)作(zuo),或(huo)者網絡和(he)(he)文件(jian)(jian)(jian)的操(cao)(cao)作(zuo),如發(fa)通知,寫文件(jian)(jian)(jian)等,所以有(you)時(shi)候(hou)我們的事件(jian)(jian)(jian)需要設計成異步(bu)的事件(jian)(jian)(jian)。
程序中的事件事件
#region 領域模型 public class Order { public Order() { Lind.DDD.Events.EventBus.Instance.Subscribe(new OrderInsertEventHandler()); Lind.DDD.Events.EventBus.Instance.Subscribe<OrderPaid>(new OrderUpdateEventHandler()); } public System.Guid Id { get; set; } public System.Guid UserId { get; set; } public string UserName { get; set; } public decimal TotalFee { get; set; } /// <summary> /// 用戶提交并確認訂單 /// </summary> public void ComfirmOrder() { //事(shi)件發(fa)布 Lind.DDD.Events.EventBus.Instance.Publish(new OrderConfirm { TotalFee = TotalFee, UserName = UserName, UserId = UserId, }); } } #endregion
下面是領域事件源
/// <summary> /// 訂單被確認的(de)事件源 /// </summary> public class OrderConfirm : Lind.DDD.Events.IEvent { public override string ToString() { return "訂單已(yi)經確認(ren)"; } /// <summary> /// 訂單(dan)總(zong)金額(e) /// </summary> public decimal TotalFee { get; set; } /// <summary> /// 購買者ID /// </summary> public Guid UserId { get; set; } /// <summary> /// 購(gou)買者(zhe) /// </summary> public string UserName { get; set; } #region IEvent 成員 public Guid AggregateRoot { get { throw new NotImplementedException(); } } #endregion }
下面是領(ling)域事件的處理程序
/// <summary> /// 訂單(dan)被插(cha)入時的處理(li)程序 /// </summary> public class OrderInsertEventHandler : Lind.DDD.Events.IEventHandler<Events.OrderConfirm> { #region IEventHandler<OrderSigned> 成員 public void Handle(Events.OrderConfirm evt) { //處理訂單確(que)認的邏輯 var orderRepository = new Lind.DDD.Repositories.EF.EFRepository<Orders>(); orderRepository.SetDataContext(new testEntities()); orderRepository.Insert(new Orders { Id = Guid.NewGuid(), OrderStatus = 1, TotalFee = evt.TotalFee, UserId = evt.UserId, UserName = evt.UserName, }); } #endregion }
如果(guo)希望將自己的(de)事件處理程(cheng)序設計成(cheng)異常的(de),即不阻(zu)塞當前(qian)線程(cheng)的(de),可以(yi)讓它添加HandlesAsynchronouslyAttribute這個特(te)性(xing),如下(xia)面這個發送Email的(de)處理程(cheng)序就是一個異步的(de)。
/// <summary> /// 發郵件(jian)功(gong)能[某個(ge)事件(jian)的行為(wei)] /// </summary> [HandlesAsynchronouslyAttribute] public class SendEmailEventHandler : IEventHandler<OrderEvent>, IEventHandler<UserEvent> { #region IEventHandler<OrderEvent> 成員 public void Handle(OrderEvent evt) { Console.WriteLine("生成和(he)確認訂單(dan){0}時發Email", evt.OrderId); } #endregion #region IEventHandler<UserEvent> 成員 public void Handle(UserEvent evt) { Console.WriteLine("建立用戶(hu)后發Email,用戶(hu)ID{0}", evt.UserId); } #endregion }
全局注冊所有事件處理程序
這(zhe)個是(shi)看完ABP之后(hou)的(de)(de)(de)想法,原理(li)(li)是(shi)把BIN下所(suo)有繼承(cheng)了IEventHandler的(de)(de)(de)類都自(zi)動注冊到事件總線(xian)中,然(ran)后(hou)在事件被觸發后(hou),就自(zi)動執行你(ni)訂閱(yue)的(de)(de)(de)方法了,在項目開發過程(cheng)(cheng)中,推薦使(shi)用(yong)這(zhe)種方法,但需要注意(yi)的(de)(de)(de)是(shi),你(ni)的(de)(de)(de)事件處(chu)理(li)(li)程(cheng)(cheng)序必(bi)須是(shi)顯示定(ding)義的(de)(de)(de),不能使(shi)用(yong)匿(ni)名的(de)(de)(de)處(chu)理(li)(li)程(cheng)(cheng)序.
/// <summary> /// 全局統(tong)一注冊所有(you)事件處理(li)程序,實現了IEventHandlers的 /// </summary> public void SubscribeAll() { var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes() .Where(t => t.GetInterfaces().Contains(typeof(IEventHandlers)))) .Where(i => !Excepts.Contains(i.Name)) .ToArray(); foreach (var item in types) { if (!item.ContainsGenericParameters) { var en = Activator.CreateInstance(item); foreach (var t in item.GetInterfaces().Where(i => i.Name != "IEventHandlers")) { Subscribe(t, en); } } } }
感謝各位的閱讀!