.net core下使用事件總線


 
      隨着微服務的火熱,DDD(領域驅動設計模式)思想風起雲涌,沖擊着整個軟件生態系統。其中,事件總線那是必須知道的了,於是我便抱着一個學習DDD的心態搭建了一個博客網站,目前該網站正在建設階段,后續會不斷完善,這里我只是講一下我里面所用到的事件總線。

      事件總線,我的理解就是發布訂閱模式,這里有一篇文章寫的比較好,我就是按着這個文章來完成的事件總線:事件總線知多少。我之前按照他的文章結合自己寫的,但是今天又看了下自己寫的,發現好多都生疏了,所以覺得有必要來回憶下,這里只是我個人的理解,如有不對請指出。

     事件總線就肯定要有事件源,這里我定義一個Command事件源:   
    /// <summary>
    /// 領域命令基類(此處文章里我稱之為事件源)
    /// </summary>
    public  class Command
    {
        
    }

 然后我根據事件源來定義一個事件源處理的接口和它的實現類:

    /// <summary>
    /// 創建用戶領域命令(創建事件源)
    /// </summary>
    public class CreateUserCommand: Command
    {
        public CreateUserCommand(User user)
        {
            User = user;
        }
        public User User { get; private set; }

    }
    /// <summary>
    /// 用戶命令處理程序(處理事件源)
    /// </summary>
    public class UserCommandHandler : ICommandHandler<CreateUserCommand>, 
    {
        private readonly IUserRepository _userRepository;
        private readonly IEventBus _eventBus;
        public UserCommandHandler(IUserRepository userRepository, IEventBus eventBus)
        {
            _userRepository = userRepository;
            _eventBus = eventBus;
        }

        public void Handler(CreateUserCommand command)
        {
            int count = _userRepository.SelectCountByAccount(command.User.Account);
            if (count > 0)
            {
                _eventBus.RaiseEvent(new NotifyValidation("該賬號已存在"));
                return;
            }
            _userRepository.Insert(command.User);
        }
    }

     此處我覺得已經完成了關於事件源的功能,那么我們就來思考根據這個事件源來處理發布訂閱的關系。

     就那注冊用戶功能來說,前面已經將注冊用戶的事件源已經寫好了,那么發布訂閱怎么處理呢?首先,我們應該清楚一個邏輯,那就是頁面生成用戶信息,后端獲取信息生成UserModel,然后我們根據UserModel轉為我們需要的CreateUserCommand,然后我們ICommandHandler根據CreateUserCommand來調用Handler,這是一個簡單的調用邏輯。那么IcommandHnadler怎么知道調用哪一個Handler呢?那就是我將事件源和事件源處理類存入集合里面,這樣我以后就會根據Command來獲取到我的ICommandHandler了,又因為.net core遵循依賴注入原則,所以我需要往容器了注入ICommander和他的實現類,就是UserCommandhandler,這個時候可以說是已經將事件源都注冊到了內存里了,以下是我的相關代碼:

 
        /// <summary>
        /// 臨時存儲類型數組
        /// </summary>
        private static Type[] serviceTypes = Assembly.Load("Blog.Domain").GetTypes();
 
        private static ConcurrentDictionary<Type, IList<Type>> handlerMapping = new ConcurrentDictionary<Type, IList<Type>>();

        public static IList<Type> GetOrAddHandlerMapping(this Type eventType)
        {
           return handlerMapping.GetOrAdd(eventType,(Type type)=>new List<Type>());
        }

        /// <summary>
        /// 注冊事件總線(事件源)
        /// </summary>
        /// <typeparam name="TImplementation">ICommandler<CreateUserCommand></typeparam>
        /// <typeparam name="TService">CreateUserCommand</typeparam>
        /// <param name="serviceDescriptors"></param>
        public static void AddEventBus<TImplementation, TService>(this IServiceCollection serviceDescriptors)
        {
            Type handler = typeof(TImplementation);
            Type serviceType = serviceTypes.FirstOrDefault(s => handler.IsAssignableFrom(s));//獲得接口的實現類
            if (serviceType == null)
                throw new ArgumentNullException(string.Format("類型{0}未找到實現類", handler.FullName));
            serviceDescriptors.AddTransient(handler, serviceType);//.net core自帶的IOC容器
            GetOrAddHandlerMapping(typeof(TService)).Add(handler);//將事件源和事件源處理程序注冊到內存里,可以說生成了一個訂閱列表
        }

    接下來我們再看發布與訂閱,我要先定義一個發布訂閱的中間件,

    /// <summary>
    /// 中間件
    /// </summary>
   public  interface  IEventBus
    {
        /// <summary>
        /// 發布
        /// </summary>
        /// <typeparam name="TEventData"></typeparam>
        /// <param name="eventData"></param>
        void Publish<TCommand>(TCommand command) where TCommand : Command;
     }

  然后還有它的實現類,處理邏輯就是根據UserCommandHandler去ConcurrentDictionary里找到它的對應的ICommandHandler,然后在從IOC容器找到ICommandHandler的實現類,然后執行里面的方法,如下:

    public sealed class EventBus : IEventBus
    {
        private IServiceProvider _serviceProvider;
        public EventBus(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
        /// <summary>
        /// 發布事件
        /// </summary>
        /// <typeparam name="TEventData"></typeparam>
        /// <param name="eventData"></param>
        public void Publish<TCommand>(TCommand command) where TCommand : Command
        {
            IList<Type> types=typeof(TCommand).GetOrAddHandlerMapping();
            if (types == null || types.Count == 0)
                throw new ServiceException("事件總線未注冊:" + typeof(TCommand).Name);
            foreach (var type in types)//從訂閱列表里尋找
            {
                object obj = _serviceProvider.GetService(type);
                if(type.IsAssignableFrom(obj.GetType()))
                {
                    ICommandHandler<TCommand> handler = obj as ICommandHandler<TCommand>;
                    if (handler != null)
                        handler.Handler(command);//
                }
            }
        }
  }

  這個時候可以說已經完成了發布訂閱,程序生成CreateUserCommand,這里我的理解為就是發布,調用Publish方法,這里我覺得就是訂閱,然后最后執行Handler完成業務邏輯:

  public class UserService : IUserService
    {
        private readonly IUserRepository _userRepository;
        private readonly IEventBus _eventBus;
        /// <summary>
        /// 根據UserModel轉實體
        /// </summary>
        /// <param name="userModel"></param>
        /// <returns></returns>
        private User TransferModel(UserModel userModel)
        {
            return user;
        }
        public UserService(IUserRepository userRepository, IEventBus eventBus)
        {
            _userRepository = userRepository;
            _eventBus = eventBus;
        }
        public void Insert(UserModel userModel)
        {
            userModel.Password = EncrypUtil.MD5Encry(userModel.Password);
            var command = new CreateUserCommand(TransferModel(userModel));//創建事件源
            _eventBus.Publish(command);//發布命令
        }
}

        還有就是最后的IServiceCollection注入了:

        /// <summary>
        /// 服務集合
        /// </summary>
        /// <param name="services"></param>
        public static void AddServices(this IServiceCollection services)
        {
            services.AddTransient<IUserRepository, UserRepository>();
            services.AddTransient<IUserService, UserService>();
            services.AddEventBus<ICommandHandler<CreateUserCommand>, CreateUserCommand>();
services.AddTransient<IEventBus,EventBus>() services.DisposeServiceTypes(); }

  以上就是我對事件總線的具體應用,希望有大佬能指出我這菜鳥的不足指出!

        好記性不如爛筆頭,所以我把這個玩意用到了我的網站里面,我的個人站點的地址是:www.ttblog.site,源代碼的地址是:https://github.com/Hansdas/BlogH.git,個人站點處於建設階段,很多功能不完善,由於時間原因,所以進度比較慢,但是我也是每天回到家后都會去完善,自己做的飯再難吃也要吃完自己做的網站,不好看也要用心呵護。 

      

     

      


免責聲明!

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



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