private void DispatchMessages(UndispatchedMessages undispatched, List <Envelope <ICommand> > deserializedCommands = null) { if (undispatched != null) { if (deserializedCommands == null) { deserializedCommands = this.serializer.Deserialize <IEnumerable <Envelope <ICommand> > >(undispatched.Commands).ToList(); } var originalCommandsCount = deserializedCommands.Count; try { while (deserializedCommands.Count > 0) { this.commandBus.Send(deserializedCommands.First()); deserializedCommands.RemoveAt(0); } } catch (Exception) { // We catch a generic exception as we don't know what implementation of ICommandBus we might be using. if (originalCommandsCount != deserializedCommands.Count) { // if we were able to send some commands, then updates the undispatched messages. undispatched.Commands = this.serializer.Serialize(deserializedCommands); try { this.context.SaveChanges(); } catch (DbUpdateConcurrencyException) { // if another thread already dispatched the messages, ignore and surface original exception instead } } throw; } // we remove all the undispatched messages for this process manager. this.context.Set <UndispatchedMessages>().Remove(undispatched); this.retryPolicy.ExecuteAction(() => this.context.SaveChanges()); } }
/// <summary> /// Saves the state of the process manager and publishes the commands in a resilient way. /// </summary> /// <param name="processManager">The instance to save.</param> /// <remarks>For explanation of the implementation details, see <see cref="http://go.microsoft.com/fwlink/p/?LinkID=258557"> Journey chapter 7</see>.</remarks> public void Save(T processManager) { var entry = this.context.Entry(processManager); if (entry.State == System.Data.Entity.EntityState.Detached) { this.context.Set <T>().Add(processManager); } var commands = processManager.Commands.ToList(); UndispatchedMessages undispatched = null; if (commands.Count > 0) { // if there are pending commands to send, we store them as undispatched. undispatched = new UndispatchedMessages(processManager.Id) { Commands = this.serializer.Serialize(commands) }; this.context.Set <UndispatchedMessages>().Add(undispatched); } try { this.retryPolicy.ExecuteAction(() => this.context.SaveChanges()); } catch (DbUpdateConcurrencyException e) { throw new ConcurrencyException(e.Message, e); } try { this.DispatchMessages(undispatched, commands); } catch (DbUpdateConcurrencyException) { // if another thread already dispatched the messages, ignore Trace.TraceWarning("Ignoring concurrency exception while marking commands as dispatched for process manager with ID {0} in Save method.", processManager.Id); } }