Transactional NTFS et DotNet
Microsoft introduit un concept intéressant à partir des versions de Windows Vista et Server 2008.
Cette nouveauté est appelée Transactional NTFS (TxF).
Elle permet aux développeurs d’écrire des fonctions d’Entrée/Sortie garantissant le succès complet ou le rejet complet en cas d’erreur d’un ensemble d’opérations.
Malheureusement il n’existe pas de classe DotNet(au moins jusqu’à la version 3.5 SP1) permettant de manipuler simplement ce type d’opérations.
Pour manipuler ces opérations, nous avons besoin de passer par le P/Invoke pour utiliser ces fonctions :
CreateTransaction, CommitTransaction, RollbackTransaction et DeleteFileTransacted
class Win32Native { [StructLayout(LayoutKind.Sequential)] public struct SECURITY_ATTRIBUTES { int nLength; IntPtr lpSecurityDescriptor; int bInheritHandle; } [DllImport("ktmw32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern SafeFileHandle CreateTransaction (SECURITY_ATTRIBUTES securityAttributes , IntPtr guid, int options, int isolationLevel, int isolationFlags, int milliSeconds, string description); [DllImport("ktmw32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool CommitTransaction(SafeFileHandle transaction); [DllImport("ktmw32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool RollbackTransaction(SafeFileHandle transaction); [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] public static extern bool DeleteFileTransacted (string filename, SafeFileHandle transaction); }
Notez que j’utilise SafeFileHandle dans la signature des méthodes à la place de IntPtr pour les ancienne ressources non managées afin de garantir que les éléments seront bien libérés même si l’application doit s’arrêter.
L’étape suivante est la définition d’une classe permettant d’appeler simplement les fonctions déclarées.
public class FileManager : IDisposable { private bool _commited = false; private SafeFileHandle _tx = null; public FileManager() { _tx = Win32Native.CreateTransaction (new Win32Native.SECURITY_ATTRIBUTES(), IntPtr.Zero , 0, 0, 0, 0, null); } public bool DeleteFile(string filename) { return Win32Native.DeleteFileTransacted (filename, _tx); } public void Commit() { if (Win32Native.CommitTransaction(_tx)) _commited = true; } private void Rollback() { Win32Native.RollbackTransaction(_tx); } protected virtual void Dispose(bool disposing) { if (disposing) { if (!_commited) { Rollback(); } } } public void Dispose() { Dispose(true); } }
Exemple avec la suppression de 2 fichiers
Supposons que nous devons supprimer automatiquement 2 fichiers. Les 2 fichiers doivent être supprimés en même temps. Si un des 2 fichiers ne peut pas être supprimé, les 2 fichiers doivent être conservés.
using (FileManager manager = new FileManager()) { manager.DeleteFile("file1.txt"); Console.WriteLine("file1.txt is marked for deletion in current transaction. Press Enter..."); Console.ReadLine(); //throw new Exception("something very bad happens here"); manager.DeleteFile("file2.txt"); Console.WriteLine("file2.txt is marked for deletion in current transaction."); manager.Commit(); }
La méthode DeleteFile marque le fichier pour la suppression dans le transaction courante, mais ne la supprimera uniquement si la méthode Commit est appelée.
Grâce à TFX et les transactions distribuées, les opérations sur les fichiers et les opérations SQL peuvent s’effectuer dans une même opération.
Dans la 2ème partie, nous verrons comment utiliser la fonction CreateFileTransacted pour traiter de façon unique la lecture et l’écriture d’un fichier.