Search code examples
c#transactionsstoragefile-storage

How to implement own transaction for IO storage


public interface IFileStorage
{
    void Save(string key);
    void Delete(string key);
}
var fileStorage = new FileStorage();
var files = new List<string>();

foreach(var file in files)
{
    fileStorage.Save(file);
}

Consider a simple file storage. There is no rollback mechanism whatsoever for cases when app fails in the middle of foreach loop, already saved documents would remain saved.

What would it take to look like this?

using (var transaction = new CustomFileStorageTransaction())
{
    var fileStorage = new FileStorage();
    var files = new List<string>();

    foreach (var file in files)
    {
        FileStorage.Save(file);
    }
}

If error occurs then transaction should automatically performs Delete method on already saved documents within it's scope.

The whole thing can be done with try/catch but I want to be able to build more generic solutions.


Solution

  • You need to keep a transaction log of what you are about to do, then you need a mechanism that replays any missing parts (commit), or perhaps undoes how ever far the transaction took (rollback).

    In your case, IFileStorage doesn't actually delete any files, it adds them to a 'to be deleted' log, and when the transaction is committed, you delete them, one at a time, and also remove them from the transaction log once deletion is confirmed.

    public interface IFileStorage
    {
        void Save(string key);
        void Delete(string key);
        void Commit();
    }
    
    class CustomFileStorageTransaction : IFileStorage
    {
        ICollection<string> _filesToDelete;
        public void Delete(string key)
        { 
           _filesToDelete.Add(key);
           Persist _filesToDelete
        }
        public void Commit()
        {
            foreach(var fileToDelete in _filesToDelete)
            {
                File.Delete(fileToDelete);
                _filesToDelete.Remove(fileToDelete); <-- You can't actually do this, but this is effectively what you need to do
                Persist changed _filesToDelete
            }
        }
    }
    
    
    using (var transaction = new CustomFileStorageTransaction())
    {
        var fileStorage = new FileStorage();
        var files = new List<string>();
    
        foreach (var file in files)
        {
            transaction.Save(file); <-- Add to a transaction log
        }
    
        transaction.Commit();  <--- this is where the files are actually deleted
    }