使用ASP.NET Core怎么实现单体程序的事件发布?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
public void SubmitShoppingCart(string shoppingCartId)
{
var shoppingCart = _unitOfWork.ShoppingCartRepository
.GetShoppingCart(shoppingCartId);
_unitOfWork.ShoppingCartRepository
.SubmitShoppingCart(shoppingCartId);
_unitOfWork.OrderRepository
.CreatOrder(new CreateOrderDTO
{
Items = shoppingCart.Items
.Select(p => new NewOrderItemDTO
{
ItemId = p.ItemId,
Name = p.Name,
Price = p.Price
}).ToList()
});
//这里为了简化代码,我用命令行表示发送邮件的逻辑
Console.WriteLine("Confirm Email Sent.");
_unitOfWork.Save();
}
根据SOLID设计原则中的单一责任原则,如果一个类承担的职责过多,就等于把这些职责耦合在一起了。这里生成订单和发送邮件都不应该是当前SubmitShoppingCart需要负责的,所以我们需要它们从这个方法中移出去,使用的方法就是事件订阅/发布。
新的架构图
以下是使用事件发布/订阅之后的系统架构图。
这里我们会创建一个购物车提交事件ShoppingCartSubmittedEvent。
当站点启动的时候,我们会在一个名为EventHandlerContainer的类中注册订阅ShoppingCartSubmittedEvent事件的2个处理类CreateOrderHandler和ConfirmEmailSentHandler。
在SubmitShoppingCart方法中,我们会做2件事情:
更改当前购物车的状态。
发布ShoppingCartSubmittedEvent事件。
CreateOrderHandler事件处理器会调用OrderManager类中的创建订单方法。
ConfirmEmailSentHandler事件处理器会负责发送邮件。
好的,下面让我们来一步一步实现以上描述的代码。
添加事件基类
这里我们首先定义一个事件基类,其中暂时只添加了一个属性OccuredOn,它表示了当前事件的触发时间。
public class EventBase
{
public EventBase()
{
OccuredOn = DateTime.Now;
}
protected DateTime OccuredOn
{
get;
set;
}
}
定义购物车提交事件
接下来我们就需要创建购物车提交事件类ShoppingCartSubmittedEvent, 它继承自EventBase, 并提供了一个购物项集合
public class ShoppingCartSubmittedEvent : EventBase
{
public ShoppingCartSubmittedEvent()
{
Items = new List<ShoppingCartSubmittedItem>();
}
public List<ShoppingCartSubmittedItem> Items { get; set; }
}
public class ShoppingCartSubmittedItem
{
public string ItemId { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
定义事件处理器接口
为了添加事件处理器,我们首先需要定义一个泛型接口类IEventHandler
public interface IEventHandler<T> where T : EventBase
{
void Run(T obj);
Task RunAsync(T obj);
}
这个泛型接口类的是泛型类型必须继承自EventBase类。接口提供了2个方法Run和RunAsync。 它们定义了该接口的实现类必须实现同一个处理逻辑的同步和异步方法。
为购物车提交事件编写事件处理器
有了事件处理器接口,接下来我们就可以开始为购物车提交事件添加事件处理器了。这里我们为了实现前面定义的逻辑,我们需要创建2个处理器CreateOrderHandler和ConfirmEmailSentHandler
CreateOrderHandler.cs
public class CreateOrderHandler : IEventHandler<ShoppingCartSubmittedEvent>
{
private IOrderManager _orderManager = null;
public CreateOrderHandler(IOrderManager orderManager)
{
_orderManager = orderManager;
}
public void Run(ShoppingCartSubmittedEvent obj)
{
_orderManager.CreateNewOrder(new Models.DTOs.CreateOrderDTO
{
Items = obj.Items.Select(p => new Models.DTOs.NewOrderItemDTO
{
ItemId = p.ItemId,
Name = p.Name,
Price = p.Price
}).ToList()
});
}
public Task RunAsync(ShoppingCartSubmittedEvent obj)
{
return Task.Run(() =>
{
Run(obj);
});
}
}
代码解释:
在CreateOrderHandler的构造函数中,我们注入了IOrderManager接口对象,CreateNewOrder负责最终创建订单的工作
这里为了简化代码,我直接使用了Task.Run,并在其中调用了同步方法实现
ConfirmEmailSentHandler.cs
public class ConfirmEmailSentHandler : IEventHandler<ShoppingCartSubmittedEvent>
{
public void Run(ShoppingCartSubmittedEvent obj)
{
Console.WriteLine("Confirm Email Sent.");
}
public Task RunAsync(ShoppingCartSubmittedEvent obj)
{
return Task.Run(() =>
{
Console.WriteLine("Confirm Email Sent.");
});
}
}
代码解释:
这个处理类非常简单,为了简化代码,我仅输出了一行文本来表示实际需要运行的代码。
为OrderManager类添加创建订单方法
IOrderManager.cs
public interface IOrderManager
{
string CreateNewOrder(CreateOrderDTO dto);
}
OrderManager.cs
public class OrderManager : IOrderManager
{
private IOrderRepository _orderRepository;
public OrderManager(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}
public string CreateNewOrder(CreateOrderDTO dto)
{
var orderId = _orderRepository.CreatOrder(dto);
Console.WriteLine($"One order created: {JsonConvert.SerializeObject(dto)}");
return orderId;
}
}
创建EventHandlerContainer
下面我们来编写最核心的事件处理器容器。在这里我们的事件处理器容器完成了3个功能
订阅事件(Subscribe Event)
取消订阅事件(Unsubscribe Event)
发布事件(Publish Event)
public class EventHandlerContainer
{
private IServiceProvider _serviceProvider = null;
private static Dictionary<string, List<Type>> _mappings = new Dictionary<string, List<Type>>();
public EventHandlerContainer(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public static void Subscribe<T, THandler>()
where T : EventBase
where THandler : IEventHandler<T>
{
var name = typeof(T).Name;
if (!_mappings.ContainsKey(name))
{
_mappings.Add(name, new List<Type> { });
}
_mappings[name].Add(typeof(THandler));
}
public static void Unsubscribe<T, THandler>()
where T : EventBase
where THandler : IEventHandler<T>
{
var name = typeof(T).Name;
_mappings[name].Remove(typeof(THandler));
if (_mappings[name].Count == 0)
{
_mappings.Remove(name);
}
}
public void Publish<T>(T o) where T : EventBase
{
var name = typeof(T).Name;
if (_mappings.ContainsKey(name))
{
foreach (var handler in _mappings[name])
{
var service = (IEventHandler<T>)_serviceProvider.GetService(handler);
service.Run(o);
}
}
}
public async Task PublishAsync<T>(T o) where T : EventBase
{
var name = typeof(T).Name;
if (_mappings.ContainsKey(name))
{
foreach (var handler in _mappings[name])
{
var service = (IEventHandler<T>)_serviceProvider.GetService(handler);
await service.RunAsync(o);
}
}
}
}
代码解释:
这里我没有直接订阅事件处理器的实例,而且订阅了事件处理器的类型
多个事件处理器可以订阅同一个事件
EventHandlerContainer的构造函数中,我们注入了一个IServiceProvider,我们可以使用它来获得对应事件处理器的实例。
在程序启动时,注册事件订阅
现在我们来Startup.cs的ConfigureServices方法,这里我们需要进行服务注册,并完成事件订阅。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddScoped<IOrderManager, OrderManager>();
services.AddScoped<IShoppingCartManager, ShoppingCartManager>();
services.AddScoped<IShoppingCartRepository, ShoppingCartRepository>();
services.AddScoped<IOrderRepository, OrderRepository>();
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddScoped<CreateOrderHandler>();
services.AddScoped<ConfirmEmailSentHandler>();
services.AddScoped<EventHandlerContainer>();
EventHandlerContainer.Subscribe<ShoppingCartSubmittedEvent, CreateOrderHandler>();
EventHandlerContainer.Subscribe<ShoppingCartSubmittedEvent, ConfirmEmailSentHandler>();
}
注意:这里保证一个Api请求中的所有数据库操作在一个事务里,这里我们使用Scoped作用域。这样我们就可以在调用工作单元IUnitOfWork接口的Save代码中启用事务。
修改ShoppingCartManager
最后我们来修改ShoppingCartManager, 改用发布事件的方式来完成后续创建订单和发送邮件的功能。
public void SubmitShoppingCart(string shoppingCartId)
{
var shoppingCart = _unitOfWork.ShoppingCartRepository
.GetShoppingCart(shoppingCartId);
_unitOfWork.ShoppingCartRepository
.SubmitShoppingCart(shoppingCartId);
_container.Publish(new ShoppingCartSubmittedEvent()
{
Items = shoppingCart
.Items
.Select(p => new ShoppingCartSubmittedItem
{
ItemId = p.ItemId,
Name = p.Name,
Price = p.Price
})
.ToList()
});
_unitOfWork.Save();
}
这样ShoppingCartManager就只需要关注购物车状态的变更,而不需要关注发送确认邮件和创建订单了。
最终效果
现在让我们启动项目,
首先我们使用[POST] /api/shoppingCarts来添加一个新的购物车, 这个API会返回当前购物车的Id
然后我们使用[PUT] /api/shoppingCarts/ShoppingCart_636872897140555966来模拟提交购物车,程序返回操作成功
最后我们查看一下控制台的输出日志
关于使用ASP.NET Core怎么实现单体程序的事件发布问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注亿速云行业资讯频道了解更多相关知识。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。