From f6fc3be8ed4d558891f22a71d5ab182ba3c7d780 Mon Sep 17 00:00:00 2001 From: Jonathan Jenne Date: Thu, 15 Apr 2021 15:57:09 +0200 Subject: [PATCH] ZUGFeRDService: Integrate MSSQL Transactions, Rollback for Application Exceptions #MR-2 --- Modules.Jobs/EDMI/ZUGFeRD/EmailFunctions.vb | 9 +- .../EDMI/ZUGFeRD/ImportZUGFeRDFiles.vb | 112 +++++++++++------- 2 files changed, 74 insertions(+), 47 deletions(-) diff --git a/Modules.Jobs/EDMI/ZUGFeRD/EmailFunctions.vb b/Modules.Jobs/EDMI/ZUGFeRD/EmailFunctions.vb index af586642..57436c0d 100644 --- a/Modules.Jobs/EDMI/ZUGFeRD/EmailFunctions.vb +++ b/Modules.Jobs/EDMI/ZUGFeRD/EmailFunctions.vb @@ -2,6 +2,7 @@ Imports DigitalData.Modules.Database Imports System.Data Imports System.IO +Imports System.Data.SqlClient Public Class EmailFunctions Private ReadOnly _logConfig As LogConfig @@ -62,7 +63,7 @@ Public Class EmailFunctions _logger.Error(ex) End Try End Sub - Public Sub AddToEmailQueueMSSQL(MessageId As String, BodyText As String, pEmailData As EmailData, SourceProcedure As String, pEmailAccountId As Integer) + Public Sub AddToEmailQueueMSSQL(MessageId As String, BodyText As String, pEmailData As EmailData, SourceProcedure As String, pEmailAccountId As Integer, Transaction As SqlTransaction) If pEmailData Is Nothing Then _logger.Warn("EmailData is empty. Email will not be sent!") Exit Sub @@ -97,7 +98,7 @@ Public Class EmailFunctions _logger.Debug("Subject: {0}", oSubject) _logger.Debug("Body {0}", oFinalBodyText) Dim osql = $"Select MAX(GUID) FROM TBEMLP_HISTORY WHERE EMAIL_MSGID = '{MessageId}'" - Dim oHistoryID = _mssql.GetScalarValue(osql) + Dim oHistoryID = _mssql.GetScalarValue(osql, Transaction) 'osql = $"select * from TBEMLP_EMAIL_OUT where REFERENCE_ID = {oHistoryID} and EMAIL_ADRESS = '{oEmailTo}' and EMAIL_SUBJ = '{oSubject}'" @@ -128,7 +129,7 @@ Public Class EmailFunctions ,'{SourceProcedure}' ,'{oCreatedWho}' ,'{oAttachment}')" - _mssql.ExecuteNonQuery(oInsert) + _mssql.ExecuteNonQuery(oInsert, Transaction) Else 'If oDTResult.Rows.Count = 0 Then ' _logger.Debug("Email has already been sent!!") @@ -173,7 +174,7 @@ Public Class EmailFunctions Dim oAttachmentFile = MessageId & ".eml" Dim oAttachmentPath = Path.Combine(oAttachmentDirectory, oAttachmentFile) - If IO.File.Exists(oAttachmentPath) Then + If File.Exists(oAttachmentPath) Then Return oAttachmentPath Else _logger.Warn("Email File {0} does not exist. Empty String will be returned.", oAttachmentPath) diff --git a/Modules.Jobs/EDMI/ZUGFeRD/ImportZUGFeRDFiles.vb b/Modules.Jobs/EDMI/ZUGFeRD/ImportZUGFeRDFiles.vb index 30105d97..bb61417b 100644 --- a/Modules.Jobs/EDMI/ZUGFeRD/ImportZUGFeRDFiles.vb +++ b/Modules.Jobs/EDMI/ZUGFeRD/ImportZUGFeRDFiles.vb @@ -2,18 +2,14 @@ Imports System.Data Imports System.IO Imports System.Linq -Imports System.Reflection Imports System.Security.Cryptography -Imports System.Text.RegularExpressions -Imports System.Xml -Imports DigitalData.Modules.Filesystem Imports DigitalData.Modules.Database Imports DigitalData.Modules.Interfaces Imports DigitalData.Modules.Interfaces.Exceptions Imports DigitalData.Modules.Jobs.Exceptions Imports DigitalData.Modules.Logging Imports FirebirdSql.Data.FirebirdClient -Imports GdPicture14 +Imports System.Data.SqlClient Public Class ImportZUGFeRDFiles Implements IJob @@ -111,11 +107,11 @@ Public Class ImportZUGFeRDFiles Return oEmailData End Function - Private Sub AddRejectedState(oMessageID As String, oTitle As String, oTitle1 As String, oComment As String) + Private Sub AddRejectedState(oMessageID As String, oTitle As String, oTitle1 As String, oComment As String, Transaction As SqlTransaction) Try 'PRCUST_ADD_HISTORY_STATE: @MessageID VARCHAR(250), @TITLE1 VARCHAR(250), @TITLE2 VARCHAR(250) Dim oSQL = $"EXEC PRCUST_ADD_HISTORY_STATE '{oMessageID}','{oTitle}','{oTitle1}','{oComment.Replace("'", "''")}'" - _mssql.ExecuteNonQuery(oSQL) + _mssql.ExecuteNonQuery(oSQL, Transaction) Catch ex As Exception _logger.Error(ex) End Try @@ -159,12 +155,18 @@ Public Class ImportZUGFeRDFiles For Each oFileGroup In oGrouped ' Start a new transaction for each file group. ' This way we can rollback database changes for the whole filegroup in case something goes wrong. - Dim oConnection As FbConnection = _firebird.GetConnection() - Dim oTransaction As FbTransaction = oConnection.BeginTransaction() + Dim oFBConnection As FbConnection = _firebird.GetConnection() + Dim oFBTransaction As FbTransaction = oFBConnection.BeginTransaction() + + Dim oSQLConnection As SqlConnection = _mssql.GetConnection() + Dim oSQLTransaction As SqlTransaction = oSQLConnection.BeginTransaction() + ' Count the amount of ZUGFeRD files Dim oZUGFeRDCount As Integer = 0 + ' Set the default Move Directory Dim oMoveDirectory As String = oArgs.ErrorDirectory + ' Flag to save if the whole process was a success. ' Will be set only at the end of the function if no error occurred. Dim oIsSuccess As Boolean = False @@ -275,10 +277,9 @@ Public Class ImportZUGFeRDFiles Dim oDelSQL = $"DELETE FROM TBEDMI_ITEM_VALUE where REFERENCE_GUID = '{oMessageId}'" Dim oStep As String - oStep = "Firebird TBEDMI_ITEM_VALUE Delete messageID Items" Try - _firebird.ExecuteNonQueryWithConnection(oDelSQL, oConnection, Firebird.TransactionMode.ExternalTransaction, oTransaction) + _firebird.ExecuteNonQueryWithConnection(oDelSQL, oFBConnection) Catch ex As Exception _logger.Error(ex) _logger.Warn("Step [{0}] with SQL [{1}] was not successful.", oStep, oDelSQL) @@ -287,7 +288,7 @@ Public Class ImportZUGFeRDFiles If oArgs.InsertIntoSQLServer = True Then oStep = "MSSQL TBEDMI_ITEM_VALUE Delete messageID Items" Try - _mssql.ExecuteNonQuery(oDelSQL) + _mssql.ExecuteNonQueryWithConnectionObject(oDelSQL, oSQLConnection) Catch ex As Exception _logger.Warn("Step [{0}] with SQL [{1}] was not successful.", oStep, oDelSQL) End Try @@ -307,13 +308,13 @@ Public Class ImportZUGFeRDFiles _logger.Debug("Mapping Property [{0}] with value [{1}], Will be inserted into table [{2}]", oProperty.TableColumn, oProperty.Value.Replace("'", "''"), oProperty.TableName) ' Insert into SQL Server If oArgs.InsertIntoSQLServer = True Then - Dim oResult = _mssql.ExecuteNonQuery(oCommand) + Dim oResult = _mssql.ExecuteNonQueryWithConnectionObject(oCommand, oSQLConnection, MSSQLServer.TransactionMode.ExternalTransaction, oSQLTransaction) If oResult = False Then _logger.Warn($"SQL Command [{oCommand}] was not successful. Check the log.") End If End If ' Insert into Firebird - _firebird.ExecuteNonQueryWithConnection(oCommand, oConnection, Firebird.TransactionMode.ExternalTransaction, oTransaction) + _firebird.ExecuteNonQueryWithConnection(oCommand, oFBConnection, Firebird.TransactionMode.ExternalTransaction, oFBTransaction) Next Next @@ -326,9 +327,11 @@ Public Class ImportZUGFeRDFiles 'Log the History If oMD5CheckSum <> String.Empty Then Dim oInsertCommand = $"INSERT INTO TBEDM_ZUGFERD_HISTORY_IN (MESSAGE_ID, MD5HASH) VALUES ('{oMessageId}', '{oMD5CheckSum}')" - _firebird.ExecuteNonQueryWithConnection(oInsertCommand, oConnection, Firebird.TransactionMode.ExternalTransaction, oTransaction) - 'commit the transaction - oTransaction.Commit() + _firebird.ExecuteNonQueryWithConnection(oInsertCommand, oFBConnection, Firebird.TransactionMode.ExternalTransaction, oFBTransaction) + + ' History ID is only need in case of an error + oFBTransaction.Commit() + Try Dim oSQL = $"SELECT MAX(GUID) FROM TBEDM_ZUGFERD_HISTORY_IN WHERE MESSAGE_ID = '{oMessageId}'" HISTORY_ID = _firebird.GetScalarValue(oSQL) @@ -342,55 +345,55 @@ Public Class ImportZUGFeRDFiles Catch ex As MD5HashException _logger.Error(ex) - oTransaction.Rollback() + 'oFBTransaction.Rollback() Dim oSQL = $"UPDATE TBEDM_ZUGFERD_HISTORY_IN SET COMMENT = 'REJECTED - Already processed (MD5Hash)' WHERE GUID = '{HISTORY_ID}'" _firebird.ExecuteNonQuery(oSQL) Dim oBody = EmailStrings.EMAIL_MD5_ERROR Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId) - _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "MD5HashException", _EmailOutAccountId) - AddRejectedState(oMessageId, "MD5HashException", "Die gesendete Rechnung wurde bereits verarbeitet!", "") + _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "MD5HashException", _EmailOutAccountId, oSQLTransaction) + AddRejectedState(oMessageId, "MD5HashException", "Die gesendete Rechnung wurde bereits verarbeitet!", "", oSQLTransaction) Catch ex As InvalidFerdException _logger.Error(ex) - oTransaction.Rollback() + 'oFBTransaction.Rollback() Dim oSQL = $"UPDATE TBEDM_ZUGFERD_HISTORY_IN SET COMMENT = 'REJECTED - ZUGFeRD yes but incorrect format' WHERE GUID = '{HISTORY_ID}'" _firebird.ExecuteNonQuery(oSQL) Dim oBody = EmailStrings.EMAIL_INVALID_DOCUMENT Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId) - _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "InvalidFerdException", _EmailOutAccountId) - AddRejectedState(oMessageId, "InvalidFerdException", "Inkorrekte Formate", "") + _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "InvalidFerdException", _EmailOutAccountId, oSQLTransaction) + AddRejectedState(oMessageId, "InvalidFerdException", "Inkorrekte Formate", "", oSQLTransaction) Catch ex As TooMuchFerdsException _logger.Error(ex) - oTransaction.Rollback() + 'oFBTransaction.Rollback() Dim oSQL = $"UPDATE TBEDM_ZUGFERD_HISTORY_IN SET COMMENT = 'REJECTED - More than one ZUGFeRD-document in email' WHERE GUID = '{HISTORY_ID}'" _firebird.ExecuteNonQuery(oSQL) Dim oBody = EmailStrings.EMAIL_TOO_MUCH_FERDS Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId) - _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "TooMuchFerdsException", _EmailOutAccountId) - AddRejectedState(oMessageId, "TooMuchFerdsException", "Email enthielt mehr als ein ZUGFeRD-Dokument", "") + _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "TooMuchFerdsException", _EmailOutAccountId, oSQLTransaction) + AddRejectedState(oMessageId, "TooMuchFerdsException", "Email enthielt mehr als ein ZUGFeRD-Dokument", "", oSQLTransaction) Catch ex As NoFerdsException _logger.Error(ex) - oTransaction.Rollback() + 'oFBTransaction.Rollback() Dim oSQL = $"UPDATE TBEDM_ZUGFERD_HISTORY_IN SET COMMENT = 'REJECTED - no ZUGFeRD-Document in email' WHERE GUID = '{HISTORY_ID}'" _firebird.ExecuteNonQuery(oSQL) Dim oBody = EmailStrings.EMAIL_NO_FERDS Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId) - _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "NoFerdsException", _EmailOutAccountId) - AddRejectedState(oMessageId, "NoFerdsException", " Email enthielt keine ZUGFeRD-Dokumente", "") + _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "NoFerdsException", _EmailOutAccountId, oSQLTransaction) + AddRejectedState(oMessageId, "NoFerdsException", " Email enthielt keine ZUGFeRD-Dokumente", "", oSQLTransaction) Catch ex As MissingValueException _logger.Error(ex) - oTransaction.Rollback() + 'oFBTransaction.Rollback() Dim oMessage As String = "" For Each prop In oMissingProperties @@ -401,33 +404,56 @@ Public Class ImportZUGFeRDFiles Dim oBody = CreateBodyForMissingProperties(ex.File.Name, oMissingProperties) Dim oEmailData = MoveAndRenameEmailToRejected(oArgs, oMessageId) - _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "MissingValueException", _EmailOutAccountId) - AddRejectedState(oMessageId, "MissingValueException", "Es fehlten ZugferdSpezifikationen", oMessage) + _email.AddToEmailQueueMSSQL(oMessageId, oBody, oEmailData, "MissingValueException", _EmailOutAccountId, oSQLTransaction) + AddRejectedState(oMessageId, "MissingValueException", "Es fehlten ZugferdSpezifikationen", oMessage, oSQLTransaction) Catch ex As OutOfMemoryException _logger.Warn("OutOfMemory Error occurred: {0}", ex.Message) _logger.Error(ex) - oTransaction.Rollback() - oMoveDirectory = DIRECTORY_DONT_MOVE + ' Rollback Firebird + oFBTransaction.Rollback() - 'Dim oSQL = $"UPDATE TBEDM_ZUGFERD_HISTORY_IN SET COMMENT = 'REJECTED - Out of memory' WHERE GUID = '{HISTORY_ID}'" - '_firebird.ExecuteNonQuery(oSQL) - 'AddRejectedState(oMessageId, "OutOfMemoryException", "", ex.Message) + ' Rollback MSSQL + oSQLTransaction.Rollback() + + oMoveDirectory = DIRECTORY_DONT_MOVE Catch ex As Exception _logger.Warn("Unknown Error occurred: {0}", ex.Message) _logger.Error(ex) - oTransaction.Rollback() + + ' Rollback Firebird + oFBTransaction.Rollback() + + ' Rollback MSSQL + oSQLTransaction.Rollback() oMoveDirectory = DIRECTORY_DONT_MOVE - 'Dim oSQL = $"UPDATE TBEDM_ZUGFERD_HISTORY_IN SET COMMENT = 'REJECTED - Unknown error occured' WHERE GUID = '{HISTORY_ID}'" - '_firebird.ExecuteNonQuery(oSQL) - 'oMoveDirectory = oArgs.ErrorDirectory - 'AddRejectedState(oMessageId, "UnexpectedException", "", ex.Message) Finally - oConnection.Close() + Try + ' If everything went OK, finally commit all changes to the Database + ' ================================================================== + If oIsSuccess Then + ' Commit SQL Transaction + oSQLTransaction.Commit() + + ' Commit Firebird Transaction + oFBTransaction.Commit() + End If + Catch ex As Exception + _logger.Error(ex) + _logger.Warn("Database Transactions were not committed successfully.") + End Try + + Try + oFBConnection.Close() + oSQLConnection.Close() + Catch ex As Exception + _logger.Error(ex) + _logger.Warn("Database Connections were not closed successfully.") + End Try Try ' If an application error occurred, dont move files so they will be processed again later