С# File.Replace защита от сбоя

Выполняет ли операция File.Replace атомную/транзакционную операцию, если при сбое или сбое питания целевой файл никогда не будет отсутствовать, а частичный файл (т.е. будет оригиналом или новым файлом)?

Если нет, существует ли другой способ защиты от этого сценария?

Примечание. Это будет на диске NTFS с Windows 7 или новее, которое, как я понимаю, поддерживает транзакции.

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

Ответ 1

File.Replace использует функцию WinAPI ReplaceFile внутренне (в Windows, конечно). Однако атомарность не является документированным поведением даже в этой функции, а документация несколько неоднозначна.

Во-первых, если вы хотите согласованности, вы должны использовать файл резервной копии. Согласно документации:

[При перемещении файла сбой...] Если указано lpBackupFileName, замененные и заменяющие файлы сохраняют свои исходные имена файлов. В противном случае замененный файл больше не существует, и файл замены существует под его первоначальным именем.

Другой режим отказа приводит к

[При перемещении файла сбой...] Файл замены по-прежнему существует под его первоначальным именем; однако он унаследовал файловые потоки и атрибуты из файла, который он заменяет. Файл, который нужно заменить, все еще существует с другим именем. Если указано lpBackupFileName, это будет имя замененного файла.

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

Наконец, когда удаление не выполняется, ничего не происходит.

Итак, вся операция атома? Несмотря на то, что это официально не задокументировано, у нас есть несколько указателей. Во-первых, операция замены - это, в конечном счете, своп идентификаторов файлов (и одностороннее обновление всех атрибутов файла), если вы используете опцию резервного файла; что операция, транзакционная на NTFS, поэтому я ожидал, что эта часть будет фактически атомарной, если вам не нужно беспокоиться об атрибутах файлов, ACL и альтернативных потоках данных.

Однако это поведение не является договорным, ни для File.Replace, ни для ReplaceFile. Если вам нужен контрактный способ реализации трансакционных операций, вам необходимо использовать TxF. Две основные проблемы: один, TxF поддерживается только с Vista, и два, он практически не использовался на практике и устарел. Bummer:) Официальный рекомендуемый Microsoft способ замены TxF описан в https://msdn.microsoft.com/en-us/library/windows/desktop/hh802690%28v=vs.85%29.aspx - и включает использование ReplaceFile (отображается в .NET как File.Replace).