See my portfolio for information on the Document Builder project that used the Query Tree component.
Imports System.Collections ' For access to IComparer
Imports System.Data.Common ' For access to DbCommand, DbDataReader
Imports NACS.Utilities.ObjectReader ' For access to DbObjectReader, Required, Collection
Imports NACS.Helpers.Activator ' For access to ActivatorHelper
Imports NACS.Helpers.Data ' For access to DataHelper
Namespace NACS.Data.QueryTree
Public Class Node
#Region " Member Variables and Properties "
' Public fields read from the xml file
<Required()> _
Public Name As String = Nothing
<Required()> _
Public KeyName As String = Nothing
<Required()> _
Public QueryFile As String = Nothing
<Collection(Type:=GetType(Node))> _
Public Nodes As Nodes = Nothing
' Private member variables corresponding to public properties
Private m_oRoot As Root = Nothing
Private m_oParent As Node = Nothing
Private m_oKeyValue As Object = Nothing
Private m_iRecordCount As Integer = 0
Private m_iTotalRecordCount As Integer = 0
Private m_bIsEntering As Boolean = False
Private m_bIsLeaving As Boolean = False
Private m_bIsFirst As Boolean = False
Private m_bIsLast As Boolean = False
Private m_oEntity As IEntityInstance = Nothing
' Private member variables used internally
Private m_oCommand As DbCommand = Nothing
Private m_oDataReader As DbDataReader = Nothing
Private m_iKeyOrdinal As Integer = -1
Private m_iParentKeyOrdinal As Integer = -1
Private m_oObjectReader As ObjectReader = Nothing
Public Property Root() As Root
Get
Return m_oRoot
End Get
Private Set(ByVal oRoot As Root)
m_oRoot = oRoot
End Set
End Property
Public Property Parent() As Node
Get
Return m_oParent
End Get
Private Set(ByVal oParent As Node)
m_oParent = oParent
End Set
End Property
Public Property KeyValue() As Object
Get
Return m_oKeyValue
End Get
Private Set(ByVal oKeyValue As Object)
m_oKeyValue = oKeyValue
End Set
End Property
Public Property RecordCount() As Integer
Get
Return m_iRecordCount
End Get
Private Set(ByVal iRecordNumber As Integer)
m_iRecordCount = iRecordNumber
End Set
End Property
Public Property TotalRecordCount() As Integer
Get
Return m_iTotalRecordCount
End Get
Private Set(ByVal iTotalRecordNumber As Integer)
m_iTotalRecordCount = iTotalRecordNumber
End Set
End Property
Public Property IsEntering() As Boolean
Get
Return m_bIsEntering
End Get
Friend Set(ByVal bIsEntering As Boolean)
m_bIsEntering = bIsEntering
End Set
End Property
Public Property IsLeaving() As Boolean
Get
Return m_bIsLeaving
End Get
Friend Set(ByVal bIsLeaving As Boolean)
m_bIsLeaving = bIsLeaving
End Set
End Property
Public Property IsFirst() As Boolean
Get
Return m_bIsFirst
End Get
Friend Set(ByVal bIsFirst As Boolean)
m_bIsFirst = bIsFirst
End Set
End Property
Public Property IsLast() As Boolean
Get
Return m_bIsLast
End Get
Friend Set(ByVal bIsLast As Boolean)
m_bIsLast = bIsLast
End Set
End Property
Public Property Entity() As IEntityInstance
Get
Return m_oEntity
End Get
Private Set(ByVal oInstance As IEntityInstance)
m_oEntity = oInstance
End Set
End Property
Private Property Command() As DbCommand
Get
Return m_oCommand
End Get
Set(ByVal oCommand As DbCommand)
m_oCommand = oCommand
End Set
End Property
Private Property DataReader() As DbDataReader
Get
Return m_oDataReader
End Get
Set(ByVal oDataReader As DbDataReader)
m_oDataReader = oDataReader
End Set
End Property
Private Property KeyOrdinal() As Integer
Get
Return m_iKeyOrdinal
End Get
Set(ByVal iKeyOrdinal As Integer)
m_iKeyOrdinal = iKeyOrdinal
End Set
End Property
Private Property ParentKeyOrdinal() As Integer
Get
Return m_iParentKeyOrdinal
End Get
Set(ByVal iParentKeyOrdinal As Integer)
m_iParentKeyOrdinal = iParentKeyOrdinal
End Set
End Property
Private Property ObjectReader() As ObjectReader
Get
Return m_oObjectReader
End Get
Set(ByVal oObjectReader As ObjectReader)
m_oObjectReader = oObjectReader
End Set
End Property
#End Region
#Region " Initialize() and Terminate() Methods "
Friend Sub Initialize(ByVal oRoot As Root, ByVal oParent As Node)
Me.Root = oRoot
Me.Parent = oParent
Me.KeyValue = Nothing
Me.RecordCount = 0
Me.TotalRecordCount = 0
Me.InitInstance()
Me.InitDataReader()
Me.InitOrdinals()
Me.InitObjectReader()
Me.InitNodes()
End Sub
Private Sub InitInstance()
Try
Me.Entity = DirectCast(ActivatorHelper.CreateObject(Me.Root.Assembly, Me.Root.Namespace, Me.Name), IEntityInstance)
Catch oException As Exception
Throw New NoEntityInstanceException(Me.Root.Assembly, Me.Root.Namespace, Me.Name, oException)
End Try
End Sub
Private Sub InitDataReader()
Dim sQueryText As String
Try
sQueryText = My.Computer.FileSystem.ReadAllText(Me.QueryFile)
Catch oException As Exception
Throw New NoQueryFileException(Me.Name, Me.QueryFile, oException)
End Try
Try
m_oCommand = Me.Root.Factory.CreateCommand()
m_oCommand.Connection = Me.Root.Connection
m_oCommand.CommandText = sQueryText
m_oCommand.CommandTimeout = 480
m_oDataReader = m_oCommand.ExecuteReader()
Catch oException As Exception
Throw New NoDataReaderException(Me.Name, Me.Root.ConnectionString, oException)
End Try
End Sub
Private Sub InitOrdinals()
Try
m_iKeyOrdinal = m_oDataReader.GetOrdinal(Me.Name & Me.KeyName)
Catch oException As Exception
Throw New NoKeyException(Me.Name, Me.KeyName, oException)
End Try
If Me.Parent IsNot Nothing Then
Try
m_iParentKeyOrdinal = m_oDataReader.GetOrdinal(Me.Parent.Name & Me.Parent.KeyName)
Catch oException As Exception
Throw New NoParentKeyException(Me.Name, Me.Parent.Name, Me.Parent.KeyName, oException)
End Try
End If
End Sub
Private Sub InitObjectReader()
Me.ObjectReader = New DbObjectReader(m_oDataReader)
Me.AdvanceReader()
End Sub
Private Sub InitNodes()
If Me.Nodes IsNot Nothing Then
For Each oNode As Node In Me.Nodes
oNode.Initialize(Me.Root, Me)
Next
End If
End Sub
Friend Sub Terminate()
Me.TermNodes()
Me.ObjectReader = Nothing
Me.TermDataReader()
Me.Entity = Nothing
Me.KeyValue = Nothing
Me.Parent = Nothing
Me.Root = Nothing
End Sub
Private Sub TermNodes()
If Me.Nodes IsNot Nothing Then
For iNode As Integer = Me.Nodes.Count - 1 To 0 Step -1
Me.Nodes(iNode).Terminate()
Next
End If
End Sub
Private Sub TermDataReader()
If m_oDataReader IsNot Nothing Then
m_oDataReader.Close()
m_oDataReader = Nothing
End If
If m_oCommand IsNot Nothing Then
m_oCommand.Dispose()
m_oCommand = Nothing
End If
End Sub
#End Region
#Region " Process() and Related Methods "
Friend Sub Process()
' When we enter this method, the data reader will be on the current record.
Me.RecordCount = 0
Do While IsDataAvailable()
HandleTransition(Status.DataAvailable)
Me.AdvanceReader()
Loop
HandleTransition(Status.NoDataAvailable)
End Sub
Private Function IsDataAvailable() As Boolean
If Me.ObjectReader.IsDataAvailable Then
If Me.Parent IsNot Nothing Then
Return Me.IsChildInSyncWithParent
Else
Return True
End If
Else
Return False
End If
End Function
Private Function IsChildInSyncWithParent() As Boolean
' This compares the current key value of our parent node with the key value for our parent that's in our query data.
' If the two don't match, that means we've moved onto a record that doesn't belong to our parent.
If Me.Root.Comparer.Compare(Me.Parent.KeyValue, DataHelper.GetDataReaderValue(Me.DataReader, Me.ParentKeyOrdinal)) = 0 Then
Return True
Else
Return False
End If
End Function
Private Sub HandleTransition(ByVal eStatus As Status)
Me.PrepareEntity(eStatus) ' Loads the new key value and figures if we're leaving and/or entering old/new records.
If Me.IsLeaving Then Me.Entity.LeaveRecord(Me, Me.Root.Arguments)
Me.LoadEntity(eStatus) ' Loads all of the fields in the object from the data source.
If Me.IsEntering Then Me.Entity.EnterRecord(Me, Me.Root.Arguments)
ProcessChildren()
End Sub
Private Sub PrepareEntity(ByVal eDataStatus As Status)
Select Case eDataStatus
Case Status.DataAvailable : SetNewKeyValue(DataHelper.GetDataReaderValue(Me.DataReader, Me.KeyOrdinal))
Case Status.NoDataAvailable : SetNewKeyValue(Nothing)
End Select
End Sub
Private Sub SetNewKeyValue(ByVal oNewKeyValue As Object)
' oNewKeyValue is the new key value we just read from the data source. We need to compare it to the key value for the
' previous record (which is in Me.KeyValue at the start of this method) to figure out what kind of transition,
' if any, has happened. We then save the new key value into the entity as the current key.
Dim oPrevKeyValue As Object = Me.KeyValue
Me.IsEntering = False : Me.IsLeaving = False : Me.IsFirst = False : Me.IsLast = False
If Me.Root.Comparer.Compare(oPrevKeyValue, oNewKeyValue) <> 0 Then
' The new current key is different from the old key, so we know we're transitioning to a new record.
If oPrevKeyValue IsNot Nothing Then
' The previous key was non-null, so we're leaving a valid record. If the new current
' key is null, then that valid record is the last one.
Me.IsLeaving = True
If oNewKeyValue Is Nothing Then
Me.IsLast = True
End If
End If
If oNewKeyValue IsNot Nothing Then
' The new current key is non-null, so we're entering a valid record. If the previous
' key is null, then this valid record is the first one.
Me.IsEntering = True
If oPrevKeyValue Is Nothing Then
Me.IsFirst = True
Me.RecordCount = 1
Else
Me.RecordCount += 1
End If
Me.TotalRecordCount += 1
End If
End If
Me.KeyValue = oNewKeyValue
End Sub
Private Sub LoadEntity(ByVal eDataStatus As Status)
Select Case eDataStatus
Case Status.DataAvailable : Me.ObjectReader.GetValues(Me.Entity)
Case Status.NoDataAvailable : Me.ObjectReader.ClearValues(Me.Entity)
End Select
End Sub
Private Sub ProcessChildren()
If Me.Nodes IsNot Nothing Then
For Each oNode As Node In Me.Nodes
oNode.Process()
Next
End If
End Sub
Private Sub AdvanceReader()
Try
Me.ObjectReader.Read()
Catch oException As Exception
Throw New DataReadException(Me.Name, oException)
End Try
End Sub
#End Region
End Class
End Namespace





