0

我有一个Module我想用来缓存一些东西的东西。这很简单。我想回避,ConcurrentDictionary因为它需要有保证的操作。

Public Module SchemaTableCache
    Private lockObject As New Object
    Private columnCache As New Dictionary(Of String, SortedSet(Of String))

    <Extension>
    Public Sub CacheSchemaTable(dataReader As IDataReader, name As String)
        SyncLock lockObject
            Dim rows As New List(Of DataRow)
            If columnCache.ContainsKey(name) Then
                Return
            End If

            rows = dataReader.GetSchemaTable().Rows.OfType(Of DataRow)().ToList()
            columnCache.Add(name, New SortedSet(Of String)(rows.Select(Function(r) r.Field(Of String)("ColumnName"))))
        End SyncLock
    End Sub

    <Extension>
    Public Function HasColumn(name As String, column As String) As Boolean
        SyncLock lockObject
            Dim cols As New SortedSet(Of String)
            If Not columnCache.TryGetValue(name, cols) Then
                Return False
            End If

            Return cols.Contains(column)
        End SyncLock
    End Function
End Module

事情就是这样。我有一些单元测试来测试利用该HasColumn功能的代码。我像这样设置这些测试:

dataReader.Setup(Function(x) x(field)).Returns(val)

' setup the schema table
Dim table As New DataTable()
table.Columns.Add("ColumnName", GetType(String))
If setupTable Then
    table.Rows.Add(field)
End If
dataReader.Setup(Function(x) x.GetSchemaTable()) _
    .Returns(table)

dataReader.Object.CacheSchemaTable("table")

然后他们测试这个功能:

Dim typeName = GetType(T).Name
Debug.WriteLine($"IDataReader_Value({schemaTableName}.{column})")

If Not schemaTableName.HasColumn(column) Then
    Debug.WriteLine($"Could not find column {column}; returning default value.")
    Return typeName.DefaultValue()
End If

Dim input = dr(column)
Debug.WriteLine($"Found column {column}; returning value {input}.")
Return Value(Of T)(input)

你可以在这里看到我点击该HasColumn方法的地方。事情就是这样。如果我单独执行这些测试,它们会成功;但是,如果我执行整个测试集,它们就会失败。

显然这里存在线程安全问题,但我无法终生弄清楚我做错了什么。有人可以帮我看看我哪里出错了吗?

测试失败时的输出是:

Test Name:  IDataReader_ValueBoolean
Test Outcome:   Failed
Result Message: Assert.AreEqual failed. Expected:<True>. Actual:<False>.
Result StandardOutput:  
Debug Trace:
IDataReader_Value(table.field)
Could not find column field; returning default value.

测试成功时的输出是:

Test Name:  IDataReader_ValueBoolean
Test Outcome:   Passed
Result StandardOutput:  
Debug Trace:
IDataReader_Value(table.field)
Found column field; returning value True.
4

1 回答 1

1

我想到了。问题不在于SyncLock,而在于我的逻辑。每个测试都遇到不同的问题。有些人正在测试缺少的列,而有些人则期望它存在。因此,我需要能够更新缓存。

这是新的逻辑:

SyncLock lockObject
    Debug.WriteLine($"Caching schema table {name}.")
    Dim rows As New List(Of DataRow)
    If Not columnCache.ContainsKey(name) Then
        Debug.WriteLine($"Adding cache key for {name}.")
        columnCache.Add(name, New SortedSet(Of String)())
    End If

    rows = dataReader.GetSchemaTable().Rows.OfType(Of DataRow)().ToList()
    Debug.WriteLine($"Schema table rows count: {rows.Count}")
    columnCache(name) = New SortedSet(Of String)(rows.Select(Function(r) r.Field(Of String)("ColumnName")))
    Debug.WriteLine($"Successfully cached {name}.")
End SyncLock
于 2016-11-17T06:41:26.167 回答