/// <summary> /// Initializes a new instance of the <see cref="ChainedContextStateMachine"/> class. /// </summary> /// <param name="onlineContext">The online context.</param> /// <param name="offlineContext">The offline context.</param> /// <param name="getCachedEntityFuncAsync">The function delegate to get cached entities.</param> /// <param name="contextState">The initial context state.</param> /// <param name="reconnectionInterval">The time interval for the next connection attempt.</param> public ChainedContextStateMachine(IContext onlineContext, IContext offlineContext, Func <Type, Task <object> > getCachedEntityFuncAsync, ChainedContextState contextState, TimeSpan reconnectionInterval) { if (getCachedEntityFuncAsync == null) { throw new ArgumentNullException("getCachedEntityFuncAsync"); } if (onlineContext == null && (contextState != ChainedContextState.OfflineOnly)) { throw new InvalidOperationException("Online context can't be null when chained context is initialized in online mode."); } if (offlineContext == null && contextState == ChainedContextState.OfflineOnly) { throw new InvalidOperationException("Offline context can't be null when chained context is initialized in offline mode."); } this.reconnectionInterval = reconnectionInterval; this.OnlineContext = onlineContext; this.OfflineContext = offlineContext; this.getCachedEntityFuncAsync = getCachedEntityFuncAsync; this.forcedToOffline = false; this.contextState = contextState; this.InitTransitionMap(); }
public StateTransition(ChainedContextState start, ChainedContextState stop, StateTransitionEvent allowedEvents, StateTransitionAction action) { this.Start = start; this.Stop = stop; this.AllowedEvents = allowedEvents; this.Action = action; }
/// <summary> /// Switches to online mode asynchronously. /// </summary> /// <param name="targetState">The target state.</param> /// <returns>No return.</returns> private async Task SwitchToOnlineAsync(ChainedContextState targetState) { await this.TransferShiftToOnlineAsync(); this.SetState(targetState); // Delete the offline shift after transition is done. await this.DeleteOfflineShiftAsync(); }
/// <summary> /// Switches to offline mode asynchronously. /// </summary> /// <param name="targetState">The target state.</param> /// <returns>The result object.</returns> private async Task SwitchToOfflineAsync(ChainedContextState targetState) { await this.TransferShiftToOfflineAsync(); await this.TransferCartToOfflineAsync(); // Change the transition state since the shift and cart have been transfered. this.SetState(targetState); }
private void SetState(ChainedContextState state) { this.contextState = state; }
/// <summary> /// Switches to online mode and executes the method asynchronously. If failed, resubmit the request in offline mode. /// </summary> /// <typeparam name="TResult">The type of result object.</typeparam> /// <param name="entitySet">The target entity set.</param> /// <param name="action">The action name.</param> /// <param name="method">The method to be executed.</param> /// <param name="targetState">The target state.</param> /// <returns>The result object.</returns> private async Task <TResult> SwitchToOnlineAndExecuteIfFailedTriggerOfflineRequestAsync <TResult>(string entitySet, string action, Func <IContext, Task <TResult> > method, ChainedContextState targetState) { try { TResult result; // For logon request, transfer request can only be done after it succeeds. if (action.Equals(CommerceAuthenticationProvider.AcquireTokenActionName, StringComparison.OrdinalIgnoreCase)) { result = await method(this.OnlineContext); await this.TransferShiftToOnlineAsync(); } else { await this.TransferShiftToOnlineAsync(); result = await method(this.OnlineContext); } this.SetState(targetState); // Delete the offline shift after transition is done. await this.DeleteOfflineShiftAsync(); return(result); } catch (CommunicationException ex) { if (!this.ShouldTryOffline(ex)) { throw; } } return(await this.MoveNextAsync(entitySet, action, method : method, isRequestOffline : true)); }
/// <summary> /// Switches to offline mode and executes the method asynchronously. /// </summary> /// <typeparam name="TResult">The type of the result object.</typeparam> /// <param name="action">The action name.</param> /// <param name="method">The method to be executed.</param> /// <param name="targetState">The target state.</param> /// <returns>The result object.</returns> private async Task <TResult> SwitchToOfflineAndExecuteAsync <TResult>(string action, Func <IContext, Task <TResult> > method, ChainedContextState targetState) { this.SetState(targetState); TResult result; // For logon request, transfer request can only be done after it succeeds. if (action.Equals(CommerceAuthenticationProvider.AcquireTokenActionName, StringComparison.OrdinalIgnoreCase)) { result = await method(this.OfflineContext); await this.TransferShiftToOfflineAsync(); await this.TransferCartToOfflineAsync(); } else { await this.TransferShiftToOfflineAsync(); await this.TransferCartToOfflineAsync(); result = await method(this.OfflineContext); } return(result); }
/// <summary> /// Executes the method in offline mode asynchronously. /// </summary> /// <typeparam name="TResult">The type of the result object.</typeparam> /// <param name="method">The method to be executed.</param> /// <param name="targetState">The target state.</param> /// <returns>The result object.</returns> private async Task <TResult> ExecuteOfflineAsync <TResult>(Func <IContext, Task <TResult> > method, ChainedContextState targetState) { var result = await method(this.OfflineContext); this.SetState(targetState); return(result); }
/// <summary> /// Executes the method in both online and offline mode asynchronously. /// </summary> /// <typeparam name="TResult">The type of the result object.</typeparam> /// <param name="method">The method to be executed.</param> /// <param name="targetState">The target state.</param> /// <returns>The result object.</returns> private async Task <TResult> ExecuteBothOnlineAndOfflineAsync <TResult>(Func <IContext, Task <TResult> > method, ChainedContextState targetState) { var tasks = new Task <TResult> [2]; Exception exceptionThrownInOnlineCall = null, exceptionThrownInOfflineCall = null; // Online task. tasks[0] = Task.Run(async() => { try { return(await method(this.OnlineContext)); } catch (Exception e) { exceptionThrownInOnlineCall = e; } return(default(TResult)); }); // Offline task. tasks[1] = Task.Run(async() => { try { return(await method(this.OfflineContext)); } catch (Exception e) { exceptionThrownInOfflineCall = e; } return(default(TResult)); }); await Task.WhenAll(tasks); if (exceptionThrownInOnlineCall != null) { // Return offline result if online failed but offline succeeded. if (exceptionThrownInOfflineCall == null) { await this.SwitchToOfflineAsync(); // Return offline result. return(await tasks[1]); } else { // Throw online exception if both online and offline failed. throw exceptionThrownInOnlineCall; } } var onlineResult = await tasks[0]; if (exceptionThrownInOfflineCall != null) { // Handle the offline request failure silently. await this.MoveNextAsync(StateTransitionEvent.OfflineAuthenticationFailed); return(onlineResult); } // Both online and offline succeed. this.SetState(targetState); return(onlineResult); }
/// <summary> /// Executes the method in online mode asynchronously. If failed, resubmit it as offline. /// </summary> /// <typeparam name="TResult">The type of the result object.</typeparam> /// <param name="entitySet">The target entity set.</param> /// <param name="action">The action name.</param> /// <param name="method">The method to be executed.</param> /// <param name="targetState">The target state.</param> /// <returns>The result object.</returns> private async Task <TResult> ExecuteOnlineIfFailedTriggerOfflineRequestAsync <TResult>(string entitySet, string action, Func <IContext, Task <TResult> > method, ChainedContextState targetState) { try { var result = await method(this.OnlineContext); this.SetState(targetState); return(result); } catch (CommunicationException ex) { if (!this.ShouldTryOffline(ex)) { throw; } } return(await this.MoveNextAsync(entitySet, action, method : method, isRequestOffline : true)); }
public ChainedContext(IContext onlineContext, IContext offlineContext, Func <Type, Task <object> > getCachedEntityFunc, ChainedContextState initialState, TimeSpan reconnectionInterval) { this.contextStateMachine = new ChainedContextStateMachine(onlineContext, offlineContext, getCachedEntityFunc, initialState, reconnectionInterval); }