mercredi 4 juin 2008

Implémentation d'un objet de gestion d'une transaction sur les fichiers

Implémentation de FileTransaction, un objet simple de sauvegarde et de restauration des fichiers sur lesquels on travail.

/// <summary>
///
Se charge du backup et de la restauration des fichiers qui sont ajoute9 e0 sa Gestion.
/// ne sauvegarde pas 2 fois le meame fichier.
/// </summary>
public class FileTransaction : IDisposable
{
const string DELETE_FILE_MAP = "DELETE";
string BackupFolder;

#region Constructeurs
/// <summary>
///
En utilisant ce constructeur c'est un re9pertoire alle9atoire qui est utilise9 pour la sauvegarde
/// </summary>
public FileTransaction()
: this(
Path.Combine(
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Machine),
Path.GetRandomFileName()
)
) { }
/// <summary>
/// </summary>
/// <param name="backupFolder">
Spe9cifie le re9pertoire qui sera utilise9 pour les sauvegardes</param>
public FileTransaction(string backupFolder)
{
if (Directory.Exists(backupFolder)) Directory.Delete(backupFolder, true);
BackupFolder = Directory.CreateDirectory(backupFolder).FullName;
}
#endregion

private readonly
Dictionary<String, String> File_Map = new Dictionary<string, string>();

#region Ajout de fichier e0 la transaction
/// <summary>
///
Ajoute une liste de fichier e0 la transaction
/// </summary>
/// <param name="files"></param>
public void AddFiles(IEnumerable<String> files)
{
foreach (string file in files) AddFile(file);
}

/// <summary>
///
Ajout un fichier e0 la transaction
/// </summary>
/// <param name="file">
Chemin du fichier (relatif ou absolu)</param>
public void AddFile(string file)
{
string saved_file = Path.GetFullPath(file);
// Pas 2 fois le meame fichier quand meame !
if (File_Map.ContainsKey(saved_file)) return;

if (!File.Exists(saved_file))
{
// En cas de RollBack on supprimera le fichier
File_Map.Add(saved_file, DELETE_FILE_MAP);
}
else
{
string renamed_backup_filename = Path.GetFileName(saved_file) + "." + Path.GetRandomFileName();
string backup_file = Path.Combine(BackupFolder, renamed_backup_filename);

// On s'assure que le re9pertoire de copie existe
Directory.CreateDirectory(Path.GetDirectoryName(backup_file));

// c'est parti
File.Copy(saved_file, backup_file);
File_Map.Add(saved_file, backup_file);
}
}
#endregion

#region
Commit
/// <summary>
///
Efface la transaction et la possibilite9 de RollBack
/// </summary>
public void Commit() { Commit(true); }
public void Commit(bool destroyBackup)
{
Clear_File_Map();
if (destroyBackup) DestroyBackup();
}
#endregion

#region
RollBack
/// <summary>
///
Remet tous les fichier sauvegarde9 e0 leur place d'origine
/// </summary>
public void RollBack() { RollBack(true); }
public void RollBack(bool destroyBackup)
{
RestoreFiles();
Clear_File_Map();
if (destroyBackup) DestroyBackup();
}
#endregion

#region
Me9thodes prive9es
void Clear_File_Map()
{
File_Map.Clear();
}

void RestoreFiles()
{
foreach (string file in this.File_Map.Keys)
{
string backup = File_Map[file];

if (backup == DELETE_FILE_MAP)
{
if (File.Exists(file)) File.Delete(file);
}
else
{
string dir = Path.GetDirectoryName(file);
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir);
File.Copy(backup, file, true);
}
}
}

void DestroyBackup() { DestroyBackup(this.BackupFolder); }
void DestroyBackup(string backupFolder)
{
if (Directory.Exists(backupFolder)) Directory.Delete(backupFolder, true);
}
#endregion

#region
IDisposable Membres

public void Dispose()
{
if (File_Map.Count != 0) RollBack();
else Clear_File_Map();
}

#endregion
}


Utilisation :

FileTransaction FT = new FileTransaction();
String filename = "MonFichierSuperImportant.dat";
FT.AddFile(filename);
File.Delete(filename);
FT.RollBack();
// le fichier est restauré


Autre utilisation :

using (FileTransaction FT = new FileTransaction())
{
try
{
FT.AddFiles(new string[]{
"fichier1.txt",
"fichier2.txt",
"fichier3.txt"
});

// Traitement atomique : tout ou rien

// Helas quelque chose tourne mal
throw new Exception("TEST");

FT.Commit();
}
catch (Exception ex)
{
FT.RollBack();
}
}


FileTransaction implémente IDispose, ce qui lui donne des possibilités de restauration des fichiers en cas de destruction de l'objet avant le Commit.

string TestFile = "Mon fichier";
using (FileTransaction FT = new FileTransaction())
{
FT.AddFile(TestFile);
File.Delete(TestFile);
}// Dispose ;-)


Ici le fichier est restauré à la fin du using, automatiquement.

Aucun commentaire: