2016-11-17 69 views
0

我有一個Module,我想用來緩存一些東西。這很簡單。我想避開ConcurrentDictionary,因爲它需要有保證的操作。SyncLock不能在單元測試中工作

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. 

回答

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