我一直在考虑在我的网络应用中实现事件模型。
我需要实施的方法是:
根据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传递给当前处理程序?我是否正确在事件处理程序构造函数中传递此类参数?我遇到的第一个变体是将这些数据作为属性添加到事件类中,但这似乎是错误的决定。请问,请解释一下这种情况下哪种设计方法会更好。
答案 0 :(得分:0)
如果您正在使用像SimpleInjector这样的IoC容器,那么事件处理程序类应该由容器解析,反过来应该从容器中解析所有依赖项。这使事件处理程序与引发事件的类分离。事件类只需要包含描述事件的数据,而不是任何其他依赖项。
所以是的,事件处理程序的构造函数将是放置其依赖项的正确位置,就像引发事件的类一样。
我使用了IEventBus
接口,我将其注入到类中 - 与IEventPublisher
几乎相同。该实现解析了向容器注册的IEventHandler<T>
的实现。这样一切都可以从容器中解决。
Here's an implementation I use。事件总线本身与容器无关。如果您正在使用DI,则需要IHandlerProvider
实现来解析使用容器的处理程序。我的实现使用Windsor。但设计是您可以使用不同的容器来解析处理程序。您只需编写IHandlerProvider
的其他实现。