public ServiceWorkerActor(IActorRef parent) { Receive <SetWorkMessage>(message => { var senderClosure = Sender; var parentClosure = parent;// todo probably dont need to close over parent IConcurrentExecutorResponseMessage resultMessage; try { var workFactory = message.WorkFactory; if (workFactory.RunAsyncMethod) { workFactory.ExecuteAsync(message.Command) .ContinueWith(r => { if (r.IsFaulted) { resultMessage = new SetWorkErrorMessage("Unable to complete operation", message.Id); } else { var result = r.Result; if (workFactory.IsAFailedResult(result)) { resultMessage = new SetWorkErrorMessage("operation completed but client said its was a failed operation", message.Id); } else { resultMessage = new SetWorkSucceededMessage(result, message.Id); } } parentClosure.Tell(resultMessage); // because There is no active ActorContext, this is most likely due to use of async operations from within this actor. senderClosure.Tell(resultMessage); return(resultMessage); }, TaskContinuationOptions.AttachedToParent & TaskContinuationOptions.ExecuteSynchronously) ;// .PipeTo(senderClosure).PipeTo(parentClosure); } else { workFactory.Execute(); } } catch (Exception e) { resultMessage = new SetWorkErrorMessage(e.Message + " " + e.InnerException?.Message, message.Id); senderClosure.Tell(resultMessage); Context.Parent.Tell(resultMessage); } }); }
/// <summary> /// /// </summary> /// <typeparam name="TResult"></typeparam> /// <typeparam name="TCommand"></typeparam> /// <param name="id"></param> /// <param name="operation"></param> /// <param name="hasFailed"></param> /// <param name="returnExistingResultWhenDuplicateId"></param> /// <param name="maxExecutionTimePerAskCall"></param> /// <param name="transformResult">Also passed boolean if error contains existing result</param> /// <returns></returns> private async Task <ExecutionResult <TResult> > Execute <TResult, TCommand>(string id, TCommand command, Func <TCommand, Task <TResult> > operation, Func <TResult, bool> hasFailed = null, bool returnExistingResultWhenDuplicateId = true, TimeSpan?maxExecutionTimePerAskCall = null, Func <ExecutionResult <TResult>, TResult> transformResult = null, bool storeCommands = false) where TResult : class { if (operation == null) { throw new ArgumentNullException(nameof(operation)); } if (id == null) { throw new ArgumentNullException(nameof(id)); } IConcurrentExecutorResponseMessage result; var maxExecTime = maxExecutionTimePerAskCall ?? MaxExecutionTimePerAskCall; try { result = await ReceptionActorRef.Ask <IConcurrentExecutorResponseMessage>(new SetWorkMessage(id, command, new WorkFactory(async(o) => await operation((TCommand)o), (r) => hasFailed?.Invoke((TResult)r) ?? false), storeCommands), maxExecTime).ConfigureAwait(false); } catch (Exception e) { result = new SetWorkErrorMessage($"Operation execution timed out . execution time exceeded the set max execution time of {maxExecTime.TotalMilliseconds} ms to worker id: {id} ", id); } var finalResult = new ExecutionResult <TResult>(); if (result is SetWorkErrorMessage) { finalResult.Errors.Add((result as SetWorkErrorMessage).Error); } else if (result is SetCompleteWorkErrorMessage) { finalResult.Errors.Add((result as SetCompleteWorkErrorMessage).Error); if (returnExistingResultWhenDuplicateId) { finalResult.Result = (result as SetCompleteWorkErrorMessage)?.LastSuccessfullResult as TResult; } } else { finalResult.Succeeded = true; finalResult.Result = (result as SetWorkSucceededMessage)?.Result as TResult; } finalResult.Result = transformResult == null ? finalResult.Result : transformResult(finalResult); return(finalResult); }