Как .NET (CF) DataRow самопроизвольно установить все значения в DBNull? - PullRequest
0 голосов
/ 20 октября 2011

Я работаю с .NET DataSets в Compact Framework.Один DataSet содержит четыре или пять таблиц с легким использованием данных (порядка нескольких строк каждая).Таблица первичных данных (на которой основаны все ограничения внешнего ключа) содержит одну строку.DataSet сохраняется на диске в виде XML-файла (с использованием DataSet.WriteXML).

Мы обнаруживаем, что очень часто все значения DataRow устанавливаются в DBNull без видимой причины.Более того, когда данные снова сохраняются, одна строка первичной таблицы данных записывается без данных.

Например, мы ожидаем увидеть:

<MyDataSet>
<MyTypedDataRow id="1" data1="data" data2="more data" />
...

где «data1»и «data2» - столбцы, не допускающие значения NULL.Вместо этого мы редко получаем:

<MyDataSet>
<MyTypedDataRow />
...

, что должно быть невозможным.

В нашей среде используется многопоточность, но доступ к DataSet строго защищен от потоков.

Когда я пытаюсь вызвать это в отладчике, все мои усилия встречаются с ошибками из-за ограничений таблицы.

Есть ли у кого-нибудь мысли о том, как это может происходить?

Спасибо за поиск.


РЕДАКТИРОВАТЬ: Одна вещь, которую я не упомянул, может быть фактором, способствующим.Это строго типизированный DataSet, который был создан с использованием дизайнера Visual Studio, но после создания претерпел значительные ручные изменения (не вините меня, это было до моего времени).Очевидно, что это может привести к непредвиденным побочным эффектам ... но я не понимаю, как можно решить эту проблему, даже если мы попробуем.

Ответы [ 2 ]

0 голосов
/ 08 ноября 2011

Я узнал немного больше об этой проблеме, которая, я думаю, решит мою проблему.

Я создал простое тестовое приложение, которое содержит построенный дизайнером DataSet со строго типизированными данными.Приложение порождает три потока при запуске.Два измените значения в том же столбце, затем прочитайте значение и повторите ad infintum.Третий поток непрерывно сохраняет DataSet на диск.

В конце концов (в течение промежутка времени от нескольких секунд до минуты) потоки загружают DataSet, так что он выдает исключение с текстом: «Внутренний индекс DataTableповрежден: 5 ".Если я продолжу проглатывать эти исключения, в конечном итоге я получаю «InvalidCastException», где данные не могут быть прочитаны из строки, потому что это DBNull.Когда это происходит, я вижу, что вся строка DBNull, даже первичные ключи.

Забавно, что метод «WriteXML» не вызывает исключение в этом состоянии;файл может быть сохранен таким образом, даже если он нарушает все ограничения.

В моем реальном приложении я считаю, что моей защиты от потоков недостаточно в этой области.Я не знаю, является ли это внутренней ошибкой в ​​ADO.NET или просто аргументом для хорошей защиты потоков вокруг объектов ADO.NET.В любом случае, это чрезвычайно раздражающая проблема.

0 голосов
/ 20 октября 2011

Это, конечно, странно, и я думаю, что, вероятно, происходит что-то еще.

Чтобы помочь вам в тестировании, я бы предложил подключить некоторые обработчики событий, которые вы можете проверить.

Вот тестовый класс, который вы можете использовать для WM5 и CF 2.0.

Посмотрите, что происходит, когда ваши данные теряются.

class TestOnly {

  public DataTable Table { get; set; }

  public TestOnly() {
    Table = new DataTable();
    Table.Disposed += new EventHandler(Table_Disposed);
    Table.ColumnChanging += new DataColumnChangeEventHandler(Table_ColumnChanging);
    Table.RowChanging += new DataRowChangeEventHandler(Table_RowChanging);
    Table.RowDeleting += new DataRowChangeEventHandler(Table_RowDeleting);
    Table.TableClearing += new DataTableClearEventHandler(Table_TableClearing);
  }

  bool myAct = false;

  void Table_TableClearing(object sender, DataTableClearEventArgs e) {
    Console.WriteLine("Table {0} clearing. {1}", e.TableName, DateTime.Now);
  }

  void Table_ColumnChanging(object sender, DataColumnChangeEventArgs e) {
    if (!myAct) {
      e.Row.CancelEdit();
      Console.WriteLine("Edit of {0} cancelled. {1}", e.Column.Caption, DateTime.Now);
    }
  }

  void Table_RowChanging(object sender, DataRowChangeEventArgs e) {
    if (!myAct) {
      e.Row.CancelEdit();
      Console.WriteLine("DataRow Action {0} cancelled. {2}", e.Action, DateTime.Now);
    }
  }

  void Table_RowDeleting(object sender, DataRowChangeEventArgs e) {
    if (!myAct) {
      e.Row.CancelEdit();
      Console.WriteLine("DataRow Action {0} cancelled. {2}", e.Action, DateTime.Now);
    }
  }

  void Table_Disposed(object sender, EventArgs e) {
    Console.WriteLine("Table Disposed at {0}.", DateTime.Now);
  }


}
...