public override void ExecuteJob() { using (var dbConnection = new SqlConnection(Configuration.SqlConnectionString)) { using (var dbContext = new DipsDbContext(dbConnection)) { Log.Information("Scanning database for completed codeline validation batches"); //Get all the potential batches that have been completed var completedBatches = dbContext.Queues .Where(q => !q.ResponseCompleted && q.S_LOCATION == DipsLocationType.CodelineValidationDone.Value && q.S_LOCK.Trim() == "0") .ToList(); Serilog.Log.Information("Found {0} completed codeline validation batches", completedBatches.Count()); foreach (var completedBatch in completedBatches) { string routingKey; Serilog.Log.Debug("Creating response for batch {@batch}", completedBatch.S_BATCH); //only commit the transaction if // a) we were the first application to mark this batch row as ValidateCodelineCompleted (DipsQueue uses optimistic concurrency) // b) we were able to place a response message on the bus using (var tx = dbContext.BeginTransaction()) { try { //mark the batch as completed so it gets 'locked' completedBatch.ResponseCompleted = true; routingKey = completedBatch.RoutingKey; dbContext.SaveChanges(); var batchNumber = completedBatch.S_BATCH; //get the vouchers, generate and send the response var vouchers = dbContext.NabChqPods .Where(v => v.S_BATCH == batchNumber) .AsEnumerable() .Select(v => new { S_STATUS1 = int.Parse(string.IsNullOrWhiteSpace(v.S_STATUS1) ? "0" : v.S_STATUS1), voucher = v }).ToList(); //use bitmasks to map the values from S_STATUS1 field var batchResponse = new ValidateBatchCodelineResponse { voucherBatch = new VoucherBatch { scannedBatchNumber = completedBatch.S_BATCH, workType = ResponseHelper.ParseWorkType( ResponseHelper.TrimString(completedBatch.S_JOB_ID)), batchAccountNumber = vouchers.First().voucher.batchAccountNumber, captureBsb = vouchers.First().voucher.captureBSB, processingState = ResponseHelper.ParseState( ResponseHelper.TrimString(vouchers.First().voucher.processing_state)), collectingBank = vouchers.First().voucher.collecting_bank, unitID = vouchers.First().voucher.unit_id, batchType = ResponseHelper.TrimString(vouchers.First().voucher.batch_type), subBatchType = ResponseHelper.TrimString(vouchers.First().voucher.sub_batch_type), }, voucher = vouchers.Select(v => new ValidateCodelineResponse { extraAuxDomStatus = DipsStatus1Bitmask.GetExtraAuxDomStatusMasked(v.S_STATUS1), auxDomStatus = DipsStatus1Bitmask.GetAuxDomStatusMasked(v.S_STATUS1), accountNumberStatus = DipsStatus1Bitmask.GetAccountNumberStatusMasked(v.S_STATUS1), amountStatus = DipsStatus1Bitmask.GetAmountStatusMasked(v.S_STATUS1), bsbNumberStatus = DipsStatus1Bitmask.GetBsbNumberStatusMasked(v.S_STATUS1), transactionCodeStatus = DipsStatus1Bitmask.GetTransactionCodeStatusMasked(v.S_STATUS1), documentReferenceNumber = ResponseHelper.ResolveDocumentReferenceNumber( ResponseHelper.TrimString(v.voucher.doc_ref_num)), targetEndPoint = ResponseHelper.TrimString(v.voucher.ie_endPoint), documentType = ResponseHelper.ParseDocumentType(v.voucher.doc_type), processingDate = DateTime.ParseExact(string.Format("{0}", v.voucher.proc_date), "yyyyMMdd", CultureInfo.InvariantCulture), }).ToArray() }; if (Configuration.DeleteDatabaseRows) { ResponseHelper.CleanupBatchData(batchNumber, dbContext); } if (string.IsNullOrEmpty(routingKey)) { routingKey = string.Empty; } //Task.WaitAll(responseExchange.PublishAsync(batchResponse, completedBatch.CorrelationId, routingKey.Trim())); tx.Commit(); Serilog.Log.Debug( "Codeline validation batch '{@batch}' has been completed and a response has been placed on the queue", completedBatch.S_BATCH); Serilog.Log.Information("Batch '{@batch}' response sent: {@response}", completedBatch.S_BATCH, batchResponse); } catch (OptimisticConcurrencyException) { //this is to handle the race condition where more than instance of this service is running at the same time and tries to update the row. //basically ignore the message by loggin a warning and roll back. //if this row was not included by mistake (e.g. it should be included), it will just come in in the next batch run. Serilog.Log.Warning( "Could not create a codeline validation response for batch '{@batch}' because the DIPS database row was updated by another connection", completedBatch.S_BATCH); tx.Rollback(); } catch (Exception ex) { Serilog.Log.Error( ex, "Could not complete and create a codeline validation response for batch '{@batch}'", completedBatch.S_BATCH); tx.Rollback(); } } } } } }
public override void ExecuteJob() { Log.Information("Scanning database for completed generate corresponding voucher"); using (var dbConnection = new SqlConnection(Configuration.SqlConnectionString)) { using (var dbContext = new DipsDbContext(dbConnection)) { //Get all the potential batches that have been completed var completedBatches = dbContext.Queues .Where(q => !q.ResponseCompleted && q.S_LOCATION == DipsLocationType.GenerateCorrespondingVoucherDone.Value && q.S_LOCK.Trim() == "0") .ToList(); Log.Information("Found {0} completed generate corresponding voucher", completedBatches.Count()); foreach (var completedBatch in completedBatches) { Log.Debug("Creating response for batch {@batch}", completedBatch.S_BATCH); //only commit the transaction if // a) we were the first application to mark this batch row as CorrectCodelineCompleted (DipsQueue uses optimistic concurrency) // b) we were able to place a response message on the bus using (var tx = dbContext.BeginTransaction()) { try { //mark the line as completed completedBatch.ResponseCompleted = true; var routingKey = completedBatch.RoutingKey; dbContext.SaveChanges(); var batchNumber = completedBatch.S_BATCH; //get the vouchers, generate and send the response var originalVouchers = dbContext.NabChqPods .Where( v => v.S_BATCH == batchNumber && v.S_DEL_IND != " 255" && v.isGeneratedVoucher != "1" && (v.export_exclude_flag.Trim() != "1")) .ToList(); var generatedVouchers = dbContext.NabChqPods .Where( v => v.S_BATCH == batchNumber && v.S_DEL_IND != " 255" && v.isGeneratedVoucher == "1" && (v.export_exclude_flag.Trim() != "1")) .ToList(); var batchResponse = new GenerateCorrespondingVoucherResponse { updateVoucher = originalVouchers.Select(v => new VoucherInformation() { voucherBatch = new VoucherBatch { batchAccountNumber = v.batchAccountNumber, batchType = ResponseHelper.TrimString(v.batch_type), captureBsb = v.captureBSB, collectingBank = v.collecting_bank, processingState = ResponseHelper.ParseState(ResponseHelper.TrimString(v.processing_state)), scannedBatchNumber = completedBatch.S_BATCH, unitID = originalVouchers.First().unit_id, workType = ResponseHelper.ParseWorkType( ResponseHelper.TrimString(completedBatch.S_JOB_ID)), subBatchType = ResponseHelper.TrimString(v.sub_batch_type), }, voucherProcess = new VoucherProcess { adjustedFlag = ResponseHelper.ParseStringAsBool(v.adjustedFlag), highValueFlag = ResponseHelper.ParseStringAsBool(v.highValueFlag), isGeneratedVoucher = ResponseHelper.ParseStringAsBool(v.isGeneratedVoucher), manualRepair = ResponseHelper.ParseStringAsInt(v.man_rep_ind), preAdjustmentAmount = ResponseHelper.ParseAmountField(v.orig_amount), presentationMode = ResponseHelper.TrimString(v.presentationMode), rawMICR = ResponseHelper.TrimString(v.raw_micr), rawOCR = ResponseHelper.TrimString(v.raw_ocr), repostFromDRN = ResponseHelper.TrimString(v.repostFromDRN), repostFromProcessingDate = ResponseHelper.ParseDateField(v.repostFromProcessingDate), surplusItemFlag = ResponseHelper.ParseStringAsBool(v.surplusItemFlag), suspectFraud = ResponseHelper.ParseStringAsBool(v.micr_suspect_fraud_flag), thirdPartyCheckFailed = ResponseHelper.ParseTpcResult(v.tpcResult), thirdPartyMixedDepositReturnFlag = ResponseHelper.ParseStringAsBool(v.tpcMixedDepRet), thirdPartyPoolFlag = ResponseHelper.ParseStringAsBool(v.fxa_tpc_suspense_pool_flag), transactionLinkNumber = ResponseHelper.TrimString(v.transactionLinkNumber), unencodedECDReturnFlag = ResponseHelper.ParseStringAsBool(v.fxa_unencoded_ECD_return), unprocessable = ResponseHelper.ParseStringAsBool(v.unproc_flag), voucherDelayedIndicator = ResponseHelper.TrimString(v.voucherIndicatorField), operatorId = ResponseHelper.TrimString(v.op_id), adjustedBy = ResponseHelper.TrimString(v.op_id), adjustmentLetterRequired = ResponseHelper.ParseStringAsBool(v.adjustmentLetterRequired), forValueType = ForValueTypeEnum.Inward_Non_For_Value, postTransmissionQaAmountFlag = ResponseHelper.ParseStringAsBool(v.fxaPtQAAmtFlag), postTransmissionQaCodelineFlag = ResponseHelper.ParseStringAsBool(v.fxaPtQACodelineFlag), adjustmentReasonCode = ResponseHelper.ParseStringAsInt(v.adjustmentReasonCode), adjustmentDescription = ResponseHelper.TrimString(v.adjustmentDescription) }, voucher = new Voucher { accountNumber = ResponseHelper.TrimString(v.acc_num), amount = ResponseHelper.ParseAmountField(v.amount), auxDom = ResponseHelper.TrimString(v.ser_num), bsbNumber = v.bsb_num, documentReferenceNumber = ResponseHelper.ResolveDocumentReferenceNumber( ResponseHelper.TrimString(v.doc_ref_num)), documentType = ResponseHelper.ParseDocumentType(v.doc_type), extraAuxDom = ResponseHelper.TrimString(v.ead), processingDate = DateTime.ParseExact(string.Format("{0}", v.proc_date), "yyyyMMdd", CultureInfo.InvariantCulture), transactionCode = ResponseHelper.TrimString(v.trancode), } }).ToArray(), generatedVoucher = generatedVouchers.Select(v => new VoucherInformation() { voucherBatch = new VoucherBatch { batchAccountNumber = v.batchAccountNumber, batchType = ResponseHelper.TrimString(v.batch_type), captureBsb = v.captureBSB, collectingBank = v.collecting_bank, processingState = ResponseHelper.ParseState(ResponseHelper.TrimString(v.processing_state)), scannedBatchNumber = completedBatch.S_BATCH, unitID = originalVouchers.First().unit_id, workType = ResponseHelper.ParseWorkType( ResponseHelper.TrimString(completedBatch.S_JOB_ID)), subBatchType = ResponseHelper.TrimString(v.sub_batch_type), }, voucherProcess = new VoucherProcess { adjustedFlag = ResponseHelper.ParseStringAsBool(v.adjustedFlag), highValueFlag = ResponseHelper.ParseStringAsBool(v.highValueFlag), isGeneratedVoucher = ResponseHelper.ParseStringAsBool(v.isGeneratedVoucher), manualRepair = ResponseHelper.ParseStringAsInt(v.man_rep_ind), preAdjustmentAmount = ResponseHelper.ParseAmountField(v.orig_amount), presentationMode = ResponseHelper.TrimString(v.presentationMode), rawMICR = ResponseHelper.TrimString(v.raw_micr), rawOCR = ResponseHelper.TrimString(v.raw_ocr), repostFromDRN = ResponseHelper.TrimString(v.repostFromDRN), repostFromProcessingDate = ResponseHelper.ParseDateField(v.repostFromProcessingDate), surplusItemFlag = ResponseHelper.ParseStringAsBool(v.surplusItemFlag), suspectFraud = ResponseHelper.ParseStringAsBool(v.micr_suspect_fraud_flag), thirdPartyCheckFailed = ResponseHelper.ParseTpcResult(v.tpcResult), thirdPartyMixedDepositReturnFlag = ResponseHelper.ParseStringAsBool(v.tpcMixedDepRet), thirdPartyPoolFlag = ResponseHelper.ParseStringAsBool(v.fxa_tpc_suspense_pool_flag), transactionLinkNumber = ResponseHelper.TrimString(v.transactionLinkNumber), unencodedECDReturnFlag = ResponseHelper.ParseStringAsBool(v.fxa_unencoded_ECD_return), unprocessable = ResponseHelper.ParseStringAsBool(v.unproc_flag), voucherDelayedIndicator = ResponseHelper.TrimString(v.voucherIndicatorField), operatorId = ResponseHelper.TrimString(v.op_id), adjustedBy = ResponseHelper.TrimString(v.op_id), adjustmentLetterRequired = ResponseHelper.ParseStringAsBool(v.adjustmentLetterRequired), forValueType = ForValueTypeEnum.Inward_Non_For_Value, postTransmissionQaAmountFlag = ResponseHelper.ParseStringAsBool(v.fxaPtQAAmtFlag), postTransmissionQaCodelineFlag = ResponseHelper.ParseStringAsBool(v.fxaPtQACodelineFlag), adjustmentReasonCode = ResponseHelper.ParseStringAsInt(v.adjustmentReasonCode), adjustmentDescription = ResponseHelper.TrimString(v.adjustmentDescription) }, voucher = new Voucher { accountNumber = ResponseHelper.TrimString(v.acc_num), amount = ResponseHelper.ParseAmountField(v.amount), auxDom = ResponseHelper.TrimString(v.ser_num), bsbNumber = v.bsb_num, documentReferenceNumber = ResponseHelper.ResolveDocumentReferenceNumber( ResponseHelper.TrimString(v.doc_ref_num)), documentType = ResponseHelper.ParseDocumentType(v.doc_type), extraAuxDom = ResponseHelper.TrimString(v.ead), processingDate = DateTime.ParseExact(string.Format("{0}", v.proc_date), "yyyyMMdd", CultureInfo.InvariantCulture), transactionCode = ResponseHelper.TrimString(v.trancode), } }).ToArray() }; if (Configuration.DeleteDatabaseRows) { ResponseHelper.CleanupBatchData(batchNumber, dbContext); } if (string.IsNullOrEmpty(routingKey)) { routingKey = string.Empty; } Exchange.SendMessage(CustomJsonSerializer.MessageToBytes(batchResponse), routingKey.Trim(), completedBatch.CorrelationId); tx.Commit(); Log.Debug( "Generate corresponding voucher batch '{@batch}' has been completed and a response has been placed on the queue", completedBatch.S_BATCH); Log.Information("Batch '{@batch}' response sent: {@response}", completedBatch.S_BATCH, batchResponse); } catch (OptimisticConcurrencyException) { //this is to handle the race condition where more than instance of this service is running at the same time and tries to update the row. //basically ignore the message by loggin a warning and rolling back. //if this row was not included by mistake (e.g. it should be included), it will just come in in the next batch run. Log.Warning( "Could not create a generate corresponding voucher response for batch '{@batch}' because the DIPS database row was updated by another connection", completedBatch.S_BATCH); tx.Rollback(); } catch (Exception ex) { Log.Error( ex, "Could not complete and create a generate corresponding voucher response for batch '{@batch}'", completedBatch.S_BATCH); tx.Rollback(); } } } } } Log.Information("Finished processing completed generate corresponding voucher batches"); }
public override void ExecuteJob() { Log.Information("Scanning database for completed get vouchers information requests"); using (var dbConnection = new SqlConnection(Configuration.SqlConnectionString)) { using (var dbContext = new DipsDbContext(dbConnection)) { //Get all the potential requests that have been completed var pendingRequests = dbContext.DipsRequest .Where(q => !q.RequestCompleted.HasValue || !q.RequestCompleted.Value) .ToList(); Log.Information("Found {0} completed get vouchers information requests", pendingRequests.Count()); foreach (var pendingRequest in pendingRequests) { Log.Debug("Creating Request for request {@guidName}", pendingRequest.guid_name); //only commit the transaction if // a) we were the first application to mark this request row as CorrectCodelineCompleted (DipsQueue uses optimistic concurrency) // b) we were able to place a Request message on the bus using (var tx = dbContext.BeginTransaction()) { try { //mark the line as completed pendingRequest.RequestCompleted = true; dbContext.SaveChanges(); var requestNumber = pendingRequest.guid_name; //get the record, generate and send the Request var payload = JsonConvert.DeserializeObject<List<Criteria>>(pendingRequest.payload); var requestRequest = new GetVouchersInformationRequest { jobIdentifier = pendingRequest.guid_name.Trim(), imageRequired = ImageType.JPEG, imageResponseType = ResponseType.MESSAGE, metadataResponseType = ResponseType.MESSAGE, searchCriteria = payload.ToArray() }; if (Configuration.DeleteDatabaseRows) { RequestHelper.CleanupRequestData(requestNumber, dbContext); } Exchange.SendMessage(CustomJsonSerializer.MessageToBytes(requestRequest), "NSBD", pendingRequest.guid_name); tx.Commit(); Log.Debug( "get vouchers information request '{@guidName}' has been completed and a Request has been placed on the queue", pendingRequest.guid_name); Log.Information("Batch '{@guidName}' Request sent: {@requestRequest}", pendingRequest.guid_name, requestRequest); } catch (OptimisticConcurrencyException) { //this is to handle the race condition where more than instance of this service is running at the same time and tries to update the row. //basically ignore the message by loggin a warning and rolling back. //if this row was not included by mistake (e.g. it should be included), it will just come in in the next request run. Log.Warning( "Could not create a get vouchers information Request for request '{@guidName}' because the DIPS database row was updated by another connection", pendingRequest.guid_name); tx.Rollback(); } catch (Exception ex) { Log.Error( ex, "Could not complete and create a get vouchers information Request for request '{@guidName}'", pendingRequest.guid_name); tx.Rollback(); } } } } } Log.Information("Finished processing completed get vouchers information requests"); }
public override void ExecuteJob() { Log.Information("Scanning database for completed transaction validation batches"); using (var dbConnection = new SqlConnection(Configuration.SqlConnectionString)) { using (var dbContext = new DipsDbContext(dbConnection)) { //Get all the potential batches that have been completed var completedBatches = dbContext.Queues .Where(q => !q.ResponseCompleted && q.S_LOCATION == DipsLocationType.TransactionValidationDone.Value && q.S_LOCK.Trim() == "0") .ToList(); Log.Information("Found {0} completed transaction validation batches", completedBatches.Count()); foreach (var completedBatch in completedBatches) { Log.Debug("Creating response for batch {@batch}", completedBatch.S_BATCH); //only commit the transaction if // a) we were the first application to mark this batch row as ValidateTransactionCompleted (DipsQueue uses optimistic concurrency) // b) we were able to place a response message on the bus using (var tx = dbContext.BeginTransaction()) { try { //mark the batch as completed so it gets 'locked' completedBatch.ResponseCompleted = true; var routingKey = completedBatch.RoutingKey; dbContext.SaveChanges(); var batchNumber = completedBatch.S_BATCH; //get the vouchers, generate and send the response var vouchers = dbContext.NabChqPods .Where(v => v.S_BATCH == batchNumber && v.S_DEL_IND != " 255") .ToList() .Select(v => new { S_STATUS1 = int.Parse(string.IsNullOrWhiteSpace(v.S_STATUS1) ? "0" : v.S_STATUS1), voucher = v }).ToList(); if (vouchers.Count > 0) { var firstVoucher = vouchers.First(v => v.voucher.isGeneratedVoucher != "1"); //use bitmasks to map the values from S_STATUS1 field var batchResponse = new ValidateBatchTransactionResponse { voucherBatch = new VoucherBatch { scannedBatchNumber = completedBatch.S_BATCH, workType = ResponseHelper.ParseWorkType(ResponseHelper.TrimString(completedBatch.S_JOB_ID)), batchAccountNumber = firstVoucher.voucher.batchAccountNumber, captureBsb = firstVoucher.voucher.captureBSB, processingState = ResponseHelper.ParseState(ResponseHelper.TrimString(firstVoucher.voucher.processing_state)), collectingBank = firstVoucher.voucher.collecting_bank, unitID = firstVoucher.voucher.unit_id, batchType = ResponseHelper.TrimString(firstVoucher.voucher.batch_type), subBatchType = ResponseHelper.TrimString(firstVoucher.voucher.sub_batch_type), }, voucher = vouchers.Select(v => new ValidateTransactionResponse { documentReferenceNumber = v.voucher.S_TRACE, surplusItemFlag = ResponseHelper.ParseStringAsBool(v.voucher.surplusItemFlag), suspectFraudFlag = ResponseHelper.ParseStringAsBool(v.voucher.micr_suspect_fraud_flag), isGeneratedVoucher = ResponseHelper.ParseStringAsBool(v.voucher.isGeneratedVoucher), codelineFieldsStatus = new CodelineStatus { extraAuxDomStatus = DipsStatus1Bitmask.GetExtraAuxDomStatusMasked(v.S_STATUS1), auxDomStatus = DipsStatus1Bitmask.GetAuxDomStatusMasked(v.S_STATUS1), accountNumberStatus = DipsStatus1Bitmask.GetAccountNumberStatusMasked(v.S_STATUS1), amountStatus = DipsStatus1Bitmask.GetAmountStatusMasked(v.S_STATUS1), bsbNumberStatus = DipsStatus1Bitmask.GetBsbNumberStatusMasked(v.S_STATUS1), transactionCodeStatus = DipsStatus1Bitmask.GetTransactionCodeStatusMasked(v.S_STATUS1), }, reasonCode = ResponseHelper.GetExpertBalanceReason(v.voucher.balanceReason), transactionLinkNumber = ResponseHelper.TrimString(v.voucher.transactionLinkNumber), unprocessable = ResponseHelper.ParseStringAsBool(v.voucher.unproc_flag), forValueIndicator = ResponseHelper.TrimString(v.voucher.fv_ind), dips_override = ResponseHelper.TrimString(v.voucher.@override), thirdPartyCheckRequired = ResponseHelper.ParseYNStringAsBool(v.voucher.tpcRequired), unencodedECDReturnFlag = ResponseHelper.ParseStringAsBool(v.voucher.fxa_unencoded_ECD_return), thirdPartyMixedDepositReturnFlag = ResponseHelper.ParseStringAsBool(v.voucher.tpcMixedDepRet), postTransmissionQaAmountFlag = ResponseHelper.ParseStringAsBool(v.voucher.fxaPtQAAmtFlag), postTransmissionQaCodelineFlag = ResponseHelper.ParseStringAsBool(v.voucher.fxaPtQACodelineFlag), voucher = new Voucher { accountNumber = ResponseHelper.TrimString(v.voucher.acc_num), amount = ResponseHelper.ParseAmountField(v.voucher.amount), auxDom = ResponseHelper.TrimString(v.voucher.ser_num), bsbNumber = v.voucher.bsb_num, //DIPS overwrites doc_ref_num with empty values during auto balancing .. //documentReferenceNumber = ResponseHelper.ResolveDocumentReferenceNumber(ResponseHelper.TrimString(v.voucher.doc_ref_num)), documentReferenceNumber = ResponseHelper.ResolveDocumentReferenceNumber(v.voucher.S_TRACE.Trim()), extraAuxDom = ResponseHelper.TrimString(v.voucher.ead), transactionCode = ResponseHelper.TrimString(v.voucher.trancode), documentType = ResponseHelper.ParseDocumentType(v.voucher.doc_type), processingDate = DateTime.ParseExact(string.Format("{0}{1}", completedBatch.S_SDATE, completedBatch.S_STIME), "dd/MM/yyHH:mm:ss", CultureInfo.InvariantCulture), } }).ToArray() }; if (Configuration.DeleteDatabaseRows) { ResponseHelper.CleanupBatchData(batchNumber, dbContext); } if (string.IsNullOrEmpty(routingKey)) { routingKey = string.Empty; } Exchange.SendMessage(CustomJsonSerializer.MessageToBytes(batchResponse), routingKey.Trim(), completedBatch.CorrelationId); //Task.WaitAll(responseExchange.PublishAsync(batchResponse, completedBatch.CorrelationId, routingKey.Trim())); tx.Commit(); Log.Debug("Transaction validation batch '{@batch}' has been completed and a response has been placed on the queue", completedBatch.S_BATCH); Log.Information("Batch '{@batch}' response sent: {@response}", completedBatch.S_BATCH, batchResponse); } else { Log.Error( "Could not create a transaction validation response for batch '{@batch}' because the vouchers cannot be queried", completedBatch.S_BATCH); } } catch (OptimisticConcurrencyException) { //this is to handle the race condition where more than instance of this service is running at the same time and tries to update the row. //basically ignore the message by loggin a warning and roll back. //if this row was not included by mistake (e.g. it should be included), it will just come in in the next batch run. Log.Warning( "Could not create a transaction validation response for batch '{@batch}' because the DIPS database row was updated by another connection", completedBatch.S_BATCH); tx.Rollback(); } catch (Exception ex) { Log.Error( ex, "Could not complete and create an transaction validation response for batch '{@batch}'", completedBatch.S_BATCH); tx.Rollback(); } } } } } Log.Information("Finished processing completed transaction validation batches"); }