.net core下使用事件总线

.net core下使用事件总线

 
 
      随着微服务的火热,DDD(领域驱动设计模式)思想风起云涌,冲击着整个软件生态系统。其中,事件总线那是必须知道的了,于是我便抱着一个学习DDD的心态搭建了一个博客网站,目前该网站正在建设阶段,后续会不断完善,这里我只是讲一下我里面所用到的事件总线。

      事件总线,我的理解就是发布订阅模式,这里有一篇文章写的比较好,我就是按着这个文章来完成的事件总线:事件总线知多少。我之前按照他的文章结合自己写的,但是今天又看了下自己写的,发现好多都生疏了,所以觉得有必要来回忆下,这里只是我个人的理解,如有不对请指出。

     事件总线就肯定要有事件源,这里我定义一个Command事件源:   
1
2
3
4
5
6
7
/// <summary>
/// 领域命令基类(此处文章里我称之为事件源)
/// </summary>
public  class Command
{
     
}

 然后我根据事件源来定义一个事件源处理的接口和它的实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/// <summary>
/// 创建用户领域命令(创建事件源)
/// </summary>
public class CreateUserCommand: Command
{
    public CreateUserCommand(User user)
    {
        User = user;
    }
    public User User { getprivate 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,这个时候可以说是已经将事件源都注册到了内存里了,以下是我的相关代码:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// <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);//将事件源和事件源处理程序注册到内存里,可以说生成了一个订阅列表
}

    接下来我们再看发布与订阅,我要先定义一个发布订阅的中间件,

1
2
3
4
5
6
7
8
9
10
11
12
/// <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的实现类,然后执行里面的方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  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完成业务逻辑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  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注入了:

1
2
3
4
5
6
7
8
9
10
11
/// <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>();<br>            services.AddTransient<IEventBus,EventBus>()
    services.DisposeServiceTypes();
}

  以上就是我对事件总线的具体应用,希望有大佬能指出我这菜鸟的不足指出!

        好记性不如烂笔头,所以我把这个玩意用到了我的网站里面,我的个人站点的地址是:www.ttblog.site,源代码的地址是:https://github.com/Hansdas/BlogH.git,个人站点处于建设阶段,很多功能不完善,由于时间原因,所以进度比较慢,但是我也是每天回到家后都会去完善,自己做的饭再难吃也要吃完自己做的网站,不好看也要用心呵护。 

posted on 2024-08-28 10:49  漫思  阅读(38)  评论(0编辑  收藏  举报

导航