public virtual void OnOperationCompleted(IOperation operation) { if (operation.AgentId == AgentInfo.Id.Value) { return; // Local completions are handled by LocalCompletionProducer } if (!(operation.Command is ICommand command)) { return; // We can't complete non-commands } if (command is IServerSideCommand serverSideCommand) { serverSideCommand.MarkServerSide(); // Server-side commands should be marked as such } Task.Run(async() => { try { await Commander.CallAsync(Completion.New(operation), true).ConfigureAwait(false); var logEnabled = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel); if (logEnabled) { Log.Log(LogLevel, "External operation completion succeeded. Agent: '{AgentId}', Command: {Command}", operation.AgentId, command); } } catch (Exception e) { Log.LogError(e, "External operation completion failed! Agent: '{AgentId}', Command: {Command}", operation.AgentId, command); } }); }
public virtual Task OnOperationCompleted(IOperation operation) { if (!(operation.Command is ICommand command)) { return(Task.CompletedTask); // We can't complete non-commands } return(Task.Run(async() => { var isLocal = operation.AgentId == AgentInfo.Id.Value; var operationType = isLocal ? "Local" : "External"; try { if (command is IServerSideCommand serverSideCommand) { serverSideCommand.MarkServerSide(); // Server-side commands should be marked as such } await Commander.Call(Completion.New(operation), true).ConfigureAwait(false); var logEnabled = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel); if (logEnabled) { Log.Log(LogLevel, "{OperationType} operation completion succeeded. Agent: '{AgentId}', Command: {Command}", operationType, operation.AgentId, command); } } catch (Exception e) { Log.LogError(e, "{OperationType} operation completion failed! Agent: '{AgentId}', Command: {Command}", operationType, operation.AgentId, command); } })); }
public virtual Task OnOperationCompleted(IOperation operation) { if (operation.Command is not ICommand command) { return(Task.CompletedTask); // We can't complete non-commands } return(Task.Run(async() => { var isLocal = StringComparer.Ordinal.Equals(operation.AgentId, AgentInfo.Id.Value); var operationType = isLocal ? "Local" : "External"; try { // if (command is IBackendCommand backendCommand) // backendCommand.MarkValid(); await Commander.Call(Completion.New(operation), true).ConfigureAwait(false); if (IsLoggingEnabled) { Log.Log(LogLevel, "{OperationType} operation completion succeeded. Agent: '{AgentId}', Command: {Command}", operationType, operation.AgentId, command); } } catch (Exception e) { Log.LogError(e, "{OperationType} operation completion failed! Agent: '{AgentId}', Command: {Command}", operationType, operation.AgentId, command); } })); }
public async Task OnCommandAsync(ICommand command, CommandContext context, CancellationToken cancellationToken) { var operationRequired = context.OuterContext == null && // Should be top-level command !(command is IMetaCommand) && // No operations for "second-order" commands !Computed.IsInvalidating(); if (!operationRequired) { await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false); return; } var tScope = typeof(DbOperationScope <TDbContext>); if (context.Items[tScope] != null) // Safety check { throw Stl.Internal.Errors.InternalError($"'{tScope}' scope is already provided. Duplicate handler?"); } await using var scope = Services.GetRequiredService <DbOperationScope <TDbContext> >(); var operation = scope.Operation; operation.Command = command; context.Items.Set(scope); var logEnabled = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel); try { await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false); await scope.CommitAsync(cancellationToken); } catch (OperationCanceledException) { throw; } catch (Exception e) { if (scope.IsUsed) { Log.LogError(e, "Operation failed: {Command}", command); } try { await scope.RollbackAsync(); } catch { // Intended } throw; } if (scope.IsUsed) { if (logEnabled) { Log.Log(LogLevel, "Operation succeeded: {Command}", command); } var completion = Completion.New(operation); context.Items.Set(completion); OperationCompletionNotifier.NotifyCompleted(operation); try { await context.Commander.CallAsync(completion, true, default).ConfigureAwait(false); } catch (Exception e) { Log.LogError(e, "Local operation completion failed! Command: {Command}", command); // No throw: the operation itself succeeded } } }
public async Task OnCommandAsync(ICommand command, CommandContext context, CancellationToken cancellationToken) { var operationRequired = context.OuterContext == null && // Should be top-level command !(command is IMetaCommand) && // No operations for "second-order" commands !Computed.IsInvalidating(); if (!operationRequired) { await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false); return; } await using var scope = Services.GetRequiredService <LocalOperationScope>(); var operation = scope.Operation; operation.Command = command; context.Items.Set(scope); context.SetOperation(operation); var logEnabled = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel); try { await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false); await scope.CommitAsync(cancellationToken); } catch (OperationCanceledException) { throw; } catch (Exception e) { if (scope.IsUsed) { Log.LogError(e, "Operation failed: {Command}", command); } await scope.RollbackAsync(); throw; } // Since this is the outermost scope handler, it's reasonable to run the // ICompletion command right from it for any other scope too. var completion = context.Items.TryGet <ICompletion>(); if (completion == null) // Also means scope.IsUsed == true { if (logEnabled) { Log.Log(LogLevel, "Operation succeeded: {Command}", command); } completion = Completion.New(operation); OperationCompletionNotifier.NotifyCompleted(operation); try { await context.Commander.CallAsync(completion, true, default).ConfigureAwait(false); } catch (Exception e) { Log.LogError(e, "Local operation completion failed! Command: {Command}", command); // No throw: the operation itself succeeded } } }