/// <summary>
        /// Iteratively executes the business logic until the application is stopped.
        /// </summary>
        /// <param name="stoppingToken">The <see cref="CancellationToken"/> that can be used to stop execution of the application.</param>
        /// <returns></returns>
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            MethodBase methodBase = MethodBase.GetCurrentMethod();

            logger.Trace($"Begin {methodBase.ReflectedType.Name}.{methodBase.Name}");

            // This is the loop containing all of the business logic that is executed iteratively throughout the lifeime of the application.
            while (!stoppingToken.IsCancellationRequested)
            {
                // Abort if waiting for restoration of connectivity to the MyGeotab server or to the database.
                if (StateMachine.CurrentState == State.Waiting)
                {
                    continue;
                }

                if (initializationCompleted == false)
                {
                    PerformInitializationTasks();
                    continue;
                }

                try
                {
                    logger.Trace($"Started iteration of {methodBase.ReflectedType.Name}.{methodBase.Name}");

                    // Only proceed if the DVIRLogManipulatorIntervalSeconds has elapsed since the last iteration was initiated.
                    if (Globals.TimeIntervalHasElapsed(lastIterationStartTimeUtc, Globals.DateTimeIntervalType.Seconds, Globals.ConfigurationManager.DVIRLogManipulatorIntervalSeconds))
                    {
                        lastIterationStartTimeUtc = DateTime.UtcNow;

                        using (var cancellationTokenSource = new CancellationTokenSource())
                        {
                            try
                            {
                                // Retrieve a batch of DbDVIRDefectUpdate entities from the database.
                                var dbDVIRDefectUpdates = await DbDVIRDefectUpdateService.GetAllAsync(connectionInfo, commandTimeout, DVIRDefectUpdateBatchSize);

                                int dbDVIRDefectUpdateCount          = dbDVIRDefectUpdates.Count();
                                int processedDbDVIRDefectUpdateCount = 0;
                                int failedDbDVIRDefectUpdateCount    = 0;

                                if (dbDVIRDefectUpdates.Any())
                                {
                                    logger.Info($"Retrieved {dbDVIRDefectUpdateCount} records from DVIRDefectUpdates table. Processing...");
                                }
                                else
                                {
                                    logger.Debug($"No records retrieved from DVIRDefectUpdates table.");
                                    continue;
                                }

                                // Process each of the retrieved DbDVIRDefectUpdate entities.
                                foreach (var dbDVIRDefectUpdate in dbDVIRDefectUpdates)
                                {
                                    try
                                    {
                                        // Get the subject DVIRLog from the MyGeotab database.
                                        var dvirLogResultList = await MyGeotabApiUtility.GetAsync <DVIRLog>(Globals.MyGeotabAPI, new DVIRLogSearch { Id = Id.Create(dbDVIRDefectUpdate.DVIRLogId) });

                                        if (dvirLogResultList.Any())
                                        {
                                            var dvirLogToUpdate = dvirLogResultList.First();
                                            // Get the subject DVIRDefect to be updated.
                                            var dvirDefectToUpdate = dvirLogToUpdate.DVIRDefects.Where(dvirDefect => dvirDefect.Id == Id.Create(dbDVIRDefectUpdate.DVIRDefectId)).FirstOrDefault();
                                            if (dvirDefectToUpdate == null)
                                            {
                                                throw new ArgumentException($"No DVIRDefect with the sepecified Id is associated with the subject DVIRLog in the MyGeotab database.");
                                            }
                                            else
                                            {
                                                bool defectRemarkAddedToDVIRDefectToUpdate   = AddDefectRemark(dvirDefectToUpdate, dbDVIRDefectUpdate);
                                                bool repairStatusUpdatedOnDVIRDefectToUpdate = UpdateDVIRDefectRepairStatus(dvirDefectToUpdate, dbDVIRDefectUpdate);

                                                if (defectRemarkAddedToDVIRDefectToUpdate == false && repairStatusUpdatedOnDVIRDefectToUpdate == false)
                                                {
                                                    throw new ArgumentException($"No action to take because insufficent data was provided to either add a DefectRemark or update the RepairStatus of the subject DVIRDefect.");
                                                }

                                                var deleteCurrentDbDVIRDefectUpdateRecord = false;
                                                try
                                                {
                                                    // Update the DVIRLog in the MyGeotab database.
                                                    var result = await MyGeotabApiUtility.SetAsync <DVIRLog>(Globals.MyGeotabAPI, dvirLogToUpdate);

                                                    deleteCurrentDbDVIRDefectUpdateRecord = true;
                                                    processedDbDVIRDefectUpdateCount     += 1;
                                                }
                                                catch (MyGeotabConnectionException)
                                                {
                                                    // Pass the MyGeotabConnectionException along so that the connectivity loss can be addressed and the subject DbDVIRDefectUpdate can be re-tried once connectivity is restored.
                                                    throw;
                                                }
                                                catch (Exception exception)
                                                {
                                                    // Flag the current DbDVIRDefectUpdateRecord for deletion since this is not a connectivity-related exception and the same exception could occur repeatedly if the adapter is restarted and this record remains in the adapter database.
                                                    deleteCurrentDbDVIRDefectUpdateRecord = true;
                                                    throw new Exception($"MyGeotab Exception encountered on Set<DVIRLog> call.", exception);
                                                }
                                                finally
                                                {
                                                    if (deleteCurrentDbDVIRDefectUpdateRecord == true)
                                                    {
                                                        // Delete the current DbDVIRDefectUpdate from the DVIRDefectUpdates table.
                                                        await DbDVIRDefectUpdateService.DeleteAsync(connectionInfo, dbDVIRDefectUpdate, commandTimeout);
                                                    }
                                                }
                                            }
                                        }
                                        else
                                        {
                                            throw new Exception($"No DVIRLog with the specified Id exists in the MyGeotab database.");
                                        }
                                    }
                                    catch (Exception dvirLogUpdateException)
                                    {
                                        try
                                        {
                                            failedDbDVIRDefectUpdateCount += 1;
                                            var failureMessage = GenerateMessageForDVIRLogUpdateException(dvirLogUpdateException);

                                            // Insert a record into the FailedDVIRDefectUpdates table.
                                            DbFailedDVIRDefectUpdate dbFailedDVIRDefectUpdate = ObjectMapper.GetDbFailedDVIRDefectUpdate(dbDVIRDefectUpdate, failureMessage);
                                            dbFailedDVIRDefectUpdate.DatabaseWriteOperationType = Common.DatabaseWriteOperationType.Insert;
                                            dbFailedDVIRDefectUpdate.RecordCreationTimeUtc      = DateTime.UtcNow;
                                            await DbFailedDVIRDefectUpdateService.InsertAsync(connectionInfo, dbFailedDVIRDefectUpdate, commandTimeout);

                                            // Delete the current DbDVIRDefectUpdate from the DVIRDefectUpdates table.
                                            await DbDVIRDefectUpdateService.DeleteAsync(connectionInfo, dbDVIRDefectUpdate, commandTimeout);
                                        }
                                        catch (Exception ex)
                                        {
                                            throw new Exception($"Exception encountered while writing to {ConfigurationManager.DbFailedDVIRDefectUpdatesTableName} table or deleting from {ConfigurationManager.DbDVIRDefectUpdatesTableName} table.", ex);
                                        }
                                    }
                                }

                                logger.Info($"Of the {dbDVIRDefectUpdateCount} records from DVIRDefectUpdates table, {processedDbDVIRDefectUpdateCount} were successfully processed and {failedDbDVIRDefectUpdateCount} failed. Copies of any failed records have been inserted into the FailedDVIRDefectUpdates table for reference.");
                            }
                            catch (TaskCanceledException taskCanceledException)
                            {
                                string errorMessage = $"Task was cancelled. TaskCanceledException: \nMESSAGE [{taskCanceledException.Message}]; \nSOURCE [{taskCanceledException.Source}]; \nSTACK TRACE [{taskCanceledException.StackTrace}]";
                                logger.Warn(errorMessage);
                            }
                        }
                    }
                    else
                    {
                        logger.Debug($"Propagation of DVIRLog changes from tables in the adapter database to the associated MyGeotab database has not been initiated on this iteration; {Globals.ConfigurationManager.DVIRLogManipulatorIntervalSeconds} seconds have not passed since the process was last initiated.");
                    }

                    logger.Trace($"Completed iteration of {methodBase.ReflectedType.Name}.{methodBase.Name}");
                }
                catch (OperationCanceledException)
                {
                    string errorMessage = $"{nameof(DVIRLogManipulator)} process cancelled.";
                    logger.Warn(errorMessage);
                    throw new Exception(errorMessage);
                }
                catch (DatabaseConnectionException databaseConnectionException)
                {
                    HandleException(databaseConnectionException, NLog.LogLevel.Error, $"{nameof(DVIRLogManipulator)} process caught an exception");
                }
                catch (MyGeotabConnectionException myGeotabConnectionException)
                {
                    HandleException(myGeotabConnectionException, NLog.LogLevel.Error, $"{nameof(DVIRLogManipulator)} process caught an exception");
                }
                catch (Exception ex)
                {
                    // If an exception hasn't been handled to this point, log it and kill the process.
                    HandleException(ex, NLog.LogLevel.Fatal, $"******** {nameof(DVIRLogManipulator)} process caught an unhandled exception and will self-terminate now.");
                    System.Diagnostics.Process.GetCurrentProcess().Kill();
                }
            }

            logger.Trace($"End {methodBase.ReflectedType.Name}.{methodBase.Name}");
        }
Example #2
0
 /// <summary>
 /// Inserts a <see cref="DbFailedDVIRDefectUpdate"/> entity into the database.
 /// </summary>
 /// <param name="connectionInfo">The database connection information.</param>
 /// <param name="dbFailedDVIRDefectUpdate">A <see cref="DbFailedDVIRDefectUpdate"/> entity to be inserted.</param>
 /// <param name="commandTimeout">The number of seconds before command execution timeout.</param>
 /// <returns></returns>
 public static async Task <long> InsertAsync(ConnectionInfo connectionInfo, DbFailedDVIRDefectUpdate dbFailedDVIRDefectUpdate, int commandTimeout)
 {
     return(await new DbFailedDVIRDefectUpdateRepository().InsertAsync(connectionInfo, dbFailedDVIRDefectUpdate, commandTimeout));
 }