事件匯流排demo

科睿思博發表於2017-02-12

經過幾天的努力拜讀大牛高手文章,終於對事件匯流排有所瞭解,特此記錄下來,以免忘記

1、定義相關的介面:

  A  事件介面

 1 public interface IDomainEvent
 2     {
 3         DateTime OccurredOn();
 4 
 5         /// <summary>
 6         /// 設定為已讀
 7         /// </summary>
 8         /// <returns></returns>
 9         void Read();
10 
11         /// <summary>
12         /// 是否已讀
13         /// </summary>
14         bool IsRead { get; }
15     }

  B  事件訂閱介面

    public interface IDomainEventSubscriber
    {
        Type SubscribedToEventType();

        void Handle(object domainEvent);
    }

2、定義相關實現

  A  事件實現

 public abstract class DomainEvent :  IDomainEvent
    {
        public readonly DateTime OccurredOnTime;
        public string ID;

        protected DomainEvent()
        {
            this.ID = Guid.NewGuid().ToString();
            this.OccurredOnTime = DateTime.Now;
            this.IsRead = false;
        }

        public DateTime OccurredOn()
        {
            return this.OccurredOnTime;
        }

        public void Read()
        {
            this.IsRead = true;
        }

        public bool IsRead { get; private set; }
    }

  B  事件訂閱實現

    public abstract class DomainEventSubscriber<T> : IDomainEventSubscriber where T : IDomainEvent
    {
        /// <summary>訂閱的事件型別
        /// </summary>
        /// <returns></returns>
        public Type SubscribedToEventType()
        {
            return typeof(T);
        }

        public abstract void HandleEvent(T domainEvent);

        public void Handle(object domainEvent)
        {
            if (domainEvent is T)
            {
                this.HandleEvent((T)domainEvent);
            }
            else
            {
                throw new NotSupportedException(string.Format("當前訂閱者支援的事件型別是:{0},當前事件是:{1}", typeof(T).FullName, domainEvent.GetType().FullName));
            }
        }
    }

3、定義事件匯流排實現

 public class DomainEventBus
    {
        public delegate void DistributeExceptionHandle(IDomainEventSubscriber subscriber, IDomainEvent domainEvent, Exception exception);
        /// <summary>
        /// Key:DomainEvent的型別,Value訂閱該型別事件的訂閱者列表
        /// </summary>
        private static readonly Dictionary<Type, List<IDomainEventSubscriber>> _subscribers = new Dictionary<Type, List<IDomainEventSubscriber>>();

        private static readonly object _lockObj = new object();

        public event DistributeExceptionHandle DistributeExceptionEvent;

        private static DomainEventBus _instance;
        public static DomainEventBus Instance()
        {
            if (_instance != null)
                return _instance;
            var temp = new DomainEventBus();
            Interlocked.CompareExchange(ref _instance, temp, null);
            return temp;
        }

        public void Publish<T>(T aDomainEvent) where T : IDomainEvent
        {
            if (aDomainEvent.IsRead)
                return;

            var registeredSubscribers = _subscribers;
            if (registeredSubscribers != null)
            {
                var domainEventType = aDomainEvent.GetType();
                List<IDomainEventSubscriber> subscribers;
                if (!registeredSubscribers.TryGetValue(domainEventType, out subscribers))
                {
                    aDomainEvent.Read();  //未找到訂閱者,但是訊息還是消費掉。
                    return;
                }

                foreach (var domainEventSubscriber in subscribers)
                {
                    var subscribedTo = domainEventSubscriber.SubscribedToEventType();
                    if (subscribedTo == domainEventType || subscribedTo is IDomainEvent)
                    {
                        Distribute(domainEventSubscriber, aDomainEvent);
                    }
                }

                aDomainEvent.Read();
            }
        }

        private void Distribute(IDomainEventSubscriber subscriber, IDomainEvent domainEvent)
        {
            try
            {
                subscriber.Handle(domainEvent);
            }
            catch (Exception ex)
            {
                OnDistributeExceptionEvent(subscriber, domainEvent, ex);
            }
        }

        public void Subscribe(IDomainEventSubscriber aSubscriber)
        {
            lock (_lockObj)
            {
                var registeredSubscribers = _subscribers;

                var domainEventType = aSubscriber.SubscribedToEventType();
                List<IDomainEventSubscriber> subscribers;

                if (!registeredSubscribers.TryGetValue(domainEventType, out subscribers))
                {
                    subscribers = new List<IDomainEventSubscriber>();
                    registeredSubscribers.Add(domainEventType, subscribers);
                }

                if (subscribers.Any(ent => ent.SubscribedToEventType().FullName == aSubscriber.SubscribedToEventType().FullName && ent.GetType().FullName == aSubscriber.GetType().FullName))  //相同的訂閱只接收一次。
                    return;

                subscribers.Add(aSubscriber);
            }
        }

        protected virtual void OnDistributeExceptionEvent(IDomainEventSubscriber subscriber, IDomainEvent domainEvent, Exception exception)
        {
            var handler = DistributeExceptionEvent;
            if (handler != null)
                handler(subscriber, domainEvent, exception);
        }
    }

4、定義具體的事件

public class OrderCreated : DomainEventCore.DomainEvent
    {
        public string OrderId { get; private set; }

        public string UserId { get; private set; }

        public string Receiver { get; private set; }

        public OrderCreated(string orderId, string userId, string receiver)
        {
            this.OrderId = orderId;
            this.UserId = userId;
            this.Receiver = receiver;
        }
    }
View Code

5、定義具體事件觸發後要執行的方法

 public class OrderCreatedSubscriberPaymentContext : DomainEventSubscriber<OrderCreated>
    {
        public override void HandleEvent(OrderCreated domainEvent)
        {
            //TODO anything
            Console.WriteLine("Order ID:{0},i have payment",domainEvent.OrderId);
        }
    }
View Code
 public class OrderCreatedSubscriberSellingPriceContext : DomainEventSubscriber<OrderCreated>
    {
        public override void HandleEvent(OrderCreated domainEvent)
        {
            //TODO anything
            Console.WriteLine("Order ID:{0},i have show price", domainEvent.OrderId);
        }
    }
View Code

6、程式碼測試

class Program
    {
        static void Main(string[] args)
        {
            //事件訂閱
            DomainEventBus.Instance().Subscribe(new OrderCreatedSubscriberSellingPriceContext());
            DomainEventBus.Instance().Subscribe(new OrderCreatedSubscriberPaymentContext());

            var tempGuid = Guid.NewGuid().ToString();
            var entity = new OrderCreated(tempGuid, "mikechang", "sprite");
            Console.WriteLine("生產一個新的訂單:{0}",tempGuid);
            //事件釋出
            DomainEventBus.Instance().Publish(entity);
            Console.ReadLine();
        }
    }

 

相關文章