Определение свойства класса, чтобы избежать ненужных обращений к базе данных


Проблема я столкнулся, заключается в создании класса для Loan объект, который содержит ряд свойств, некоторые из которых являются классы сами по себе с аналогичными атрибутами (например, Lender), а также некоторые List(Of...) свойства (Borrowers и залога). Я не хочу, чтобы эти классы загружать свои свойства, если только специально позвонил по нескольким причинам:

  • Избежать ненужных обращений к базе данных
  • Попытаться предотвратить использование "устаревших" данных в родительском классе при обращении к этим свойствам
  • Когда это List(Of...) собственность, вещи могут получить дополнительную рисковую

Для того, чтобы достичь этого, я собрал следующую структуру, которая, кажется, работает нормально. Однако, я действительно начинаю задаваться вопросом, если я слишком много думаю и что делает его гораздо более сложным, чем он должен быть - в основном в List(Of...) свойства. Любые предложения, безусловно, приветствуем.

#Region "LOAN RECORD"
    ''' <summary>
    ''' Standard object containing details about a specific loan record
    ''' </summary>
    Public Class Loan
#Region "PRIVATE PROPERTIES"
        <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Private LenderID As Integer

        <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Private Property BorrowerIDs As List(Of Integer)

        <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Private Property VehicleIDs As List(Of Integer)

        <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Private Property RealEstatePropertyIDs As List(Of Integer)

#Region "BUFFERS TO PREVENT UNNECESSARY DATABASE CALLS"
        <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Private _Lender As Lender

        <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Private _Borrowers As List(Of Borrower)

        <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Private _Vehicles As List(Of VehicleCollateral)

        <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Private _RealEstateProperties As List(Of RealEstateCollateral)
#End Region
#End Region

#Region "PUBLIC PROPERTIES"
        Public Property LoanID As Integer
        Public Property LoanNumber As String
        Public Property InceptionDate As Nullable(Of Date)
        Public Property MaturityDate As Nullable(Of Date)
        Public Property CurrentBalance As Decimal
        Public Property CreditLimit As Decimal
        Public Property InterestRate As Decimal
        Public Property PaymentFrequency As Integer
        Public Property CurrentPaymentAmount As Decimal
        Public Property PaidDate As Nullable(Of Date)

        <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Public Property Lender As Lender
            Get
                If Me._Lender Is Nothing OrElse Me._Lender.LenderID <> Me.LenderID Then
                    Me._Lender = New Lender(Me.LenderID)
                End If

                Return Me._Lender
            End Get

            Set(value As Lender)
                Me.LenderID = value.LenderID
                Me._Lender = value
            End Set
        End Property

        <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Public Property Borrowers As List(Of Borrower)
            Get
                If Not Me.BorrowerIDs Is Nothing AndAlso Me.BorrowerIDs.Count > 0 Then
                    If Me._Borrowers Is Nothing OrElse Me._Borrowers.Count <> Me.BorrowerIDs.Count Then
                        Dim LoanBorrowers As New List(Of Borrower)

                        For Each BorrowerID As Integer In Me.BorrowerIDs
                            If BorrowerID > 0 Then
                                LoanBorrowers.Add(New Borrower(BorrowerID))
                            End If
                        Next BorrowerID

                        If LoanBorrowers.Count > 0 Then
                            If Not Me._Borrowers Is Nothing Then
                                Me._Borrowers.Clear()
                            Else
                                Me._Borrowers = New List(Of Borrower)
                                Me._Borrowers.Clear()
                            End If

                            Me._Borrowers = LoanBorrowers
                        Else
                            Me._Borrowers = Nothing
                        End If
                    Else
                        Dim Reload As Boolean = False

                        For Each LoanBorrower As Borrower In Me._Borrowers
                            If Not Me.BorrowerIDs.Contains(LoanBorrower.BorrowerID) Then
                                Reload = True
                                Exit For
                            End If
                        Next LoanBorrower

                        If Reload Then
                            Dim LoanBorrowers As New List(Of Borrower)

                            For Each BorrowerID As Integer In Me.BorrowerIDs
                                If BorrowerID > 0 Then
                                    LoanBorrowers.Add(New Borrower(BorrowerID))
                                End If
                            Next BorrowerID

                            If LoanBorrowers.Count > 0 Then
                                If Not Me._Borrowers Is Nothing Then
                                    Me._Borrowers.Clear()
                                Else
                                    Me._Borrowers = New List(Of Borrower)
                                    Me._Borrowers.Clear()
                                End If

                                Me._Borrowers = LoanBorrowers
                            Else
                                Me._Borrowers = Nothing
                            End If
                        End If
                    End If
                Else
                    Me._Borrowers = Nothing
                End If

                Return Me._Borrowers
            End Get

            Set(value As List(Of Borrower))
                If Not value Is Nothing AndAlso value.Count > 0 Then
                    If Me.BorrowerIDs Is Nothing Then
                        Me.BorrowerIDs = New List(Of Integer)
                    End If

                    Me.BorrowerIDs.Clear()

                    For Each valueBorrower As Borrower In value
                        Me.BorrowerIDs.Add(valueBorrower.BorrowerID)
                    Next valueBorrower

                    Me._Borrowers = value
                Else
                    Me._Borrowers = Nothing
                End If
            End Set
        End Property

        <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Public Property Vehicles As List(Of VehicleCollateral)
            Get
                If Not Me.VehicleIDs Is Nothing AndAlso Me.VehicleIDs.Count > 0 Then
                    If Me._Vehicles Is Nothing OrElse Me._Vehicles.Count <> Me.VehicleIDs.Count Then
                        Dim Collateral As New List(Of VehicleCollateral)

                        For Each CollateralID As Integer In Me.VehicleIDs
                            If CollateralID > 0 Then
                                Collateral.Add(New VehicleCollateral(CollateralID))
                            End If
                        Next CollateralID

                        If Collateral.Count > 0 Then
                            If Not Me._Vehicles Is Nothing Then
                                Me._Vehicles.Clear()
                            Else
                                Me._Vehicles = New List(Of VehicleCollateral)
                                Me._Vehicles.Clear()
                            End If

                            Me._Vehicles = Collateral
                        Else
                            Me._Vehicles = Nothing
                        End If
                    Else
                        Dim Reload As Boolean = False

                        For Each Vehicle As VehicleCollateral In Me._Vehicles
                            If Not Me.VehicleIDs.Contains(Vehicle.CollateralID) Then
                                Reload = True
                                Exit For
                            End If
                        Next Vehicle

                        If Reload Then
                            Dim Collateral As New List(Of VehicleCollateral)

                            For Each CollateralID As Integer In Me.VehicleIDs
                                If CollateralID > 0 Then
                                    Collateral.Add(New VehicleCollateral(CollateralID))
                                End If
                            Next CollateralID

                            If Collateral.Count > 0 Then
                                If Not Me._Vehicles Is Nothing Then
                                    Me._Vehicles.Clear()
                                Else
                                    Me._Vehicles = New List(Of VehicleCollateral)
                                    Me._Vehicles.Clear()
                                End If

                                Me._Vehicles = Collateral
                            Else
                                Me._Vehicles = Nothing
                            End If
                        End If
                    End If
                Else
                    Me._Vehicles = Nothing
                End If

                Return Me._Vehicles
            End Get

            Set(value As List(Of VehicleCollateral))
                If Not value Is Nothing AndAlso value.Count > 0 Then
                    If Me.VehicleIDs Is Nothing Then
                        Me.VehicleIDs = New List(Of Integer)
                    End If

                    Me.VehicleIDs.Clear()

                    For Each valueCollateral As VehicleCollateral In value
                        Me.VehicleIDs.Add(valueCollateral.CollateralID)
                    Next valueCollateral

                    Me._Vehicles = value
                Else
                    Me._Vehicles = Nothing
                End If
            End Set
        End Property

        <DebuggerBrowsable(DebuggerBrowsableState.Never)>
        Public Property RealEstateProperties As List(Of RealEstateCollateral)
            Get
                If Not Me.RealEstatePropertyIDs Is Nothing AndAlso Me.RealEstatePropertyIDs.Count > 0 Then
                    If Me._RealEstateProperties Is Nothing OrElse Me._RealEstateProperties.Count <> Me.RealEstatePropertyIDs.Count Then
                        Dim Collateral As New List(Of RealEstateCollateral)

                        For Each CollateralID As Integer In Me.RealEstatePropertyIDs
                            If CollateralID > 0 Then
                                Collateral.Add(New RealEstateCollateral(CollateralID))
                            End If
                        Next CollateralID

                        If Collateral.Count > 0 Then
                            If Not Me._RealEstateProperties Is Nothing Then
                                Me._RealEstateProperties.Clear()
                            Else
                                Me._RealEstateProperties = New List(Of RealEstateCollateral)
                                Me._RealEstateProperties.Clear()
                            End If

                            Me._RealEstateProperties = Collateral
                        Else
                            Me._RealEstateProperties = Nothing
                        End If
                    Else
                        Dim Reload As Boolean = False

                        For Each REProperty As RealEstateCollateral In Me._RealEstateProperties
                            If Not Me.RealEstatePropertyIDs.Contains(REProperty.CollateralID) Then
                                Reload = True
                                Exit For
                            End If
                        Next REProperty

                        If Reload Then
                            Dim Collateral As New List(Of RealEstateCollateral)

                            For Each CollateralID As Integer In Me.RealEstatePropertyIDs
                                If CollateralID > 0 Then
                                    Collateral.Add(New RealEstateCollateral(CollateralID))
                                End If
                            Next CollateralID

                            If Collateral.Count > 0 Then
                                If Not Me._RealEstateProperties Is Nothing Then
                                    Me._RealEstateProperties.Clear()
                                Else
                                    Me._RealEstateProperties = New List(Of RealEstateCollateral)
                                    Me._RealEstateProperties.Clear()
                                End If

                                Me._RealEstateProperties = Collateral
                            Else
                                Me._RealEstateProperties = Nothing
                            End If
                        End If
                    End If
                Else
                    Me._RealEstateProperties = Nothing
                End If

                Return Me._RealEstateProperties
            End Get

            Set(value As List(Of RealEstateCollateral))
                If Not value Is Nothing AndAlso value.Count > 0 Then
                    If Me.RealEstatePropertyIDs Is Nothing Then
                        Me.RealEstatePropertyIDs = New List(Of Integer)
                    End If

                    Me.RealEstatePropertyIDs.Clear()

                    For Each valueCollateral As RealEstateCollateral In value
                        Me.RealEstatePropertyIDs.Add(valueCollateral.CollateralID)
                    Next valueCollateral

                    Me._RealEstateProperties = value
                Else
                    Me._RealEstateProperties = Nothing
                End If
            End Set
        End Property
#End Region

#Region "PUBLIC METHODS"
#Region "CONSTRUCTORS"
        ''' <summary>
        ''' Creates a new instance of a Loan object with default values
        ''' </summary>
        Public Sub New()
            Me.Initialize()
        End Sub

        ''' <summary>
        ''' Creates a new instance of a Loan object based on the internal ID assigned to the database record
        ''' </summary>
        ''' <param name="NewLoanID">Database-assigned identification number for a specific Loan record</param>
        Public Sub New(ByVal NewLoanID As Integer)
            Me.Initialize()
            Me.GetLoanDetail(NewLoanID)
        End Sub
#End Region

        Public Shared Function FindExistingLoan(ByVal LoanNumber As String, ByVal LenderID As Integer) As LoanDetail.Loan
            Dim FoundLoan As New LoanDetail.Loan

            FoundLoan.GetLoanDetail(LoanNumber, LenderID)

            Return FoundLoan
        End Function
#End Region

#Region "PRIVATE METHODS"
        ''' <summary>
        ''' Sets all of the values to default for a new instance of a Loan object
        ''' </summary>
        Private Sub Initialize()
            Me.LoanID = 0
            Me.LenderID = 0
            Me.LoanNumber = String.Empty
            Me.InceptionDate = Nothing
            Me.MaturityDate = Nothing
            Me.CurrentBalance = 0
            Me.CreditLimit = 0
            Me.InterestRate = 0
            Me.PaymentFrequency = 12
            Me.CurrentPaymentAmount = 0
            Me.PaidDate = Nothing

            Me.BorrowerIDs = New List(Of Integer)
            Me.VehicleIDs = New List(Of Integer)
            Me.RealEstatePropertyIDs = New List(Of Integer)

            Me._Lender = Nothing
            Me._Borrowers = Nothing
            Me._Vehicles = Nothing
            Me._RealEstateProperties = Nothing
        End Sub

        ''' <summary>
        ''' Populates all of the available values for the Loan object from the database record
        ''' </summary>
        ''' <param name="DBData">DataRow containing the details for the specified Loan record from the database</param>
        Private Sub Fill(ByRef DBData As DataRow)
            With DBData
                Me.LoanID = Convert.ToInt32(.Item("LoanID"))
                Me.LenderID = Convert.ToInt32(.Item("LenderID"))
                Me.LoanNumber = Convert.ToString(.Item("LoanNumber"))

                If Not IsDBNull(.Item("InceptionDate")) Then
                    Me.InceptionDate = Convert.ToDateTime(.Item("InceptionDate"))
                End If

                If Not IsDBNull(.Item("MaturityDate")) Then
                    Me.MaturityDate = Convert.ToDateTime(.Item("MaturityDate"))
                End If

                If Not IsDBNull(.Item("CurrentBalance")) Then
                    Me.CurrentBalance = Convert.ToDecimal(.Item("CurrentBalance"))
                End If

                If Not IsDBNull(.Item("CreditLimit")) Then
                    Me.CreditLimit = Convert.ToDecimal(.Item("CreditLimit"))
                End If

                If Not IsDBNull(.Item("InterestRate")) Then
                    Me.InterestRate = Convert.ToDecimal(.Item("InterestRate"))
                End If

                If Not IsDBNull(.Item("PaymentFrequency")) Then
                    Me.PaymentFrequency = Convert.ToInt32(.Item("PaymentFrequency"))
                End If

                If Not IsDBNull(.Item("CurrentPaymentAmount")) Then
                    Me.CurrentPaymentAmount = Convert.ToDecimal(.Item("CurrentPaymentAmount"))
                End If

                If Not IsDBNull(.Item("PaidDate")) Then
                    Me.PaidDate = Convert.ToDateTime(.Item("PaidDate"))
                End If
            End With

            Me.BorrowerIDs = GetLoanBorrowerIDs()
            Me.VehicleIDs = GetLoanVehicleIDs()
            Me.RealEstatePropertyIDs = GetLoanRealEstateIDs()
        End Sub

        ''' <summary>
        ''' Gets the details of a Loan record based on the internal ID assigned in the database
        ''' </summary>
        ''' <param name="CurrentLoanID">Database-assigned identification number for a specific Loan record</param>
        Private Sub GetLoanDetail(ByVal CurrentLoanID As Integer)
            Dim MyDB As New SQLDB(DBServer)
            Dim DBData As DataTable

            MyDB.Parameters.Add("requestloanid", CurrentLoanID)
            DBData = MyDB.RunProc("""Loan"".""SelectLoan""", "LoanData")

            If DBData.Rows.Count = 1 Then
                Me.Fill(DBData.Rows(0))
            ElseIf DBData.Rows.Count > 1 Then
                Throw New Exception("More than one record was found with the specified identifier.")
            End If

            MyDB.Dispose()
            DBData.Dispose()
        End Sub

        Private Function GetLoanBorrowerIDs() As List(Of Integer)
            Dim LoanBorrowers As New List(Of Integer)

            If Me.LoanID > 0 Then
                Dim MyDB As New SQLDB(DBServer)
                Dim DBData As DataTable

                MyDB.Parameters.Add("requestloanid", Me.LoanID)
                DBData = MyDB.RunProc("""Loan"".""SelectLoanBorrower""", "LoanBorrowers")

                If DBData.Rows.Count > 0 Then
                    For Each LoanBorrower As DataRow In DBData.Rows
                        LoanBorrowers.Add(Convert.ToInt32(LoanBorrower("borrowerid")))
                    Next LoanBorrower
                End If

                MyDB.Dispose()
                DBData.Dispose()
            End If

            Return LoanBorrowers
        End Function

        ''' <summary>
        ''' Retrieves details about vehicles related to a specific loan as identified by the database-assigned identification number
        ''' </summary>
        ''' <returns></returns>
        Private Function GetLoanVehicleIDs() As List(Of Integer)
            Dim LoanVehicles As New List(Of Integer)

            If Me.LoanID > 0 Then
                Dim MyDB As New SQLDB(DBServer)
                Dim DBData As DataTable

                MyDB.Parameters.Add("requestloanid", Me.LoanID)
                DBData = MyDB.RunProc("""Loan"".""SelectLoanVehicle""", "LoanVehicles")

                If DBData.Rows.Count > 0 Then
                    For Each LoanVehicle As DataRow In DBData.Rows
                        LoanVehicles.Add(Convert.ToInt32(LoanVehicle("vehicleid")))
                    Next LoanVehicle
                End If

                MyDB.Dispose()
                DBData.Dispose()
            End If

            Return LoanVehicles
        End Function

        ''' <summary>
        ''' Retrieves details about real estate properties related to a specific loan as identified by the database-assigned identification number
        ''' </summary>
        ''' <returns></returns>
        Private Function GetLoanRealEstateIDs() As List(Of Integer)
            Dim LoanProperties As New List(Of Integer)

            If Me.LoanID > 0 Then
                Dim MyDB As New SQLDB(DBServer)
                Dim DBData As DataTable

                MyDB.Parameters.Add("requestloanid", Me.LoanID)
                DBData = MyDB.RunProc("""Loan"".""SelectLoanRealEstate""", "LoanRealEstate")

                If DBData.Rows.Count > 0 Then
                    For Each LoanProperty As DataRow In DBData.Rows
                        LoanProperties.Add(Convert.ToInt32(LoanProperty("realestateid")))
                    Next LoanProperty
                End If

                MyDB.Dispose()
                DBData.Dispose()
            End If

            Return LoanProperties
        End Function
#End Region
    End Class
#End Region

Примечание: я использовал EditorBrowsable и DebuggerBrowsable атрибуты, чтобы предотвратить некоторые свойства появляться в Visual студии IntelliSense, чтобы избежать путаницы при использовании Loan объект. Я тоже недавно добавили DebuggerBrowsableState.Never параметр для большинства из моего класса свойства, чтобы предотвратить их от попыток загрузки (и, таким образом, делая один из тех ненужных обращений к базе данных), когда я отладки программы. У них были некоторые неожиданные результаты, в то время как я был отладки, и я, честно говоря, не могу придумать лучшего способа, чтобы справиться с ними, чем просто "спрятать", если явно не назовешь. Как я уже говорил выше, на данный момент вроде бы все работает, как задумано, но я просто не могу помочь чувство, что я "пересолил" или, по крайней мере, могли бы делать это лучше.

Кроме того, я удалила ряд методов (в Insert, Update и Delete методов, к примеру) из кода ниже, как они на самом деле не относящиеся к специфике данного вопроса. Спасибо за ваше время.

Полное раскрытие: я полностью самоучка программист. Этот код на самом деле возник из-за нескольких комментариев, которые я получила на вопрос, который я задал на сайте StackOverflow сайта "VB.NET Как избежать бесконечной рекурсии в объект населения".



Комментарии
1 ответ

Общие


  • Почему бы вам не использовать Lazy<T> который, кажется, идеально подходит для использования своим любимым напитком.

  • Регионах, регионах, как правило, признак того, что класс может быть слишком большой. Если вам нужна регионов структурировать код, то вам придется много кода в этом классе, и вы должны рассмотреть, чтобы разделить класс на 2 или более классов.

    По крайней мере описание региона не должны лгать о его содержании. Private LenderID As Integer в области имени PRIVATE PROPERTIES но явно переменной.


  • Хотя VB.NET не зависит от регистра, вы должны рассмотреть возможность применить .Чистая именования ориентиры , потому что читатели вашего кода будет обычно ожидают этого, и это сделает его легче для них, чтобы прочитать и понять ваш код.

    Например, методы параметров с использованием camelCase корпус, classlevel переменных либо с использованием camelCase корпус или подчеркивания префикс. Если вы используете подчеркивание префикс variablenames вы не должны использовать их Me описатель вместе. В общем, вы должны использовать Me только если нужно четко различать между собой на уровне класса переменную и электронной.г параметр метода, который имеет то же имя.


  • Вы должны извлечь магических строк/чисел в осмысленные именованных констант. Предположим, что вы измените имя requestloanid параметр, было бы гораздо проще, чтобы изменить это только в одном месте в коде. Прямо сейчас вы должны изменить его как минимум в 4 местах в коде.


Собственность Кредитора

Если значение этого свойства Nothing а NullReferenceException будет брошен. Хотя, что может быть приемлемо было бы лучше бросить ArgumentNullException. ИМО параметров public методы/свойства должны всегда быть проверены, по крайней мере, для Nothing.

Свойства Заемщиков, Средствами, RealEstateProperties,

У вас есть много дублирующегося кода в добытчики этих свойств, которые должны быть извлечены отдельные методы. Кроме того, вам не нужно Clear() а New List<T>.

Вы могли бы думать о ли вы хотите установить свойство Nothingпо крайней мере для геттер List<T> свойства. Если нет каких-либо элементов в List<T> почему бы вам не оставить их как они есть? Это может привести к не необходимости проверять Nothing от вызывающей стороны собственность.

Вызов конструктора из перегруженного конструктора больше .Чистый, как и намерен называть более понятно.

Как так

Public Sub New()
Me.Initialize()
End Sub

Public Sub New(ByVal NewLoanID As Integer)
Me.New()
Me.GetLoanDetail(NewLoanID)
End Sub

Но читая Me.GetLoanDetail(NewLoanID) мне стало интересно, что GetXX() метод будет делать, если вернулся не назначено ничего. Это явный признак того, что вы должны работать на вашем именования. ИМО перегрузки Fill() способ будет лучшим способом. Не заставляй сопровождающих ваш код интересно, что он делает. Предположим вам нужно добавить функции или исправить ошибки в 3 или 6 месяцев, вы бы понять это на первый взгляд, что вызов о, если бы вы читали Me.Fill(NewLoanID).

GetLoanDetail(), GetLoanBorrowerIDs(), GetLoanVehicleIDs()

Вместо того, чтобы вручную удалив соединение и объект DataTable(который вы не должны распоряжаться кстати), вы должны использовать Using заявление.

GetLoanBorrowerIDs(), GetLoanVehicleIDs()

Путем возврата if состояние If Me.LoanID > 0 Then вы могли бы вернуться раньше и сэкономили бы на один уровень отступа. Предполагая, что LoanID не быть отрицательным, это будет выглядеть так

Private Function GetLoanVehicleIDs() As List(Of Integer)
Dim LoanVehicles As New List(Of Integer)

If Me.LoanID = 0 Then
Return LoanVehicles
End If

Dim MyDB As New SQLDB(DBServer)
Dim DBData As DataTable

MyDB.Parameters.Add("requestloanid", Me.LoanID)
DBData = MyDB.RunProc("""Loan"".""SelectLoanVehicle""", "LoanVehicles")

If DBData.Rows.Count > 0 Then
For Each LoanVehicle As DataRow In DBData.Rows
LoanVehicles.Add(Convert.ToInt32(LoanVehicle("vehicleid")))
Next LoanVehicle
End If

MyDB.Dispose()
DBData.Dispose()

Return LoanVehicles
End Function

1
ответ дан 5 февраля 2018 в 07:02 Источник Поделиться