Collection 列表自行清空

Collection List empties itself

我有一个 class 用于向继承 System.Collections.CollectionBase 的数据库 (PostgreSQL) 连接 object 添加参数。这个 class 在我用于多个应用程序的公共库中。通常,它会毫无问题地添加 objects,但我开始遇到一个奇怪的问题,即 collection 无缘无故清空其 objects。这会导致 SQL 语句未正确构建的错误。


环境


疑难解答

这是发生问题的(混淆)代码:

SQLCommand = "INSERT INTO table" & vbCrLf
SQLCommand += "(" & vbCrLf
SQLCommand += vbTab & "column1," & vbCrLf
SQLCommand += vbTab & "column2," & vbCrLf
SQLCommand += vbTab & "column3," & vbCrLf
SQLCommand += vbTab & "column4," & vbCrLf
SQLCommand += vbTab & "column5," & vbCrLf
SQLCommand += vbTab & "column6," & vbCrLf
SQLCommand += vbTab & "column7," & vbCrLf
SQLCommand += vbTab & "column8," & vbCrLf
SQLCommand += vbTab & "column9," & vbCrLf
SQLCommand += vbTab & "column10" & vbCrLf
SQLCommand += ")" & vbCrLf
SQLCommand += "VALUES" & vbCrLf
SQLCommand += "(" & vbCrLf
SQLCommand += vbTab & ":parameter1," & vbCrLf
SQLCommand += vbTab & ":parameter2," & vbCrLf
SQLCommand += vbTab & ":parameter3," & vbCrLf
SQLCommand += vbTab & ":parameter4," & vbCrLf
SQLCommand += vbTab & ":parameter5," & vbCrLf
SQLCommand += vbTab & ":parameter6," & vbCrLf
SQLCommand += vbTab & ":parameter7," & vbCrLf
SQLCommand += vbTab & ":parameter8," & vbCrLf
SQLCommand += vbTab & ":parameter9," & vbCrLf
SQLCommand += vbTab & ":parameter10" & vbCrLf
SQLCommand += ")" & vbCrLf
SQLCommand += "RETURNING pkid"

PGDB.Parameters.Add(":parameter1", value1) 'String
PGDB.Parameters.Add(":parameter2", value2) 'String
PGDB.Parameters.Add(":parameter3", value3) 'String
PGDB.Parameters.Add(":parameter4", value4) 'String
PGDB.Parameters.Add(":parameter5", value5) 'String
PGDB.Parameters.Add(":parameter6", value6) 'String
PGDB.Parameters.Add(":parameter7", value7) 'String - THIS IS WHERE THE LIST CLEARS
PGDB.Parameters.Add(":parameter8", value8) 'Boolean
PGDB.Parameters.Add(":parameter9", value9) 'String
PGDB.Parameters.Add(":parameter10", value10) 'String

MyID = Convert.ToInt32(PGDB.ExecuteStatementScalar(SQLCommand))

PGDB object 是我的 Npgsql 数据库连接 object 而 Parameters object 是继承的 collection .前六个参数添加到 collection 没有问题,但一旦它添加第七个,整个列表就会清空并重新开始。正在执行的 SQL 语句 应该 如下所示:

-- EXPECTED SQL - WHAT IS STORED IN THE SQLCommand VARIABLE
INSERT INTO table
(
    column1,
    column2,
    column3,
    column4,
    column5,
    column6,
    column7,
    column8,
    column9,
    column10
)
VALUES
(
    :parameter1,
    :parameter2,
    :parameter3,
    :parameter4,
    :parameter5,
    :parameter6,
    :parameter7,
    :parameter8,
    :parameter9,
    :parameter10
)
RETURNING pkid

...但是,我得到的是:

-- ACTUAL (WRONG) SQL EXECUTED BY THE DATABASE
INSERT INTO table
(
    column1,
    column2,
    column3,
    column4,
    column5,
    column6,
    column7,
    column8,
    column9,
    column10
)
VALUES
(
    :parameter1,
    :parameter2,
    :parameter3,
    :parameter4,
    :parameter5,
    :parameter6,
    ,
    ,
    ,
    
)
RETURNING pkid

...然后在实际尝试执行 SQL 语句时生成 Npgsql.PostgresException 抱怨语法:

42601: syntax error at or near ":"

我已经设置了断点来逐步完成该过程,但它始终是相同的行为。这是我的 IDE:

的一些截图

这是添加第一个参数之前 Parameters collection object 的样子:

这是添加第六个参数后的样子:

这是它在第 7 个参数上执行 Add() 方法后的样子:

可以肯定的是,我还检查了它进入第 6 个参数执行 Add() 方法时的状态:

这个方法曾经一次没有错误地工作过,所以我不确定为什么它“突然”停止工作了。为了修复此问题并让所有参数正确加载,我将 Npgsql 库从版本 4.1.8 升级到 5.0.3。在我与它战斗了一段时间后 - 我不得不解决与 System.Buffers 的一些版本冲突 - 我能够再次获得它 运行 但是,不幸的是,我得到了完全相同的结果。

以防万一这可能是内存问题,我继续关闭所有内容并重新启动计算机。那也没有解决问题。

作为参考,这里是 PGSQLParameters class 的 trimmed-down 版本,不包括不同值类型的各种重载。

Imports Npgsql

#Region "POSTGRESQL PARAMETER COLLECTION OBJECT"
Public Class PGSQLParameters
    Inherits System.Collections.CollectionBase

    Public Enum SQLDecimalType
        SQLMoney = 1
        SQLDecimal = 2
    End Enum

#Region "COLLECTION ADD AND SUPPORT METHODS"
#Region "PUBLIC METHODS FOR ADDING ITEMS TO THE COLLECTION"
    '-- Input Parameter
    Public Overloads Sub Add(ByVal ParameterName As String, ByVal DataType As DbType, ByVal Size As Int32, ByVal Value As Object)
        If ParameterName.Length = 0 Then
            Return
        End If

        If TypeOf (Value) Is String AndAlso String.IsNullOrEmpty(Convert.ToString(Value)) Then
            Value = DBNull.Value
        End If

        If Not ParameterName.StartsWith(":") Then
            ParameterName = ParameterName.Insert(0, ":")
        End If

        List.Add(BuildParameter(ParameterName, DataType, Size, ParameterDirection.Input, Value))
    End Sub

#Region "INPUT OVERLOADS"
    '-- String
    Public Overloads Sub Add(ByVal ParameterName As String, ByVal Value As String)
        Dim StringLength As Integer = 0

        If Not Value = Nothing AndAlso Not String.IsNullOrEmpty(Value) Then
            StringLength = Value.Length
        End If

        Add(ParameterName, DbType.String, StringLength, Value)
    End Sub
#End Region
#End Region

    Private Function BuildParameter(ByVal ParameterName As String, ByVal DataType As DbType, ByVal Size As Int32, ByVal Direction As ParameterDirection, ByVal Value As Object) As NpgsqlParameter
        Dim NewParameter As NpgsqlParameter

        If Size > 0 Then
            NewParameter = New NpgsqlParameter(ParameterName, DataType, Size)
        Else
            NewParameter = New NpgsqlParameter(ParameterName, DataType)
        End If

        NewParameter.Direction = Direction

        If Not (Direction = ParameterDirection.Output AndAlso Value Is Nothing) Then
            NewParameter.Value = DBNull.Value

            If Not Value Is Nothing Then
                NewParameter.Value = Value
            ElseIf TypeOf (Value) Is Boolean Then
                NewParameter.Value = Value
            End If
        End If

        Return NewParameter
    End Function
#End Region

End Class
#End Region

我看不出 Parameters collection 自发“清除”的任何原因,特别是因为导致并包括“问题”的所有参数都是 String 值,所以它们都使用完全相同的方法调用(我已经在我的 walk-through 中确认了这一点)。此外,根据执行 SQL,它 看起来 就像它实际上以某种方式 保留 collection 中的前六个参数object 只是不添加新的,这完全没有意义。

此外,它看起来不像是完全重新实例化 object 因为,如屏幕截图所示,Capacity 从第六个参数到第七个参数没有变化。如果我不得不说什么,它看起来像是在添加六个参数后克隆 collection,然后丢弃那个克隆 and/or 完全忽略它。同样,这对我来说毫无意义。

我不怀疑我只是忽略了一些东西,但我不知道那个“东西”是什么。还有其他人遇到过这种行为吗?任何帮助或想法将不胜感激。如果我可以或需要提供任何其他详细信息,请告诉我。

可能需要 post 更多代码 - posted 代码对我来说似乎没有任何问题:

将此图标拖到您的桌面,将其重命名为 .zip 并打开它:

它有我用来检查你的问题的确切解决方案文件(它没有在我的计算机上显示问题)

我已经设法解决了这个问题,但原因是我没有预料到的,如果没有 complete 的特价商品清单,原因可能不会很明显更多代码。很抱歉没有在原始问题中提供足够的细节,但我没有猜到我最终会在哪里找到问题的原因。

正如我上面所指出的,问题总是出现在序列中的相同参数上。我试图分配给该特定参数的值是另一个 class.[=26= 中的 Lazy(Of T) 对象的 Public 属性 (String) ]

有问题的对象看起来像这样:

#Region "REAL ESTATE OBJECT"
''' <summary>
''' Standard object containing details about a specific piece of real estate
''' </summary>
Public Class RealEstate
#Region "PRIVATE FIELDS"
    <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
    Private _RealEstateTypeCode As String

    <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
    Private _PhysicalStreet1 As String

    <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
    Private _PhysicalStreet2 As String

    <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
    Private _PhysicalCity As String

    <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
    Private _PhysicalState As String

    <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
    Private _PhysicalZIPCode As String
#End Region

#Region "PRIVATE PROPERTIES"
    <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
    Private Property PGDB As PGSQLDB

#Region "LAZY PROPERTIES"
    ' >> THIS IS THE PROPERTY THAT APPEARS TO HAVE CAUSED THE ISSUE <<
    <EditorBrowsable(EditorBrowsableState.Never)> <DebuggerBrowsable(DebuggerBrowsableState.Never)>
    Private Property _RealEstateType As Lazy(Of PropertyType) =
    New Lazy(Of PropertyType)(Function()
                                    Return New PropertyType(_RealEstateTypeCode, Me.CIADB)
                                End Function)
#End Region
#End Region

#Region "PUBLIC PROPERTIES"
    Public Property RealEstateID As Integer

    Public Property PhysicalStreet1 As String
        Get
            Return _PhysicalStreet1
        End Get

        Set(value As String)
            If Not value Is Nothing Then
                If value.Trim.Length > 120 Then
                    _PhysicalStreet1 = value.Trim.Substring(0, 120).Trim.ToUpper
                Else
                    _PhysicalStreet1 = value.Trim.ToUpper
                End If
            Else
                _PhysicalStreet1 = String.Empty
            End If
        End Set
    End Property

    Public Property PhysicalStreet2 As String
        Get
            Return _PhysicalStreet2
        End Get

        Set(value As String)
            If Not value Is Nothing Then
                If value.Trim.Length > 120 Then
                    _PhysicalStreet2 = value.Trim.Substring(0, 120).Trim.ToUpper
                Else
                    _PhysicalStreet2 = value.Trim.ToUpper
                End If
            Else
                _PhysicalStreet2 = String.Empty
            End If
        End Set
    End Property

    Public Property PhysicalCity As String
        Get
            Return _PhysicalCity
        End Get

        Set(value As String)
            If Not value Is Nothing Then
                If value.Trim.Length > 60 Then
                    _PhysicalCity = value.Trim.Substring(0, 60).Trim.ToUpper
                Else
                    _PhysicalCity = value.Trim.ToUpper
                End If
            Else
                _PhysicalCity = String.Empty
            End If
        End Set
    End Property

    Public Property PhysicalState As String
        Get
            If _PhysicalState Is Nothing OrElse String.IsNullOrEmpty(_PhysicalState) OrElse _PhysicalState = "XX" Then
                If Not _PhysicalZIPCode Is Nothing AndAlso Not String.IsNullOrEmpty(_PhysicalZIPCode) Then
                    _PhysicalState = Utility.GetStateCodeFromZIP(_PhysicalZIPCode)
                End If
            End If

            Return _PhysicalState
        End Get

        Set(value As String)
            If Not value Is Nothing Then
                If value.Trim.Length > 2 Then
                    _PhysicalState = Utility.GetStateCodeFromName(value.Trim)
                Else
                    If [Enum].IsDefined(GetType(USState), value.Trim.ToUpper) Then
                        Dim PState As USState

                        If [Enum].TryParse(value.Trim.ToUpper, PState) Then
                            _PhysicalState = Utility.GetStateCodeFromName(PState.GetEnumDescription)
                        End If
                    Else
                        _PhysicalState = value.Trim.ToUpper
                    End If
                End If
            Else
                _PhysicalState = "OK"
            End If
        End Set
    End Property

    Public Property PhysicalZIPCode As String
        Get
            Return _PhysicalZIPCode
        End Get

        Set(value As String)
            If Not value Is Nothing Then
                If value.Trim.Length > 10 Then
                    _PhysicalZIPCode = value.Trim.Substring(0, 10).Trim.ToUpper
                Else
                    _PhysicalZIPCode = value.Trim.ToUpper
                End If
            Else
                _PhysicalZIPCode = String.Empty
            End If
        End Set
    End Property

    Public WriteOnly Property RealEstateTypeCode As String
        Set(value As String)
            _RealEstateTypeCode = value
        End Set
    End Property

    Public ReadOnly Property RealEstateType As PropertyType
        Get
            Return _RealEstateType.Value
        End Get
    End Property
#End Region

在我最初的混淆代码中,我只是注入了一个通用字符串作为占位符,但问题似乎与这个特定的分配有关。添加到参数列表的其他值是可立即访问的对象的“常规”属性。但是,当使用 Lazy(Of T) 对象作为 Value 参数调用 Add() 方法时,某些东西似乎“短路”了。

为了“解决”这个问题,我声明了一个额外的本地 String 变量,它从这个 Lazy(Of T) 对象中检索所需 属性 的值。然后我使用 局部变量 分配给 Add() 方法的 Value 参数:

With REObject 'A RealEstate object
    ' > DECLARING THIS LOCAL VARIABLE TO RETRIEVE THE VALUE OF THE LAZY PROPERTY
    Dim TypeCode As String = .RealEstateType.TypeCode
...
    PGDB.Parameters.Add("street1", .PhysicalStreet1)
    PGDB.Parameters.Add("street2", .PhysicalStreet2)
    PGDB.Parameters.Add("city", .PhysicalCity)
    PGDB.Parameters.Add("state", .PhysicalState)
    PGDB.Parameters.Add("zip", .PhysicalZIPCode)
    ' > CHANGED THE FOLLOWING
    ' PGDB.Parameters.Add("type", .RealEstateType.TypeCode) 'String - THIS IS WHERE THE LIST CLEARS
    ' > TO USE THE LOCAL VARIABLE INSTEAD OF THE LAZY OBJECT'S PROPERTY VALUE
    PGDB.Parameters.Add("type", TypeCode)
    ' > THE VALUE IS NOW SUCCESSFULLY ADDED
...
End With

执行代码会导致所有参数正确填充正确的值,并且SQL语句在数据库中执行而不会出错。因此,似乎 Lazy(Of T) 对象尚未被“填充”这一事实在进入 Add()方法。老实说,我仍然不确定为什么在尝试使用Lazy(Of T) 对象的 属性 值,但至少这个分辨率达到了预期的目标。再次,对于最初没有提供足够的细节,我深表歉意,但希望这会对其他人有所帮助。