protected override async Task OnRecordTransactionEndAsync(Common.Tracking.TrackingContext context)
        {
            Ef.Entities.Transaction tx = await _dbContext.Transactions.FirstOrDefaultAsync(t => t.TransactionId == context.Request.TransactionId);

            if (null == tx)
            {
                return;             //what happened here? where is the transaction record???
            }
            //find last attempt
            Ef.Entities.Attempt attempt = null;

            if (context.TrackingState.ContainsKey(this))
            {
                object state = context.TrackingState[this];
                if (null != state && state is int)
                {
                    attempt = tx.Attempts.FirstOrDefault(i => i.AttemptId == (int)state);
                }
            }

            if (null == attempt)
            {
                attempt = tx.Attempts.LastOrDefault();                  // should this ever happen or be allowed?
            }
            if (null != attempt)
            {
                attempt.EndingStatus = context.Result.Status;
                attempt.EndTimeUtc   = context.EndTime;

                await _dbContext.SaveChangesAsync();
            }
        }
        public async Task <ProcessResult> ProcessRequestAsync(T request, CancellationToken token)
        {
            if (null == request)
            {
                throw new ArgumentNullException(nameof(request));
            }
            //request.Validate();

            ProcessResult result = new ProcessResult()
            {
                Status = ProcessResult.ProcessingStatus.Pending
            };

            using (Common.Tracking.TrackingContext context = Tracker.GetTrackingContext(request, result))
            {
                try
                {
                    await OnProcessRequestAsync(request, result, token);
                }
                catch (Exception ex)
                {
                    result.Status      = ProcessResult.ProcessingStatus.Failed;
                    result.Description = ex.Message;
                }

                //remove Pending...
                result.Status &= ~ProcessResult.ProcessingStatus.Pending;

                if ((result.Status & ProcessResult.ProcessingStatus.Success) > 0)
                {
                    result.Status |= ProcessResult.ProcessingStatus.DeleteMessage; //message is safe to remove...
                    return(result);
                }
                else
                {
                    result.Status |= ProcessResult.ProcessingStatus.RetryRequested;

                    return(result);
                }
            } //using TrackingContext
        }
        protected override async Task OnRecordTransactionStartAsync(Common.Tracking.TrackingContext context)
        {
            Ef.Entities.Transaction tx = await _dbContext.Transactions.FirstOrDefaultAsync(t => t.TransactionId == context.Request.TransactionId);

            if (null == tx)
            {
                string key = string.Empty, uri = string.Empty;
                FormattedProcessRequest.SecretEvent     evnt = FormattedProcessRequest.SecretEvent.Unknown;
                FormattedProcessRequest.RequestedAction action = FormattedProcessRequest.RequestedAction.Unknown;
                TransactionPurpose purpose = TransactionPurpose.Unknown;

                FormattedProcessRequest fpr = context.Request as FormattedProcessRequest;
                if (null == fpr)
                {
                    fpr = (context.Request as RawProcessRequest)?.FormatRequest();
                }

                if (null != fpr)
                {
                    uri     = fpr.ObjectUri;
                    evnt    = fpr.Event;
                    action  = fpr.Action;
                    purpose = TransactionPurpose.ExecuteRotationProcess;
                }

                if (context.Request is RawProcessRequest)
                {
                    action  = FormattedProcessRequest.RequestedAction.Unknown;
                    purpose = TransactionPurpose.ProcessKVEvent;
                }

                if (!string.IsNullOrWhiteSpace(uri))
                {
                    var s = SecretManagement.Contracts.Data.SecretBase.FromKeyvaultUri(uri);
                    if (null != s)
                    {
                        key = s.Key;
                    }
                }

                tx = new Ef.Entities.Transaction()
                {
                    TransactionId = context.Request.TransactionId,
                    SecretKey     = key,
                    SecretUri     = uri,
                    Action        = action,
                    Event         = evnt,
                    Purpose       = purpose
                };

                if (context.Request.ParentTransactionId != Guid.Empty)
                {
                    tx.ParentTransaction = await _dbContext.Transactions.FirstOrDefaultAsync(t => t.TransactionId == context.Request.ParentTransactionId);
                }

                _dbContext.Transactions.Add(tx);
            }

            Ef.Entities.Attempt attempt = new Ef.Entities.Attempt()
            {
                StartingStatus = context.Result.Status,
                StartTimeUtc   = context.StartTime,
                Transaction    = tx
            };

            List <Ef.Entities.Attempt> attempts = tx.Attempts?.ToList();

            if (null == attempts)
            {
                attempts = new List <Ef.Entities.Attempt>();
            }
            attempts.Add(attempt);
            tx.Attempts = attempts;

            _dbContext.Attempts.Add(attempt);

            await _dbContext.SaveChangesAsync();

            context.TrackingState.Add(this, attempt.AttemptId);
        }