DispatchMessages方法还从SqlProcessDataContext类中的Find方法调用,以便当系统重新还原(rehydrates)RegistrationProcessManager实例时,它会尝试发送任何未发送的消息。
优化UI流程第一个优化是允许UI直接导航到注册者页面,前提是会议还有很多座位可用。RegistrationController类的StartRegistration方法介绍了这个变化,它现在会在创建预定并发送RegisterToConference命令之前执行一个额外的检查,确认有足够的剩余座位,如下面的代码示例所示:
[HttpPost] public ActionResult StartRegistration(RegisterToConference command, int orderVersion) { var existingOrder = orderVersion != 0 ? this.orderDao.FindDraftOrder(command.OrderId) : null; var viewModel = existingOrder == null ? this.CreateViewModel() : this.CreateViewModel(existingOrder); viewModel.OrderId = command.OrderId; if (!ModelState.IsValid) { return View(viewModel); } // Checks that there are still enough available seats, and the seat type IDs submitted are valid. ModelState.Clear(); bool needsExtraValidation = false; foreach (var seat in command.Seats) { var modelItem = viewModel.Items.FirstOrDefault(x => x.SeatType.Id == seat.SeatType); if (modelItem != null) { if (seat.Quantity > modelItem.MaxSelectionQuantity) { modelItem.PartiallyFulfilled = needsExtraValidation = true; modelItem.OrderItem.ReservedSeats = modelItem.MaxSelectionQuantity; } } else { // Seat type no longer exists for conference. needsExtraValidation = true; } } if (needsExtraValidation) { return View(viewModel); } command.ConferenceId = this.ConferenceAlias.Id; this.commandBus.Send(command); return RedirectToAction( "SpecifyRegistrantAndPaymentDetails", new { conferenceCode = this.ConferenceCode, orderId = command.OrderId, orderVersion = orderVersion }); }如果没有足够的可用座位,控制器将重新显示当前屏幕,显示当前可用的座位数量,以便注册者修改其订单。
更改的其余部分在RegistrationController类中的SpecifyRegistrantAndPaymentDetails方法中。下面来自V2版本的代码示例显示,在优化之前,控制器在继续跳转到注册页面之前调用WaitUntilSeatsAreConfirmed方法:
[HttpGet] [OutputCache(Duration = 0, NoStore = true)] public ActionResult SpecifyRegistrantAndPaymentDetails(Guid orderId, int orderVersion) { var order = this.WaitUntilSeatsAreConfirmed(orderId, orderVersion); if (order == null) { return View("ReservationUnknown"); } if (order.State == DraftOrder.States.PartiallyReserved) { return this.RedirectToAction("StartRegistration", new { conferenceCode = this.ConferenceCode, orderId, orderVersion = order.OrderVersion }); } if (order.State == DraftOrder.States.Confirmed) { return View("ShowCompletedOrder"); } if (order.ReservationExpirationDate.HasValue && order.ReservationExpirationDate < DateTime.UtcNow) { return RedirectToAction("ShowExpiredOrder", new { conferenceCode = this.ConferenceAlias.Code, orderId = orderId }); } var pricedOrder = this.WaitUntilOrderIsPriced(orderId, orderVersion); if (pricedOrder == null) { return View("ReservationUnknown"); } this.ViewBag.ExpirationDateUTC = order.ReservationExpirationDate; return View( new RegistrationViewModel { RegistrantDetails = new AssignRegistrantDetails { OrderId = orderId }, Order = pricedOrder }); }下面的代码示例显示了这个方法的V3版本,它不再等待预订被确认:
[HttpGet] [OutputCache(Duration = 0, NoStore = true)] public ActionResult SpecifyRegistrantAndPaymentDetails(Guid orderId, int orderVersion) { var pricedOrder = this.WaitUntilOrderIsPriced(orderId, orderVersion); if (pricedOrder == null) { return View("PricedOrderUnknown"); } if (!pricedOrder.ReservationExpirationDate.HasValue) { return View("ShowCompletedOrder"); } if (pricedOrder.ReservationExpirationDate < DateTime.UtcNow) { return RedirectToAction("ShowExpiredOrder", new { conferenceCode = this.ConferenceAlias.Code, orderId = orderId }); } return View( new RegistrationViewModel { RegistrantDetails = new AssignRegistrantDetails { OrderId = orderId }, Order = pricedOrder }); } 备注:我们将在稍后的旅程中使这个方法异步。UI流程的第二个优化是在流程的前面执行订单总数的计算。在上面的代码示例中,SpecifyRegistrantAndPaymentDetails方法仍然调用WaitUntilOrderIsPriced方法,这将暂停界面流直到系统计算出订单的总数并使其可用于控制器(在读端保存在priced-order视图模型中)。