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 に答える