Dataset in Cache: Collection was modified; enumeration operation might not execute

| | August 8, 2015

I’m storing a dataset in an ASP.Net WebApplication-Cache. Every user in this intranet-app uses the same instance. On insert/update/delete-actions the database will be updated and the dataset is modified accordingly.

But rarely I get an exception that indicates that I’ve missed something. I assume that it must have something to do with thread safety.

 Collection was modified; enumeration operation might not execute

In lines where i access a DataTable in the Dataset, for example:

Dim view As New DataView(dsERP.ERP_Charge, filter, sort, _
    Data.DataViewRowState.CurrentRows)

It was apparently changed by another thread while the view enumerates the datatable.

What is the best way to make this thread safe?

Edit: as you’ve mentioned i need to lock the objects on add/edit/delete operations.
MSDN* says that a DataSet is thread-safe for multiple users. What does this mean, are the DataTables in the Dataset also thread-safe? And how to lock a single datatable on write-operations and not the whole dataset?

*ADO.NET – Multithreaded Programming

ADO.NET is optimized for performance, throughput, and scalability. As a result, the
ADO.NET objects do not lock resources and must only be used on a single thread. The one
exception is the DataSet, which is thread-safe for multiple readers. However, you need
lock the DataSet during writes.

This is the property that returns the dataset:

Public ReadOnly Property dsERP() As ERPModel.dsERP
    Get
        If Cache("DS_ERP") Is Nothing Then
            Cache("DS_ERP") = New ERPModel.dsERP
            FillDataSet()
        End If
        Return DirectCast(Cache("DS_ERP"), ERPModel.dsERP)
    End Get
End Property

Thanks to casperOne i’ve modified the insert/update and delete operations in the following way(dsRma is a dataset):

Dim success As Boolean
SyncLock dsRMA.RMA
     success = insert()
End SyncLock

First, does it work now if another thread tries to enumerate the RMA-Table? Second, is it sufficient to lock the datarow that gets updated instead of locking the whole datatable(see below)?

Dim thisRMA As ERPModel.dsRMA.RMARow = dsRMA.RMA.FindByIdRMA(Me.IdRma)
Dim success As Boolean
SyncLock thisRMA
       success = update(thisRMA)
End SyncLock

One Response to “Dataset in Cache: Collection was modified; enumeration operation might not execute”

  1. Generally, when enumerating over a sequence, you cannot perform operations that will modify the sequence.

    Because youa re storing the collection in the WebApplication cache, it can be hit by multiple threads at the same time; you might have a request on one thread enter an item in the cache while another is trying to enumerate over it.

    To combat this, you really should make access to the data structure thread-safe.

    You have a few options for this. The first, and easiest, would be to encapsulate access to the sequence in a lock block. You have to wrap add/edit/delete operations in individual blocks, and you also have target the same object in a lock block over the enumeration of the entire sequence.

    This can easily be encapsulated in another class and then you store that in your cache.

    If you are also performing a high number of read operations compared to write operations, then you can use the ReaderWriterLockSlim class to lock reads and writes appropriately; the strategy above will lock two concurrent reads out.

    Another solution would be to lock around any add/edit/update operation on the cache, and when you want to enumerate the cache, lock and create a copy of the sequence itself and return that; once you return the copy of the sequence, you can enumerate that separately as that sequence is different from the one you are actually modifying.

Leave a Reply