private async IAsyncEnumerable <CommandResponse> InvokeCore(CommandRequest request, bool relayEvents, [EnumeratorCancellation] CancellationToken cancellationToken = default) { if (request.CommandTypeName == null) { throw new ArgumentException("Command type is not specified.", nameof(request)); } var commandType = Type.GetType(request.CommandTypeName + "," + s_serviceContractAssemblyName, throwOnError: true) !; if (!commandType.HasInterface(typeof(ICommand))) { throw new ArgumentException("Command type is invalid.", nameof(request)); } var command = (ICommand)ServiceHostContractSerializer.Default.Deserialize(request.SerializedCommand ?? Array.Empty <byte>(), commandType); ServiceErrorException?errorException = null; object?key = null; Action <ICommand, object>?handleKeyGenerated; var keyGeneratorCommand = command as IKeyGeneratorCommand; if (keyGeneratorCommand != null) { handleKeyGenerated = (_, k) => key = k; keyGeneratorCommand.OnKeyGenerated += handleKeyGenerated; } else { handleKeyGenerated = null; } try { Task dispatchTask; if (relayEvents && command is IEventProducerCommand eventProducerCommand) { var eventChannel = Channel.CreateUnbounded <Event>(s_eventChannelOptions); eventProducerCommand.OnEvent = (_, @event) => eventChannel.Writer.TryWrite(@event); dispatchTask = Task.Run(async() => { try { await _commandDispatcher.DispatchAsync(command, cancellationToken); } finally { eventChannel.Writer.Complete(); } }); while (await eventChannel.Reader.WaitToReadAsync(cancellationToken)) { while (eventChannel.Reader.TryRead(out var @event)) { yield return new CommandResponse.Notification { Event = new EventData { Value = @event } } } } ; } else { dispatchTask = _commandDispatcher.DispatchAsync(command, cancellationToken); } try { await dispatchTask; } catch (ServiceErrorException ex) { errorException = ex; } } finally { if (keyGeneratorCommand != null) { keyGeneratorCommand.OnKeyGenerated -= handleKeyGenerated; } } if (errorException != null) { yield return new CommandResponse.Failure { Error = errorException.ToData() } } ; else { yield return new CommandResponse.Success { Key = key != null?KeyData.From(key) : null } }; }