1. 程式人生 > >Lind.DDD.Events領域事件介紹

Lind.DDD.Events領域事件介紹

回到目錄

閒話多說

領域事件大叔感覺是最不好講的一篇文章,所以拖欠了很久,但最終還是在2015年年前(陰曆)把這個知識點講一下,事件這個東西早在C#1.0時代就有了,那時學起來也是一個費勁,什麼是委託,哪個是事件,搞的大家是糊里糊塗,進入C#2.0時代後,大叔也買了一本書,對於delegate和event這兩個知識點看了至少有20幾遍,感覺稍微有點明白了,明白了其中的真諦和用意。

委託:方法的規範,方法的模板,可以代表一類方法的集合

事件:委託的例項,事件在使用之前需要為它賦值,當然賦的就是一個方法;事件可以註冊和取消,當你註冊一個事件之後,在事件被觸發後,被註冊的方法將會被執行,這一般被稱為“方法的回撥”,在設計模式裡,又被稱為“pub/sub模式”,即釋出/訂閱模式;在C#語言發展過程中,設計得為程式開發者考慮的很多,有些寫法得到了精簡,如Action和Func委託的出現之後,我們基本上告別了delegate,這對程式開發人員無疑是一件好事。

大叔框架中的事件

在大叔框架裡,事件是常客,比如在早期的倉儲程式碼裡,你可以傳遞一個Action<string>的委託,來進行日誌的記錄,這種方法在IoC出現後,被大叔遮蔽了,原因不在這裡說了,還有就是在N層架構裡,WEB層與BLL層進行通訊時,WEB層通過HttpClient請求第三方的API獲取資料,而BLL層的方法需要用到這個第三方API的返回值,而在BLL層直接訪問HTTP顯然是不合適的,所以,在WEB層到BLL層的方法引數設計時,需要有一個委託來接改從WEB層回撥的方法返回值,這種程式碼一般稱為“方法回撥”。

web層向BLL層傳入一個委託

  var entity = rechargeService.RechargeAuto(
                        task,
                        beforeTime,
                        
out result, (studentid, money) => {               //程式碼               });

BLL層接改這個委託的返回值,程式碼在呼叫bll層這個方法時,首先會回撥web層的http的方法

public Task_xuexiba_Recharge RechargeAuto(
         Task_Info task,
         DateTime beforeTime,
         out bool
result, Func<int, decimal, RechargeXuexibaDTO> api) {       //程式碼    }
  var apiEntity = api(task.Task_ParametersForXuexibaRecharge.StudentID, task.Task_ParametersForXuexibaRecharge.Money);

Lind.DDD框架裡的領域事件

事件源字尾:Event

事件處理方法字尾:EventHandler

領域事件一般出現個領域實體裡,在實體被建立時,會訂閱和自己有關的事件,每個事件都有一個或者多個事件處理方法,事件處理方法可以進行資料庫操作,或者網路和檔案的操作,如發通知,寫檔案等,所以有時候我們的事件需要設計成非同步的事件。

程式中的事件事件

  #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()
        {
            //事件釋出
            Lind.DDD.Events.EventBus.Instance.Publish(new OrderConfirm
            {
                TotalFee = TotalFee,
                UserName = UserName,
                UserId = UserId,
            });
        }

    }

    #endregion

下面是領域事件源

   /// <summary>
    /// 訂單被確認的事件源
    /// </summary>
    public class OrderConfirm : Lind.DDD.Events.IEvent
    {
        public override string ToString()
        {
            return "訂單已經確認";
        }
        /// <summary>
        /// 訂單總金額
        /// </summary>
        public decimal TotalFee { get; set; }
        /// <summary>
        /// 購買者ID
        /// </summary>
        public Guid UserId { get; set; }
        /// <summary>
        /// 購買者
        /// </summary>
        public string UserName { get; set; }

        #region IEvent 成員

        public Guid AggregateRoot
        {
            get { throw new NotImplementedException(); }
        }

        #endregion
    }

下面是領域事件的處理程式

   /// <summary>
    /// 訂單被插入時的處理程式
    /// </summary>
    public class OrderInsertEventHandler :
          Lind.DDD.Events.IEventHandler<Events.OrderConfirm>
    {
        #region IEventHandler<OrderSigned> 成員

        public void Handle(Events.OrderConfirm evt)
        {
            //處理訂單確認的邏輯
            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
    }

如果希望將自己的事件處理程式設計成異常的,即不阻塞當前執行緒的,可以讓它新增HandlesAsynchronouslyAttribute這個特性,如下面這個傳送Email的處理程式就是一個非同步的。

  /// <summary>
    /// 發郵件功能[某個事件的行為]
    /// </summary>
    [HandlesAsynchronouslyAttribute]
    public class SendEmailEventHandler :
        IEventHandler<OrderEvent>,
        IEventHandler<UserEvent>
    {

        #region IEventHandler<OrderEvent> 成員

        public void Handle(OrderEvent evt)
        {
            Console.WriteLine("生成和確認訂單{0}時發Email", evt.OrderId);
        }

        #endregion

        #region IEventHandler<UserEvent> 成員

        public void Handle(UserEvent evt)
        {
            Console.WriteLine("建立使用者後發Email,使用者ID{0}", evt.UserId);
        }

        #endregion
    }

 全域性註冊所有事件處理程式

這個是看完ABP之後的想法,原理是把BIN下所有繼承了IEventHandler的類都自動註冊到事件匯流排中,然後在事件被觸發後,就自動執行你訂閱的方法了,在專案開發過程中,推薦使用這種方法,但需要注意的是,你的事件處理程式必須是顯示定義的,不能使用匿名的處理程式.

     /// <summary>
        /// 全域性統一註冊所有事件處理程式,實現了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);
                    }
                }
            }
        }

感謝各位的閱讀!

回到目錄