示例#1
0
        public async Task OnCommandAsync(ICommand command, CommandContext context, CancellationToken cancellationToken)
        {
            var operation = context.OuterContext != null?context.Items.TryGet <IOperation>() : null;

            var mustBeLogged =
                operation != null && // Should be a nested context inside a context w/ operation
                InvalidationInfoProvider.RequiresInvalidation(command) && // Command requires invalidation
                !Computed.IsInvalidating();

            if (!mustBeLogged)
            {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

            var operationItems = operation !.Items;
            var commandItems   = new OptionSet();

            operation.Items = commandItems;
            Exception?error = null;

            try {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);
            }
            catch (Exception?e) {
                error = e;
                throw;
            }
示例#2
0
 protected virtual async ValueTask InvokeNestedCommands(
     CommandContext context,
     IOperation operation,
     ImmutableList <NestedCommandEntry> nestedCommands,
     CancellationToken cancellationToken)
 {
     foreach (var commandEntry in nestedCommands)
     {
         var(command, items) = commandEntry;
         if (command is IServerSideCommand serverSideCommand)
         {
             serverSideCommand.MarkServerSide(); // Server-side commands should be marked as such
         }
         if (InvalidationInfoProvider.RequiresInvalidation(command))
         {
             operation.Items = items;
             await context.Commander.Call(command, cancellationToken).ConfigureAwait(false);
         }
         var subcommands = items.GetOrDefault(ImmutableList <NestedCommandEntry> .Empty);
         if (!subcommands.IsEmpty)
         {
             await InvokeNestedCommands(context, operation, subcommands, cancellationToken);
         }
     }
 }
示例#3
0
 public NestedCommandLogger(
     InvalidationInfoProvider invalidationInfoProvider,
     ILogger <NestedCommandLogger>?log = null)
 {
     Log = log ?? NullLogger <NestedCommandLogger> .Instance;
     InvalidationInfoProvider = invalidationInfoProvider;
 }
示例#4
0
        public async Task OnCommandAsync(IInvalidateCommand command, CommandContext context, CancellationToken cancellationToken)
        {
            var skip = !IsEnabled ||
                       !InvalidationInfoProvider.RequiresInvalidation(command.UntypedCommand) ||
                       Computed.IsInvalidating();

            if (skip)
            {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

            using var _ = Computed.Invalidate();
            var finalHandler = context.ExecutionState.FindFinalHandler();

            if (finalHandler != null)
            {
                if (Log.IsEnabled(LogLevel.Debug))
                {
                    Log.LogDebug("Invalidating via dedicated command handler: {0}", command);
                }
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);
            }
            else
            {
                if (Log.IsEnabled(LogLevel.Debug))
                {
                    Log.LogDebug("Invalidating via shared command handler: {0}", command);
                }
                await context.Commander.RunAsync(command.UntypedCommand, cancellationToken).ConfigureAwait(false);
            }
        }
示例#5
0
        public async Task OnCommandAsync(ICommand command, CommandContext context, CancellationToken cancellationToken)
        {
            var skip = !IsEnabled ||
                       context.OuterContext != null || // Should be top-level command
                       command is IInvalidateCommand || // Second handler here will take care of it
                       Computed.IsInvalidating();

            if (skip)
            {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

            if (InvalidationInfoProvider.RequiresInvalidation(command))
            {
                context.Items.Set(InvalidateCommand.New(command));
            }

            await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

            var invalidate = context.Items.TryGet <IInvalidateCommand>();

            if (invalidate != null)
            {
                await context.Commander.RunAsync(invalidate, true, default).ConfigureAwait(false);
            }
        }
示例#6
0
 public InvalidateOnCompletionCommandHandler(Options?options,
                                             InvalidationInfoProvider invalidationInfoProvider,
                                             ILogger <InvalidateOnCompletionCommandHandler>?log = null)
 {
     options ??= new();
     Log      = log ?? NullLogger <InvalidateOnCompletionCommandHandler> .Instance;
     LogLevel = options.LogLevel;
     InvalidationInfoProvider = invalidationInfoProvider;
 }
示例#7
0
        public async Task OnCommandAsync(ICompletion command, CommandContext context, CancellationToken cancellationToken)
        {
            var originalCommand      = command.UntypedCommand;
            var requiresInvalidation =
                InvalidationInfoProvider.RequiresInvalidation(originalCommand) &&
                !Computed.IsInvalidating();

            if (!requiresInvalidation)
            {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

            var oldOperation = context.Items.TryGet <IOperation>();
            var operation    = command.Operation;

            context.SetOperation(operation);
            var invalidateScope = Computed.Invalidate();

            try {
                var logEnabled   = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel);
                var finalHandler = context.ExecutionState.FindFinalHandler();
                if (finalHandler != null)
                {
                    if (logEnabled)
                    {
                        Log.Log(LogLevel, "Invalidating via dedicated command handler for '{CommandType}'", command.GetType());
                    }
                    await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    if (logEnabled)
                    {
                        Log.Log(LogLevel, "Invalidating via shared command handler for '{CommandType}'", originalCommand.GetType());
                    }
                    await context.Commander.CallAsync(originalCommand, cancellationToken).ConfigureAwait(false);
                }

                var operationItems = operation.Items;
                try {
                    var nestedCommands = operationItems.GetOrDefault(ImmutableList <NestedCommandEntry> .Empty);
                    if (!nestedCommands.IsEmpty)
                    {
                        await InvokeNestedCommandsAsync(context, operation, nestedCommands, cancellationToken);
                    }
                }
                finally {
                    operation.Items = operationItems;
                }
            }
            finally {
                context.SetOperation(oldOperation);
                invalidateScope.Dispose();
            }
        }
示例#8
0
        public InvalidateOnCompletionCommandHandler(Options?options,
                                                    InvalidationInfoProvider invalidationInfoProvider,
                                                    ILogger <InvalidateOnCompletionCommandHandler>?log = null)
        {
            options ??= new();
            Log = log ?? NullLogger <InvalidateOnCompletionCommandHandler> .Instance;
            IsLoggingEnabled = options.IsLoggingEnabled && Log.IsEnabled(LogLevel);

            InvalidationInfoProvider = invalidationInfoProvider;
        }
示例#9
0
        public async Task OnCommandAsync(IInvalidateCommand command, CommandContext context, CancellationToken cancellationToken)
        {
            var skip = !IsEnabled ||
                       !InvalidationInfoProvider.RequiresInvalidation(command.UntypedCommand) ||
                       Computed.IsInvalidating();

            if (skip)
            {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

            // Copying IOperation.Items to CommandContext.Items
            var operation      = command.Operation;
            var operationItems = operation?.Items.Items;

            if (operationItems != null)
            {
                foreach (var(key, value) in operationItems)
                {
                    if (value is IOperationItem)
                    {
                        context.Items[key] = value;
                    }
                }
            }

            var logEnabled = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel);

            using var _ = Computed.Invalidate();

            var finalHandler = context.ExecutionState.FindFinalHandler();

            if (finalHandler != null)
            {
                if (logEnabled)
                {
                    Log.Log(LogLevel, "Invalidating via dedicated command handler: {0}", command);
                }
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);
            }
            else
            {
                if (logEnabled)
                {
                    Log.Log(LogLevel, "Invalidating via shared command handler: {0}", command);
                }
                await context.Commander.RunAsync(command.UntypedCommand, cancellationToken).ConfigureAwait(false);
            }
        }
示例#10
0
 public virtual void OnOperationCompleted(IOperation operation)
 {
     if (operation.AgentId == AgentInfo.Id.Value)
     {
         // Local operations are invalidated by InvalidationHandler
         return;
     }
     if (!(operation.Command is ICommand command))
     {
         return;
     }
     if (!InvalidationInfoProvider.RequiresInvalidation(command))
     {
         return;
     }
     if (Log.IsEnabled(LogLevel.Debug))
     {
         Log.LogDebug("Invalidating operation: agent {0}, command {1}", operation.AgentId, command);
     }
     Commander.Start(InvalidateCommand.New(command, operation), true);
 }
示例#11
0
        public async Task OnCommand(ICompletion command, CommandContext context, CancellationToken cancellationToken)
        {
            var originalCommand      = command.UntypedCommand;
            var requiresInvalidation =
                InvalidationInfoProvider.RequiresInvalidation(originalCommand) &&
                !Computed.IsInvalidating();

            if (!requiresInvalidation)
            {
                await context.InvokeRemainingHandlers(cancellationToken).ConfigureAwait(false);

                return;
            }

            var oldOperation = context.Items.TryGet <IOperation>();
            var operation    = command.Operation;

            context.SetOperation(operation);
            var invalidateScope = Computed.Invalidate();

            try {
                var finalHandler = context.ExecutionState.FindFinalHandler();
                var useOriginalCommandHandler = finalHandler == null ||
                                                finalHandler.GetHandlerService(command, context) is CatchAllCompletionHandler;
                if (useOriginalCommandHandler)
                {
                    if (InvalidationInfoProvider.IsReplicaServiceCommand(originalCommand))
                    {
                        if (IsLoggingEnabled)
                        {
                            Log.Log(LogLevel, "No invalidation for replica service command '{CommandType}'",
                                    originalCommand.GetType());
                        }
                        return;
                    }
                    if (IsLoggingEnabled)
                    {
                        Log.Log(LogLevel, "Invalidating via original command handler for '{CommandType}'",
                                originalCommand.GetType());
                    }
                    await context.Commander.Call(originalCommand, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    if (IsLoggingEnabled)
                    {
                        Log.Log(LogLevel, "Invalidating via dedicated command handler for '{CommandType}'",
                                command.GetType());
                    }
                    await context.InvokeRemainingHandlers(cancellationToken).ConfigureAwait(false);
                }

                var operationItems = operation.Items;
                try {
                    var nestedCommands = operationItems.GetOrDefault(ImmutableList <NestedCommandEntry> .Empty);
                    if (!nestedCommands.IsEmpty)
                    {
                        await InvokeNestedCommands(context, operation, nestedCommands, cancellationToken);
                    }
                }
                finally {
                    operation.Items = operationItems;
                }
            }
            finally {
                context.SetOperation(oldOperation);
                invalidateScope.Dispose();
            }
        }
示例#12
0
        public async Task OnCommandAsync(ICommand command, CommandContext context, CancellationToken cancellationToken)
        {
            var skip = context.OuterContext != null || // Should be top-level command
                       command is IInvalidateCommand || // Second handler here will take care of it
                       Computed.IsInvalidating();

            if (skip)
            {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                return;
            }

            var tScope = typeof(IDbOperationScope <TDbContext>);

            if (context.Items[tScope] != null) // Safety check
            {
                throw Stl.Internal.Errors.InternalError($"'{tScope}' scope is already provided. Duplicate handler?");
            }

            var logEnabled = LogLevel != LogLevel.None && Log.IsEnabled(LogLevel);

            await using var scope = Services.GetRequiredService <IDbOperationScope <TDbContext> >();
            scope.Command         = command;
            context.Items.Set(scope);
            if (logEnabled)
            {
                Log.Log(LogLevel, "+ Operation started: {0}", command);
            }

            IOperation?operation = null;

            try {
                await context.InvokeRemainingHandlersAsync(cancellationToken).ConfigureAwait(false);

                // Building IOperation.Items from CommandContext.Items
                foreach (var(key, value) in context.Items.Items)
                {
                    if (value is IOperationItem)
                    {
                        scope.Items = scope.Items.Set(key, value);
                    }
                }
                operation = await scope.CommitAsync(cancellationToken);

                if (logEnabled)
                {
                    Log.Log(LogLevel, "- Operation succeeded: {0}", command);
                }
            }
            catch (OperationCanceledException) {
                throw;
            }
            catch (Exception e) {
                Log.LogError(e, "! Operation failed: {0}", command);
                try {
                    await scope.RollbackAsync();
                }
                catch {
                    // Intended
                }
                throw;
            }
            if (operation != null)
            {
                if (InvalidationInfoProvider?.RequiresInvalidation(command) ?? false)
                {
                    context.Items.Set(InvalidateCommand.New(command, operation));
                }
                OperationCompletionNotifier?.NotifyCompleted(operation);
            }
        }