Как предотвратить System.IO.File.Open с FileMode.Truncate от изменения файла - PullRequest
2 голосов
/ 15 декабря 2011

У меня есть приложение, которое работает с форматами файлов XML.Приложение может вносить изменения в эти файлы XML, но оно также использует System.IO.FileSystemWatcher, чтобы увидеть, изменились ли файлы XML за пределами программы во время ее работы.Если файлы изменились, то приложение перезагружает файлы XML.

Конечно, если приложение сохраняет файл XML, это заставляет Windows обнаруживать, что файл изменился, что запускает события FileSystemWatcher,Я не хочу, чтобы файл перезагружал файлы XML из-за изменений, которые он инициировал, поэтому всякий раз, когда он сохраняет файл XML, он хранит счетчик количества раз, которое следует игнорировать при изменении конкретного файла.Сохранение файла XML в приложении увеличивает количество.Реакция на изменение в файле уменьшает счетчик, если он не равен нулю, или обрабатывает изменение, если счет уже равен 0.

Проблема, с которой я сталкиваюсь, заключается в том, что при сохранении файла XML я делаю это следующим образом:

Stream fileStream = null;

if(System.IO.File.Exists(fileToSave)
{
   fileStream = System.IO.File.Open(fileName, FileMode.OpenOrCreate | FileMode.Truncate);
}
else
{
   fs = System.IO.File.Open(fileName, FileMode.OpenOrCreate);
}
...

Затем я сериализую свой объект XML в поток, используя XmlWriter, затем закрываю поток.Все работает нормально, за исключением того, что мои события в FileSystemWatcher будут вызваны дважды.Я потратил некоторое время на отладку и обнаружил, что вызов System.IO.File.Open с параметром Truncate фактически изменяет файл на диске.Я достиг точки останова сразу после вызова и обнаружил, что файл XML находится на диске размером 0 байт.Поэтому, похоже, что этот подход дважды попадает на диск, в результате чего мои события FileSystemWatcher возникают дважды.

Как решить эту проблему?

В качестве обходного пути я обнаружил, что могуудалите файл, если он существует, затем выполните простой File.Open без опции Truncate, а в моем случае это решает ... по крайней мере, пока.Я предполагаю, что это будет ударом по событию Delete в FileSystemWatcher.Технически я не хочу удалять файл, и если бы мне пришлось добавить определенную логику, когда файл будет удален позднее, этот код, вероятно, вызвал бы его неправильную работу.

Любые другие способыизменить существующий файл, не вызывая 2 события FileSystemWatcher?

обновление 1

Прошло много лет с тех пор, как я задал этот вопрос и файлСистема часов стала центральной частью приложения, с которым я работаю и поддерживаю с тех пор, как я задавала этот вопрос более 5 лет назад.

К сожалению, с тех пор я все еще не нашла лучшего решения дляSerliaizing XML на диск, не вызывая 2 файла наблюдения изменений.

Для будущих читателей вот код, который я использую:

if (System.IO.File.Exists(fileName))
{
  System.IO.File.Delete(fileName);
}

using( var fs = System.IO.File.Open(fileName, FileMode.OpenOrCreate))
{
  XmlWriterSettings settings = new XmlWriterSettings();
  settings.Indent = true;
  using(var writer = XmlWriter.Create(fs, settings))
  {
    // for info on this, see
    // /1080329/xmlserializer-dayschii-filenotfoundexception-u-konstruktora
    XmlSerializer serializer = XmlSerializer.FromTypes(new[] { type })[0];

    serializer.Serialize(writer, objectToSerialize);

  }
}

Ответы [ 2 ]

2 голосов
/ 15 декабря 2011

Итак, у вас есть ситуация, в которой изменения могут происходить в каталоге, и вы хотите, чтобы ваша программа реагировала на эти изменения. Это само по себе не проблема. Проблема в том, что ваша программа не может сказать, какой процесс вносит изменения. Так что если это что-то изменит в этом каталоге, то вы попадете в бесконечный цикл.

Любая схема, которую вы придумали для решения этой проблемы, будет хрупкой, потому что она пострадает от условий гонки. Вы можете попробовать разработать схему, которая позволит вам игнорировать следующие N уведомлений об обновлениях для файла, который вы изменили, но в какой-то момент вы обнаружите, что какой-то другой процесс сделал обновление, которое вы проигнорировали.

Единственный надежный способ для этого - иметь две директории. У вас есть каталог submissions, куда клиентские программы добавляют файлы. Ваша программа отслеживает этот каталог для дополнений. Когда клиент добавляет файл в submissions, ваша программа перемещает файл в каталог repository. Клиенты имеют доступ только для чтения к каталогу repository. Только ваша программа может добавлять, изменять или удалять файлы в хранилище.

1 голос
/ 15 декабря 2011

Я бы не реализовал никакой логики, чтобы избежать чтения XML-файла в определенной ситуации. Это сделало бы вашу программу намного проще. Просто разберитесь с ситуацией, когда ваша программа считывает изменения, которые она сделала сама, и вы уменьшите возможность пропустить некоторые изменения из-за своего рода «магического механизма, игнорирующего ваши собственные изменения».

...