使用SimpleInjector IoC发布订阅者

时间:2017-05-20 21:43:07

标签: c# design-patterns simple-injector

我一直在考虑在我的网络应用中实现事件模型。

我需要实施的方法是:

  1. 从数据库获取数据;
  2. 将其传递给Grab方法并收集数据;
  3. 将结果转换为我合适的数据并构建电子邮件,记录消息列表;
  4. 如果需要,通过电子邮件服务通知用户; (之后我们需要更改用户被通知的数据库中的bool标志)
  5. 将所有抓取的日志写入某个商店;
  6. 将抓取的数据添加到DB。
  7. 根据this回答,我决定使用上述样本。但我已经讨论了如何连接事件和传递我需要处理的数据。我有一个课程,从互联网上获取一些信息:

    public interface IScheduleService
    {
        void ParseWebData(int userId);
    }
    
    public class ScheduleService : IScheduleService
    {
        IQueryHandler<ProductGrabbedInfoByUserQuery, IEnumerable<ProductGrabbedInfo>> GrabberQuery;
        IEventPublisher publisher;
    
        List<string> logMessages = new List<string>();
        List<string> emailMessages = new List<string>();
    
    
        public ScheduleService(IQueryHandler<ProductGrabbedInfoByUserQuery, IEnumerable<ProductGrabbedInfo>> GrabberQuery,
                               IEventPublisher publisher)
        {
            this.GrabberQuery = GrabberQuery;
            this.publisher = publisher;
        }
    
        public void ParseWebData(int userId)
        {
            // получаем данные из БД
            var qry = new ProductGrabbedInfoByUserQuery { UserId = 1 }; // пока что один юзер
            var data = GrabberQuery.Handle(qry).ToList();
            if (data.Any())
            {
                // собираем информацию
                GrabberEngine ge = new GrabberEngine(BrowserType.Mozilla);
                ge.Grab(data);
                // преобразовываем в наш вид с собиранием логирования и ошибок
                var converted = ConvertFromGrabbedInfo(data);
                // HERE I THINK WE SHOULD CALL publisher.Publish(new GrabbingCompletedEvent{...}) to execute 3 points below:
                // 1. Send email message using List<string> emailMessages info
                // 2. Log messages using List<string> logMessages info
                // 3. add collected data to DB
            }
        }
    }
    

    如果我们需要将事件数据最大化,那么我们需要存储一些小值:

    public class GrabbingCompletedEvent : IDomainEvent
    {
        public readonly string JobId;
        public readonly int UserId;
    
        public GrabbingCompletedEvent(string JobId, int UserId)
        {
            this.JobId = JobId;
            this.UserId = UserId;
        }
    }
    

    好的,那么我认为我们需要创建事件处理程序:

    public class NotifyEmailGrabbingCompletedEventHandler : IEventHandler<GrabbingCompletedEvent>
    {
        private IEmailService emailService;
        private readonly ILogger logger;
        private IEnumerable<string> messages;
        private IQueryHandler<GetByPrimaryKeyQuery<User>, User> UserQuery;
    
        public NotifyEmailGrabbingCompletedEventHandler(IEmailService emailService, 
            ILogger logger, 
            IEnumerable<string> messages, 
            IQueryHandler<GetByPrimaryKeyQuery<User>, User> UserQuery)
        {
            this.emailService = emailService;
            this.logger = logger;
            this.messages = messages;
            this.UserQuery = UserQuery;
        }
    
        public void Handle(GrabbingCompletedEvent e)
        {
            var user = GetUserInfo(e.UserId);
            emailService.Send(user.Email, string.Join(Environment.NewLine, messages));
            emailService.SetDelivered(true);
        }
    
        private User GetUserInfo(int userId)
        {
            var cmd = new GetByPrimaryKeyQuery<User> { PK = userId };
            return UserQuery.Handle(cmd);
        }
    }
    
    public class LogGrabbingCompletedEventHandler : IEventHandler<GrabbingCompletedEvent>
    {
        private readonly ILogger logger;
        private IEnumerable<string> messages;
    
        public LogGrabbingCompletedEventHandler(ILogger logger, IEnumerable<string> messages)
        {
            this.logger = logger;
            this.messages = messages;
        }
    
        public void Handle(GrabbingCompletedEvent e)
        {
            foreach (var message in messages)
            {
                this.logger.Log(message);
            }
        }
    }
    

    我的问题是如何将记录器实例,消息列表和queryHandler传递给当前处理程序?我是否正确在事件处理程序构造函数中传递此类参数?我遇到的第一个变体是将这些数据作为属性添加到事件类中,但这似乎是错误的决定。请问,请解释一下这种情况下哪种设计方法会更好。

1 个答案:

答案 0 :(得分:0)

如果您正在使用像SimpleInjector这样的IoC容器,那么事件处理程序类应该由容器解析,反过来应该从容器中解析所有依赖项。这使事件处理程序与引发事件的类分离。事件类只需要包含描述事件的数据,而不是任何其他依赖项。

所以是的,事件处理程序的构造函数将是放置其依赖项的正确位置,就像引发事件的类一样。

我使用了IEventBus接口,我将其注入到类中 - 与IEventPublisher几乎相同。该实现解析了向容器注册的IEventHandler<T>的实现。这样一切都可以从容器中解决。

Here's an implementation I use。事件总线本身与容器无关。如果您正在使用DI,则需要IHandlerProvider实现来解析使用容器的处理程序。我的实现使用Windsor。但设计是您可以使用不同的容器来解析处理程序。您只需编写IHandlerProvider的其他实现。