EDMI: Add 3-tier database access in DatabaseWithFallback, add client config,

EDMI Service: Version 2.4.0.0
EDMI: API: Version 1.4.0.0
This commit is contained in:
Jonathan Jenne
2022-01-14 11:48:32 +01:00
parent 7d46951b60
commit bcbfba37b2
58 changed files with 1192 additions and 416 deletions

View File

@@ -0,0 +1,22 @@
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Namespace Methods.Base.GetClientConfig
Public Class GetClientConfigMethod
Inherits BaseMethod
Public Sub New(pLogConfig As LogConfig, pDatabaseIDB As MSSQLServer, pDatabaseECM As MSSQLServer, pGlobalState As GlobalState)
MyBase.New(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
End Sub
Public Function Run() As GetClientConfigResponse
Try
Return New GetClientConfigResponse(GlobalState.ClientConfig)
Catch ex As Exception
Logger.Warn("Error occurred while getting client config!")
Return New GetClientConfigResponse(ex)
End Try
End Function
End Class
End Namespace

View File

@@ -0,0 +1,23 @@
Imports System.Runtime.Serialization
Namespace Methods.Base.GetClientConfig
<Serializable>
<DataContract>
Public Class GetClientConfigResponse
Inherits Messages.BaseResponse
<DataMember>
Public Property ClientConfig As Config.ClientConfiguration
Public Sub New(pConfig As Config.ClientConfiguration)
MyBase.New()
ClientConfig = pConfig
End Sub
Public Sub New(pException As Exception, Optional pDetails As String = "")
MyBase.New(pException, pDetails)
End Sub
End Class
End Namespace

View File

@@ -0,0 +1,66 @@
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Namespace Methods.Database.ExecuteNonQuery
''' <summary>
''' Database method for executing a query from the supplied SQL Command and a Connection Id.
''' </summary>
Public Class ExecuteNonQueryMethod
Inherits BaseMethod
Public Sub New(pLogConfig As LogConfig, pDatabaseIDB As MSSQLServer, pDatabaseECM As MSSQLServer, pGlobalState As GlobalState)
MyBase.New(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
End Sub
''' <summary>
''' Executes a Query. If ConnectionId is empty, uses the configured DD_ECM Database.
''' </summary>
Public Function Run(pData As ExecuteNonQueryRequest) As ExecuteNonQueryResponse
Try
Dim oResult As Boolean = False
Select Case pData.DatabaseType
Case DatabaseType.MSSQL
If pData.NamedDatabase = DatabaseName.None Then
oResult = ExecuteNonQueryByConnectionId(pData.SqlCommand, pData.ConnectionId)
ElseIf pData.NamedDatabase = DatabaseName.ECM Then
oResult = DatabaseECM.ExecuteNonQuery(pData.SqlCommand)
ElseIf pData.NamedDatabase = DatabaseName.IDB Then
oResult = DatabaseIDB.ExecuteNonQuery(pData.SqlCommand)
Else
LogAndThrow($"Unsupported Named Database supplied. SQL Command [{pData.SqlCommand}] was not executed!")
End If
Case Else
LogAndThrow($"Only MSSQL Server is supported at this time. SQL Command [{pData.SqlCommand}] was not executed!")
End Select
Return New ExecuteNonQueryResponse(oResult)
Catch ex As Exception
Logger.Warn("Error occurred while getting database scalar value!")
Return New ExecuteNonQueryResponse(ex)
End Try
End Function
Private Function ExecuteNonQueryByConnectionId(pSQLCommand As String, pConnectionId As Integer) As Boolean
If pConnectionId = 0 Then
Return DatabaseECM.ExecuteNonQuery(pSQLCommand)
Else
Dim oConnectionString = DatabaseECM.Get_ConnectionStringforID(pConnectionId)
If oConnectionString <> "" Then
Return DatabaseIDB.ExecuteNonQueryWithConnection(pSQLCommand, oConnectionString)
Else
Logger.Warn("ConnectionId [{0}] could not be translated into ConnectionString. Exiting.")
Return False
End If
End If
End Function
End Class
End Namespace

View File

@@ -0,0 +1,8 @@
Namespace Methods.Database.ExecuteNonQuery
Public Class ExecuteNonQueryRequest
Public Property SqlCommand As String = ""
Public Property ConnectionId As Integer = 0
Public Property DatabaseType As DatabaseType = DatabaseType.MSSQL
Public Property NamedDatabase As DatabaseName = DatabaseName.ECM
End Class
End Namespace

View File

@@ -0,0 +1,24 @@
Imports System.Runtime.Serialization
Namespace Methods.Database.ExecuteNonQuery
<Serializable>
<DataContract>
Public Class ExecuteNonQueryResponse
Inherits Messages.BaseResponse
<DataMember>
Public Property Result As Boolean
Public Sub New(pResult As Object)
MyBase.New()
Result = pResult
End Sub
Public Sub New(pException As Exception, Optional pDetails As String = "")
MyBase.New(pException, pDetails)
End Sub
End Class
End Namespace

View File

@@ -2,7 +2,7 @@
Imports DigitalData.Modules.Language
Imports DigitalData.Modules.Logging
Namespace Methods.GetDatatableFromCache
Namespace Methods.Database.GetDatatableFromCache
Public Class GetDatatableFromCacheMethod
Inherits BaseMethod

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods.GetDatatableFromCache
Namespace Methods.Database.GetDatatableFromCache
<Serializable>
<DataContract>
Public Class GetDatatableFromCacheRequest

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods.GetDatatableFromCache
Namespace Methods.Database.GetDatatableFromCache
<Serializable>
<DataContract>
Public Class GetDatatableFromCacheResponse

View File

@@ -1,9 +1,10 @@
Imports DigitalData.Modules.Database
Imports System.IO
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Patterns
Imports DigitalData.Modules.Language
Imports DigitalData.Services.EDMIService.Methods.GetDatatableFromCache
Imports System.IO
Imports DigitalData.Services.EDMIService.Methods.Database
Imports DigitalData.Services.EDMIService.Methods.IDB
Imports DigitalData.Modules.ZooFlow.State
Namespace Methods.GlobalIndexer.ImportFile
@@ -12,7 +13,7 @@ Namespace Methods.GlobalIndexer.ImportFile
Private ReadOnly Loader As Loader
Private ReadOnly Patterns As Patterns2
Private ReadOnly GetDatatable As GetDatatableFromCacheMethod
Private ReadOnly GetDatatable As GetDatatableFromCache.GetDatatableFromCacheMethod
Private ReadOnly Connection As SqlClient.SqlConnection
Private ReadOnly Transaction As SqlClient.SqlTransaction
@@ -59,7 +60,7 @@ Namespace Methods.GlobalIndexer.ImportFile
oAutoAttributes = oAutomaticIndexing.ApplyAutomaticeAttributes(oUserAttributes, pData.File.FileInfoRaw, User)
' Import the file
Dim oNewFile As New NewFileMethod(LogConfig, DatabaseIDB, DatabaseECM, GlobalState)
Dim oNewFile As New NewFile.NewFileMethod(LogConfig, DatabaseIDB, DatabaseECM, GlobalState)
Dim oResponse = oNewFile.Run(New NewFile.NewFileRequest With {
.File = pData.File,
.BusinessEntity = pData.BusinessEntity,

View File

@@ -1,5 +1,6 @@
Imports System.Runtime.Serialization
Imports DigitalData.Modules.ZooFlow.State
Imports DigitalData.Services.EDMIService.Methods.IDB
Namespace Methods.GlobalIndexer.ImportFile
<Serializable>

View File

@@ -5,6 +5,7 @@ Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Patterns
Imports DigitalData.Modules.ZooFlow.State
Imports DigitalData.Services.EDMIService.IDB
Imports DigitalData.Services.EDMIService.Methods.IDB
Namespace Methods.GlobalIndexer.ImportFile.Steps
Public Class AutomaticIndexing

View File

@@ -1,5 +1,6 @@
Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Language
Imports DigitalData.Services.EDMIService.Methods.IDB
Namespace Methods.GlobalIndexer.ImportFile.Steps
Public Class PostProcessing

View File

@@ -1,7 +1,7 @@
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Language
Imports DigitalData.Modules.Logging
Imports DigitalData.Services.EDMIService.Methods.GetDatatableFromCache
Imports DigitalData.Services.EDMIService.Methods.Database
Namespace Methods.GlobalIndexer
Public Class Loader
@@ -12,11 +12,11 @@ Namespace Methods.GlobalIndexer
Private Const VIEW_INDEX_AUTOMATIC = "VWDDINDEX_AUTOM"
Private Const TABLE_POST_PROCESSING = "TBDD_INDEX_MAN_POSTPROCESSING"
Private GetDatatable As GetDatatableFromCacheMethod
Private GetDatatable As GetDatatableFromCache.GetDatatableFromCacheMethod
Public Sub New(pLogConfig As LogConfig, pDatabaseIDB As MSSQLServer, pDatabaseECM As MSSQLServer, pGlobalState As GlobalState)
MyBase.New(pLogConfig)
GetDatatable = New GetDatatableFromCacheMethod(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
GetDatatable = New GetDatatableFromCache.GetDatatableFromCacheMethod(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
End Sub
''' <summary>
@@ -26,7 +26,7 @@ Namespace Methods.GlobalIndexer
Logger.Debug("Start of Method [LoadProfile]")
Try
Dim oProfile = GetDatatable.Run(New GetDatatableFromCacheRequest With {
Dim oProfile = GetDatatable.Run(New GetDatatableFromCache.GetDatatableFromCacheRequest With {
.DataTable = VIEW_PROFILE,
.FilterExpression = $"DOCTYPE_ID = {pProfileId}"
})
@@ -69,7 +69,7 @@ Namespace Methods.GlobalIndexer
Try
' Load automatic Indexes for this Import
Dim oAutomaticIndexes = GetDatatable.Run(
New GetDatatableFromCacheRequest With {
New GetDatatableFromCache.GetDatatableFromCacheRequest With {
.DataTable = VIEW_INDEX_AUTOMATIC,
.FilterExpression = $"DOCTYPE_ID = {pProfileId}"
})
@@ -112,7 +112,7 @@ Namespace Methods.GlobalIndexer
Try
' Load manual Indexes for this Import
Dim oManualIndexes = GetDatatable.Run(
New GetDatatableFromCacheRequest With {
New GetDatatableFromCache.GetDatatableFromCacheRequest With {
.DataTable = VIEW_INDEX_MANUAL,
.FilterExpression = $"DOK_ID = {pProfileId}"
})
@@ -166,7 +166,7 @@ Namespace Methods.GlobalIndexer
' Load all relevant postprocessing steps
Dim oPostProcessingSteps = GetDatatable.Run(
New GetDatatableFromCacheRequest With {
New GetDatatableFromCache.GetDatatableFromCacheRequest With {
.DataTable = TABLE_POST_PROCESSING,
.FilterExpression = $"IDXMAN_ID IN ({oIndexIds})"
})

View File

@@ -1,4 +1,4 @@
Namespace Methods
Namespace Methods.IDB
Public MustInherit Class BaseAttributeValue

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods
Namespace Methods.IDB
Public Class FileProperties
''' <summary>
''' Absolute filename of the file to be imported

View File

@@ -1,7 +1,8 @@
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Logging
Imports DigitalData.Services.EDMIService.Methods.IDB.GetAttributeValue
Namespace Methods.GetAttributeValue
Namespace Methods.IDB.GetAttributeValue
Public Class GetAttributeValueMethod
Inherits BaseMethod

View File

@@ -1,4 +1,4 @@
Namespace Methods.GetAttributeValue
Namespace Methods.IDB.GetAttributeValue
Public Class GetAttributeValueRequest
Public Property ObjectId As Long
End Class

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods.GetAttributeValue
Namespace Methods.IDB.GetAttributeValue
<Serializable>
<DataContract>

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods.GetFileObject
Namespace Methods.IDB.GetFileObject
<Serializable>
Public Class FileObject
<DataMember>

View File

@@ -3,7 +3,7 @@ Imports DigitalData.Modules.Logging
Imports DigitalData.Modules.Language
Imports System.IO
Namespace Methods.GetFileObject
Namespace Methods.IDB.GetFileObject
Public Class GetFileObjectMethod
Inherits BaseMethod

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods.GetFileObject
Namespace Methods.IDB.GetFileObject
<Serializable>
<DataContract>
Public Class GetFileObjectRequest

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods.GetFileObject
Namespace Methods.IDB.GetFileObject
<Serializable>
<DataContract>
Public Class GetFileObjectResponse

View File

@@ -0,0 +1,235 @@
Imports System.Data.SqlClient
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Database.MSSQLServer.TransactionMode
Imports DigitalData.Modules.Logging
Imports DigitalData.Services.EDMIService.Methods
Imports DigitalData.Services.EDMIService.GlobalState
Namespace Methods.IDB.NewFile
Public Class NewFileMethod
Inherits BaseMethod
Private ReadOnly Connection As SqlConnection
Private ReadOnly Transaction As SqlTransaction
Public Sub New(pLogConfig As LogConfig, pDatabaseIDB As MSSQLServer, pDatabaseECM As MSSQLServer, pGlobalState As GlobalState)
MyBase.New(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
Connection = DatabaseIDB.GetConnection()
Transaction = Connection.BeginTransaction()
End Sub
Public Function Run(pData As NewFile.NewFileRequest) As Methods.IDB.NewFile.NewFileResponse
Dim oFilePath As String = Nothing
Dim oExistingObjectId = TestFileChecksumExists(pData.File.FileChecksum)
If oExistingObjectId > 0 Then
Return New NewFile.NewFileResponse(oExistingObjectId)
End If
Try
Dim oObjectId = NewObjectId(pData.KindType, pData.BusinessEntity, pData.User.UserName)
If oObjectId = 0 Then
LogAndThrow("Could not create new ObjectId!")
End If
' Find ObjectStore by Title
Logger.Debug("Checking for DataStore [{0}].", pData.StoreName)
Dim oStore = GlobalState.ObjectStores.
Where(Function(store) store.Title.Equals(pData.StoreName, StringComparison.OrdinalIgnoreCase)).
SingleOrDefault()
If oStore Is Nothing Then
LogAndThrow($"DataStore [{pData.StoreName}] does not exist. Exiting.")
End If
' Get Store base path
Dim oBasePath As String = oStore.Path
Logger.Debug("Store BasePath is [{0}]", oBasePath)
' Get directory by DateImported or, if not supplied, by current date
Dim oSubDirectory As String
If IsNothing(pData.File.FileImportedAt) Then
oSubDirectory = GetDateSubDirectory(Now)
Else
oSubDirectory = GetDateSubDirectory(pData.File.FileImportedAt)
End If
Logger.Debug("Subdirectory is [{0}]", oSubDirectory)
' Check and create final path, if necessary
Dim oFinalPath = IO.Path.Combine(oBasePath, oSubDirectory)
If Not IO.Directory.Exists(oFinalPath) Then
Try
Logger.Debug("Path does not exist, creating: [{0}]", oFinalPath)
IO.Directory.CreateDirectory(oFinalPath)
Logger.Debug("Created folder [{0}]", oFinalPath)
Catch ex As Exception
LogAndThrow(ex, $"Store Directory [{oFinalPath}] could not be created!")
End Try
End If
Logger.Debug("Final Directory is [{0}]", oFinalPath)
' Get filename
Dim oKeepFileName As Boolean = False
If oStore.IsArchive Then
Logger.Debug("Object Store is an archive: [{0}]", oStore.IsArchive)
oKeepFileName = True
End If
Dim oFileName As String = GetFileObjectFileName(oObjectId, pData.File.FileName, oKeepFileName)
Logger.Debug("Filename is [{0}]", oFileName)
oFilePath = IO.Path.Combine(oFinalPath, oFileName)
Dim oFileObjectInfo As IO.FileInfo = New IO.FileInfo(oFilePath)
Dim oFileObjectSize As Long = pData.File.FileContents.Length
Dim oFileObjectName As String = oFileObjectInfo.Name
Dim oOriginalExtension As String = pData.File.FileInfoRaw.Extension.Substring(1)
Logger.Debug("File Information for [{0}]:", oFileObjectName)
Logger.Debug("Size: [{0}]", oFileObjectSize)
Logger.Debug("Original Extension: [{0}]", oOriginalExtension)
Logger.Debug("Checksum: [{0}]", pData.File.FileChecksum)
Try
Using oStream = New IO.FileStream(oFilePath, IO.FileMode.Create, IO.FileAccess.Write)
Logger.Info("Saving file to path [{0}]", oFilePath)
oStream.Write(pData.File.FileContents, 0, oFileObjectSize)
oStream.Flush(True)
oStream.Close()
End Using
Catch ex As Exception
LogAndThrow(ex, $"Could not write file [{oFilePath}] to disk!")
End Try
'---------------------------------------------------------------------------
Logger.Info("Creating IDB FileObject for ObjectId [{0}].", oObjectId)
' Insert into DB
Dim oSQL As String = $"EXEC PRIDB_NEW_IDBFO
'{oFinalPath}',
'{oFileObjectName}',
'{oOriginalExtension}',
{oFileObjectSize},
'{pData.File.FileChecksum}' ,
'{pData.User.UserName}',
'{oObjectId}',
{oStore.Id}"
Dim oResult As Boolean = DatabaseIDB.ExecuteNonQueryWithConnectionObject(oSQL, Connection, ExternalTransaction, Transaction)
If oResult = False Then
LogAndThrow("IDB FileObject could not be created!")
End If
'---------------------------------------------------------------------------
'TODO: File dates in try catch
Dim oSystemAttributes As New Dictionary(Of String, Object) From {
{"OriginFileName", pData.File.FileName},
{"OriginCreationDatetime", pData.File.FileCreatedAt},
{"OriginChangedDatetime", pData.File.FileChangedAt}
}
For Each oAttribute As KeyValuePair(Of String, Object) In oSystemAttributes
Try
' Dont write empty attributes
If oAttribute.Value Is Nothing Then
Continue For
End If
Dim oSuccess = Helpers.SetAttributeValue(Connection, Transaction, oObjectId, oAttribute.Key, oAttribute.Value, pData.User.Language, pData.User.UserName)
If oSuccess Then
Logger.Debug("System Attribute [{0}] written with value [{1}]", oAttribute.Key, oAttribute.Value)
Else
Logger.Warn("System attribute value could not be written")
End If
Catch ex As Exception
LogAndThrow(ex, $"System attribute [{oAttribute.Key}] could not be written!")
End Try
Next
'---------------------------------------------------------------------------
' Finally, commit the transaction
Transaction?.Commit()
Return New NewFile.NewFileResponse(oObjectId)
Catch ex As Exception
Logger.Warn("Error occurred while creating file!")
Logger.Error(ex)
Logger.Info("Cleaning up files.")
If Not IsNothing(oFilePath) AndAlso IO.File.Exists(oFilePath) Then
Try
IO.File.Delete(oFilePath)
Catch exInner As Exception
Logger.Warn("Error while cleaning up files.")
Logger.Error(exInner)
End Try
End If
Logger.Info("Rolling back transaction.")
Transaction?.Rollback()
Return New NewFile.NewFileResponse(ex)
End Try
End Function
Private Function TestFileChecksumExists(pChecksum As String) As Long
Try
Dim oChecksumSQL = $"SELECT IDB_OBJ_ID FROM TBIDB_FILE_OBJECT WHERE FILE_HASH = '{pChecksum}'"
Dim oExistingObjectId As Long = DatabaseIDB.GetScalarValue(oChecksumSQL)
If oExistingObjectId > 0 Then
Logger.Info("Returning early with ObjectId [{0}] because Checksum [{1}] already exists.", oExistingObjectId, pChecksum)
Return oExistingObjectId
End If
Return Nothing
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function NewObjectId(pKindType As String, pBusinessEntity As String, pWho As String) As Long
Try
Dim oNewObjectIdSQL = $"DECLARE @NEW_IDB_OBJ_ID BIGINT
EXEC PRIDB_NEW_OBJECT '{pKindType}','{pWho}','{pBusinessEntity}',0, @IDB_OBJ_ID = @NEW_IDB_OBJ_ID OUTPUT;
SELECT @NEW_IDB_OBJ_ID"
Dim oObjectId As Long = DatabaseIDB.GetScalarValueWithConnectionObject(oNewObjectIdSQL, Connection, ExternalTransaction, Transaction)
Logger.Info("New Object with Id [{0}] created!", oObjectId)
If IsNothing(oObjectId) Then
LogAndThrow("Could not create new ObjectId!")
End If
Return oObjectId
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function GetFileObjectFileName(IDB_OBJ_ID As Long, pFilename As String, pKeepFilename As Boolean) As String
' TODO: save actual extensions
If pKeepFilename Then
Return pFilename
Else
Return $"{IDB_OBJ_ID}.ddfo"
End If
End Function
Private Function GetDateSubDirectory(pDate As Date) As String
Return IO.Path.Combine(pDate.ToString("yyyy"), pDate.ToString("MM"), pDate.ToString("dd"))
End Function
End Class
End Namespace

View File

@@ -1,7 +1,7 @@
Imports System.Runtime.Serialization
Imports DigitalData.Modules.ZooFlow.State
Namespace Methods.NewFile
Namespace Methods.IDB.NewFile
<Serializable>
<DataContract>
Public Class NewFileRequest

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods.NewFile
Namespace Methods.IDB.NewFile
<Serializable>
<DataContract>
Public Class NewFileResponse

View File

@@ -3,7 +3,7 @@ Imports DigitalData.Modules.Database
Imports DigitalData.Services.EDMIService.IDB
Imports System.Data.SqlClient
Namespace Methods.SetAttributeValue
Namespace Methods.IDB.SetAttributeValue
Public Class SetAttributeValueMethod
Inherits BaseMethod

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods.SetAttributeValue
Namespace Methods.IDB.SetAttributeValue
<Serializable>
<DataContract>
Public Class SetAttributeValueRequest

View File

@@ -1,6 +1,6 @@
Imports System.Runtime.Serialization
Namespace Methods.SetAttributeValue
Namespace Methods.IDB.SetAttributeValue
<Serializable>
<DataContract>
Public Class SetAttributeValueResponse

View File

@@ -1,232 +0,0 @@
Imports System.Data.SqlClient
Imports DigitalData.Modules.Database
Imports DigitalData.Modules.Database.MSSQLServer.TransactionMode
Imports DigitalData.Modules.Logging
Imports DigitalData.Services.EDMIService.Methods
Imports DigitalData.Services.EDMIService.GlobalState
Public Class NewFileMethod
Inherits BaseMethod
Private ReadOnly Connection As SqlConnection
Private ReadOnly Transaction As SqlTransaction
Public Sub New(pLogConfig As LogConfig, pDatabaseIDB As MSSQLServer, pDatabaseECM As MSSQLServer, pGlobalState As GlobalState)
MyBase.New(pLogConfig, pDatabaseIDB, pDatabaseECM, pGlobalState)
Connection = DatabaseIDB.GetConnection()
Transaction = Connection.BeginTransaction()
End Sub
Public Function Run(pData As NewFile.NewFileRequest) As NewFile.NewFileResponse
Dim oFilePath As String = Nothing
Dim oExistingObjectId = TestFileChecksumExists(pData.File.FileChecksum)
If oExistingObjectId > 0 Then
Return New NewFile.NewFileResponse(oExistingObjectId)
End If
Try
Dim oObjectId = NewObjectId(pData.KindType, pData.BusinessEntity, pData.User.UserName)
If oObjectId = 0 Then
LogAndThrow("Could not create new ObjectId!")
End If
' Find ObjectStore by Title
Logger.Debug("Checking for DataStore [{0}].", pData.StoreName)
Dim oStore = GlobalState.ObjectStores.
Where(Function(store) store.Title.Equals(pData.StoreName, StringComparison.OrdinalIgnoreCase)).
SingleOrDefault()
If oStore Is Nothing Then
LogAndThrow($"DataStore [{pData.StoreName}] does not exist. Exiting.")
End If
' Get Store base path
Dim oBasePath As String = oStore.Path
Logger.Debug("Store BasePath is [{0}]", oBasePath)
' Get directory by DateImported or, if not supplied, by current date
Dim oSubDirectory As String
If IsNothing(pData.File.FileImportedAt) Then
oSubDirectory = GetDateSubDirectory(Now)
Else
oSubDirectory = GetDateSubDirectory(pData.File.FileImportedAt)
End If
Logger.Debug("Subdirectory is [{0}]", oSubDirectory)
' Check and create final path, if necessary
Dim oFinalPath = IO.Path.Combine(oBasePath, oSubDirectory)
If Not IO.Directory.Exists(oFinalPath) Then
Try
Logger.Debug("Path does not exist, creating: [{0}]", oFinalPath)
IO.Directory.CreateDirectory(oFinalPath)
Logger.Debug("Created folder [{0}]", oFinalPath)
Catch ex As Exception
LogAndThrow(ex, $"Store Directory [{oFinalPath}] could not be created!")
End Try
End If
Logger.Debug("Final Directory is [{0}]", oFinalPath)
' Get filename
Dim oKeepFileName As Boolean = False
If oStore.IsArchive Then
Logger.Debug("Object Store is an archive: [{0}]", oStore.IsArchive)
oKeepFileName = True
End If
Dim oFileName As String = GetFileObjectFileName(oObjectId, pData.File.FileName, oKeepFileName)
Logger.Debug("Filename is [{0}]", oFileName)
oFilePath = IO.Path.Combine(oFinalPath, oFileName)
Dim oFileObjectInfo As IO.FileInfo = New IO.FileInfo(oFilePath)
Dim oFileObjectSize As Long = pData.File.FileContents.Length
Dim oFileObjectName As String = oFileObjectInfo.Name
Dim oOriginalExtension As String = pData.File.FileInfoRaw.Extension.Substring(1)
Logger.Debug("File Information for [{0}]:", oFileObjectName)
Logger.Debug("Size: [{0}]", oFileObjectSize)
Logger.Debug("Original Extension: [{0}]", oOriginalExtension)
Logger.Debug("Checksum: [{0}]", pData.File.FileChecksum)
Try
Using oStream = New IO.FileStream(oFilePath, IO.FileMode.Create, IO.FileAccess.Write)
Logger.Info("Saving file to path [{0}]", oFilePath)
oStream.Write(pData.File.FileContents, 0, oFileObjectSize)
oStream.Flush(True)
oStream.Close()
End Using
Catch ex As Exception
LogAndThrow(ex, $"Could not write file [{oFilePath}] to disk!")
End Try
'---------------------------------------------------------------------------
Logger.Info("Creating IDB FileObject for ObjectId [{0}].", oObjectId)
' Insert into DB
Dim oSQL As String = $"EXEC PRIDB_NEW_IDBFO
'{oFinalPath}',
'{oFileObjectName}',
'{oOriginalExtension}',
{oFileObjectSize},
'{pData.File.FileChecksum}' ,
'{pData.User.UserName}',
'{oObjectId}',
{oStore.Id}"
Dim oResult As Boolean = DatabaseIDB.ExecuteNonQueryWithConnectionObject(oSQL, Connection, ExternalTransaction, Transaction)
If oResult = False Then
LogAndThrow("IDB FileObject could not be created!")
End If
'---------------------------------------------------------------------------
'TODO: File dates in try catch
Dim oSystemAttributes As New Dictionary(Of String, Object) From {
{"OriginFileName", pData.File.FileName},
{"OriginCreationDatetime", pData.File.FileCreatedAt},
{"OriginChangedDatetime", pData.File.FileChangedAt}
}
For Each oAttribute As KeyValuePair(Of String, Object) In oSystemAttributes
Try
' Dont write empty attributes
If oAttribute.Value Is Nothing Then
Continue For
End If
Dim oSuccess = Helpers.SetAttributeValue(Connection, Transaction, oObjectId, oAttribute.Key, oAttribute.Value, pData.User.Language, pData.User.UserName)
If oSuccess Then
Logger.Debug("System Attribute [{0}] written with value [{1}]", oAttribute.Key, oAttribute.Value)
Else
Logger.Warn("System attribute value could not be written")
End If
Catch ex As Exception
LogAndThrow(ex, $"System attribute [{oAttribute.Key}] could not be written!")
End Try
Next
'---------------------------------------------------------------------------
' Finally, commit the transaction
Transaction?.Commit()
Return New NewFile.NewFileResponse(oObjectId)
Catch ex As Exception
Logger.Warn("Error occurred while creating file!")
Logger.Error(ex)
Logger.Info("Cleaning up files.")
If Not IsNothing(oFilePath) AndAlso IO.File.Exists(oFilePath) Then
Try
IO.File.Delete(oFilePath)
Catch exInner As Exception
Logger.Warn("Error while cleaning up files.")
Logger.Error(exInner)
End Try
End If
Logger.Info("Rolling back transaction.")
Transaction?.Rollback()
Return New NewFile.NewFileResponse(ex)
End Try
End Function
Private Function TestFileChecksumExists(pChecksum As String) As Long
Try
Dim oChecksumSQL = $"SELECT IDB_OBJ_ID FROM TBIDB_FILE_OBJECT WHERE FILE_HASH = '{pChecksum}'"
Dim oExistingObjectId As Long = DatabaseIDB.GetScalarValue(oChecksumSQL)
If oExistingObjectId > 0 Then
Logger.Info("Returning early with ObjectId [{0}] because Checksum [{1}] already exists.", oExistingObjectId, pChecksum)
Return oExistingObjectId
End If
Return Nothing
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function NewObjectId(pKindType As String, pBusinessEntity As String, pWho As String) As Long
Try
Dim oNewObjectIdSQL = $"DECLARE @NEW_IDB_OBJ_ID BIGINT
EXEC PRIDB_NEW_OBJECT '{pKindType}','{pWho}','{pBusinessEntity}',0, @IDB_OBJ_ID = @NEW_IDB_OBJ_ID OUTPUT;
SELECT @NEW_IDB_OBJ_ID"
Dim oObjectId As Long = DatabaseIDB.GetScalarValueWithConnectionObject(oNewObjectIdSQL, Connection, ExternalTransaction, Transaction)
Logger.Info("New Object with Id [{0}] created!", oObjectId)
If IsNothing(oObjectId) Then
LogAndThrow("Could not create new ObjectId!")
End If
Return oObjectId
Catch ex As Exception
Logger.Error(ex)
Return Nothing
End Try
End Function
Private Function GetFileObjectFileName(IDB_OBJ_ID As Long, pFilename As String, pKeepFilename As Boolean) As String
' TODO: save actual extensions
If pKeepFilename Then
Return pFilename
Else
Return $"{IDB_OBJ_ID}.ddfo"
End If
End Function
Private Function GetDateSubDirectory(pDate As Date) As String
Return IO.Path.Combine(pDate.ToString("yyyy"), pDate.ToString("MM"), pDate.ToString("dd"))
End Function
End Class