J
Jon Skeet [C# MVP]
We've had a problem we've been trying to reproduce for weeks now. It
only occurs every so often, but we can't work out when, why, or how to
fix it.
We have various forms with CurrencyManagers in. The CurrencyManagers
are bound to DataViews which have been populated from the database. The
database itself is then changed from another thread, and then from the
UI thread we call Fill on the DataAdapter, passing in the DataTable
that the DataView is reflecting. Sometimes, we then get an exception
with the following stack trace (extra line between each stack frame for
clarity):
System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.
_ChangeRecordState(int nPositionNew = -1, bool fValidating = true, bool
fEndCurrentEdit = true, bool fFirePositionChange = true, bool fPullData
= false) + 0x34 bytes
System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.
_List_ListChanged(System.Object sender = {System.Data.DataView},
System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0x335 bytes
System.Data.dll!System.Data.DataView.OnListChanged
(System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs})
System.Data.dll!System.Data.DataView.IndexListChanged
(System.Object sender = {System.Data.Index},
System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0xd bytes
System.Data.dll!System.Data.DataView.FireEvent
(System.Data.TargetEvent targetEvent = IndexListChanged, System.Object
sender = {System.Data.Index}, System.EventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0x26 bytes
System.Data.dll!System.Data.DataViewListener.IndexListChanged
(System.Object sender = {System.Data.Index},
System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0x1d bytes
System.Data.dll!System.Data.Index.OnListChanged
(System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0x1d bytes
System.Data.dll!System.Data.Index.RecordStateChanged(int
oldRecord = 1, System.Data.DataViewRowState oldOldState = Unchanged,
System.Data.DataViewRowState oldNewState = ModifiedOriginal, int
newRecord = 19, System.Data.DataViewRowState newOldState = None,
System.Data.DataViewRowState newNewState = ModifiedCurrent) + 0xdc
bytes
System.Data.dll!System.Data.DataTable.RecordStateChanged(int
record1 = 1, System.Data.DataViewRowState oldState1 = Unchanged,
System.Data.DataViewRowState newState1 = ModifiedOriginal, int record2
= 19, System.Data.DataViewRowState oldState2 = None,
System.Data.DataViewRowState newState2 = ModifiedCurrent) + 0x38 bytes
System.Data.dll!System.Data.DataTable.SetNewRecord
(System.Data.DataRow row = {System.Data.DataRow}, int proposedRecord =
19, System.Data.DataRowAction action = Change, bool isInMerge = false)
+ 0xf1 bytes
System.Data.dll!System.Data.DataTable.LoadDataRow(System.Object[]
values = {Length=36}, bool fAcceptChanges = false) + 0x6f bytes
System.Data.Common.dll!
System.Data.Common.SchemaMapping.LoadDataRow(bool clearDataValues =
false, bool acceptChanges = false) + 0x48 bytes
System.Data.Common.dll!
System.Data.Common.DbDataAdapter.FillLoadDataRow
(System.Data.Common.SchemaMapping mapping =
{System.Data.Common.SchemaMapping}) + 0x50 bytes
System.Data.Common.dll!
System.Data.Common.DbDataAdapter.FillFromReader(System.Object data =
{System.Data.DataTable}, string srcTable = null,
System.Data.IDataReader dataReader =
{System.Data.SqlServerCe.SqlCeDataReader}, int startRecord = 0, int
maxRecords = 0, System.Data.DataColumn parentChapterColumn = <undefined
value>, System.Object parentChapterValue = <undefined value>) + 0x62
bytes
System.Data.Common.dll!System.Data.Common.DbDataAdapter.Fill
(System.Data.DataTable dataTable = {System.Data.DataTable},
System.Data.IDataReader dataReader =
{System.Data.SqlServerCe.SqlCeDataReader}) + 0x3c bytes
System.Data.Common.dll!System.Data.Common.DbDataAdapter.Fill
(System.Object data = {System.Data.DataTable}, int startRecord = 0, int
maxRecords = 0, string srcTable = null, System.Data.IDbCommand command
= {System.Data.SqlServerCe.SqlCeCommand}, System.Data.CommandBehavior
behavior = SingleResult) + 0x4d bytes
System.Data.Common.dll!System.Data.Common.DbDataAdapter.Fill
(System.Data.DataTable dataTable = {System.Data.DataTable},
System.Data.IDbCommand command =
{System.Data.SqlServerCe.SqlCeCommand}, System.Data.CommandBehavior
behavior = SingleResult) + 0x23 bytes
System.Data.Common.dll!System.Data.Common.DbDataAdapter.Fill
(System.Data.DataTable dataTable = {System.Data.DataTable}) + 0x4c
bytes
Now, it seems to me that it's probably a bug in the MS databinding code
anyway, but if we could get rid of the CurrencyManager to start with it
would allow us to work round it. Our current thought is to create a new
DataView for each of the forms, so we can call Dispose on them to
unbind - but that's pretty horrible.
Has anyone else encountered this?
only occurs every so often, but we can't work out when, why, or how to
fix it.
We have various forms with CurrencyManagers in. The CurrencyManagers
are bound to DataViews which have been populated from the database. The
database itself is then changed from another thread, and then from the
UI thread we call Fill on the DataAdapter, passing in the DataTable
that the DataView is reflecting. Sometimes, we then get an exception
with the following stack trace (extra line between each stack frame for
clarity):
System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.
_ChangeRecordState(int nPositionNew = -1, bool fValidating = true, bool
fEndCurrentEdit = true, bool fFirePositionChange = true, bool fPullData
= false) + 0x34 bytes
System.Windows.Forms.dll!System.Windows.Forms.CurrencyManager.
_List_ListChanged(System.Object sender = {System.Data.DataView},
System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0x335 bytes
System.Data.dll!System.Data.DataView.OnListChanged
(System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs})
System.Data.dll!System.Data.DataView.IndexListChanged
(System.Object sender = {System.Data.Index},
System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0xd bytes
System.Data.dll!System.Data.DataView.FireEvent
(System.Data.TargetEvent targetEvent = IndexListChanged, System.Object
sender = {System.Data.Index}, System.EventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0x26 bytes
System.Data.dll!System.Data.DataViewListener.IndexListChanged
(System.Object sender = {System.Data.Index},
System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0x1d bytes
System.Data.dll!System.Data.Index.OnListChanged
(System.ComponentModel.ListChangedEventArgs e =
{System.ComponentModel.ListChangedEventArgs}) + 0x1d bytes
System.Data.dll!System.Data.Index.RecordStateChanged(int
oldRecord = 1, System.Data.DataViewRowState oldOldState = Unchanged,
System.Data.DataViewRowState oldNewState = ModifiedOriginal, int
newRecord = 19, System.Data.DataViewRowState newOldState = None,
System.Data.DataViewRowState newNewState = ModifiedCurrent) + 0xdc
bytes
System.Data.dll!System.Data.DataTable.RecordStateChanged(int
record1 = 1, System.Data.DataViewRowState oldState1 = Unchanged,
System.Data.DataViewRowState newState1 = ModifiedOriginal, int record2
= 19, System.Data.DataViewRowState oldState2 = None,
System.Data.DataViewRowState newState2 = ModifiedCurrent) + 0x38 bytes
System.Data.dll!System.Data.DataTable.SetNewRecord
(System.Data.DataRow row = {System.Data.DataRow}, int proposedRecord =
19, System.Data.DataRowAction action = Change, bool isInMerge = false)
+ 0xf1 bytes
System.Data.dll!System.Data.DataTable.LoadDataRow(System.Object[]
values = {Length=36}, bool fAcceptChanges = false) + 0x6f bytes
System.Data.Common.dll!
System.Data.Common.SchemaMapping.LoadDataRow(bool clearDataValues =
false, bool acceptChanges = false) + 0x48 bytes
System.Data.Common.dll!
System.Data.Common.DbDataAdapter.FillLoadDataRow
(System.Data.Common.SchemaMapping mapping =
{System.Data.Common.SchemaMapping}) + 0x50 bytes
System.Data.Common.dll!
System.Data.Common.DbDataAdapter.FillFromReader(System.Object data =
{System.Data.DataTable}, string srcTable = null,
System.Data.IDataReader dataReader =
{System.Data.SqlServerCe.SqlCeDataReader}, int startRecord = 0, int
maxRecords = 0, System.Data.DataColumn parentChapterColumn = <undefined
value>, System.Object parentChapterValue = <undefined value>) + 0x62
bytes
System.Data.Common.dll!System.Data.Common.DbDataAdapter.Fill
(System.Data.DataTable dataTable = {System.Data.DataTable},
System.Data.IDataReader dataReader =
{System.Data.SqlServerCe.SqlCeDataReader}) + 0x3c bytes
System.Data.Common.dll!System.Data.Common.DbDataAdapter.Fill
(System.Object data = {System.Data.DataTable}, int startRecord = 0, int
maxRecords = 0, string srcTable = null, System.Data.IDbCommand command
= {System.Data.SqlServerCe.SqlCeCommand}, System.Data.CommandBehavior
behavior = SingleResult) + 0x4d bytes
System.Data.Common.dll!System.Data.Common.DbDataAdapter.Fill
(System.Data.DataTable dataTable = {System.Data.DataTable},
System.Data.IDbCommand command =
{System.Data.SqlServerCe.SqlCeCommand}, System.Data.CommandBehavior
behavior = SingleResult) + 0x23 bytes
System.Data.Common.dll!System.Data.Common.DbDataAdapter.Fill
(System.Data.DataTable dataTable = {System.Data.DataTable}) + 0x4c
bytes
Now, it seems to me that it's probably a bug in the MS databinding code
anyway, but if we could get rid of the CurrencyManager to start with it
would allow us to work round it. Our current thought is to create a new
DataView for each of the forms, so we can call Dispose on them to
unbind - but that's pretty horrible.
Has anyone else encountered this?