diff --git a/FileFS.Client.Tests/FileFsClientTests/FileFsClientDirectoryTests.cs b/FileFS.Client.Tests/FileFsClientTests/FileFsClientDirectoryTests.cs index 0a33dab..858e687 100644 --- a/FileFS.Client.Tests/FileFsClientTests/FileFsClientDirectoryTests.cs +++ b/FileFS.Client.Tests/FileFsClientTests/FileFsClientDirectoryTests.cs @@ -1,6 +1,6 @@ using System; using FileFS.Client.Exceptions; -using FileFS.Client.Transactions.Abstractions; +using FileFS.Client.Transactions; using FileFS.DataAccess; using FileFS.DataAccess.Constants; using FileFS.DataAccess.Entities; @@ -24,7 +24,6 @@ public class FileFsClientDirectoryTests private readonly Mock _entryRepositoryMock; private readonly Mock _fileRepositoryMock; private readonly Mock _directoryRepositoryMock; - private readonly Mock _transactionWrapperMock; private readonly FileFsClient _client; public FileFsClientDirectoryTests() @@ -37,17 +36,13 @@ public FileFsClientDirectoryTests() .Setup(r => r.Exists(PathConstants.RootDirectoryName)) .Returns(true); - _transactionWrapperMock = new Mock(); - _transactionWrapperMock.Setup(t => t.BeginTransaction()); - _transactionWrapperMock.Setup(t => t.EndTransaction()); - _client = new FileFsClient( _fileRepositoryMock.Object, _directoryRepositoryMock.Object, _entryRepositoryMock.Object, null, null, - _transactionWrapperMock.Object, + new NullTransactionWrapper(), new StorageOperationLocker()); } @@ -578,4 +573,4 @@ public void IsDirectory_WhenDirectoryNotExists_ShouldReturnFalse(string name) Assert.False(directoryExists); } } -} \ No newline at end of file +} diff --git a/FileFS.Client.Tests/FileFsClientTests/FileFsClientFileTests.cs b/FileFS.Client.Tests/FileFsClientTests/FileFsClientFileTests.cs index 658b4f3..df8fe6f 100644 --- a/FileFS.Client.Tests/FileFsClientTests/FileFsClientFileTests.cs +++ b/FileFS.Client.Tests/FileFsClientTests/FileFsClientFileTests.cs @@ -2,7 +2,7 @@ using System.Text; using FileFS.Client.Abstractions; using FileFS.Client.Exceptions; -using FileFS.Client.Transactions.Abstractions; +using FileFS.Client.Transactions; using FileFS.DataAccess; using FileFS.DataAccess.Allocation.Abstractions; using FileFS.DataAccess.Constants; @@ -30,7 +30,6 @@ public class FileFsClientFileTests private readonly Mock _storageOptimizerMock; private readonly Mock _entryRepositoryMock; private readonly Mock _directoryRepositoryMock; - private readonly Mock _transactionWrapperMock; private readonly FileFsClient _client; public FileFsClientFileTests() @@ -45,17 +44,13 @@ public FileFsClientFileTests() .Setup(r => r.Exists(PathConstants.RootDirectoryName)) .Returns(true); - _transactionWrapperMock = new Mock(); - _transactionWrapperMock.Setup(t => t.BeginTransaction()); - _transactionWrapperMock.Setup(t => t.EndTransaction()); - _client = new FileFsClient( _fileRepositoryMock.Object, _directoryRepositoryMock.Object, _entryRepositoryMock.Object, _externalFileManagerMock.Object, _storageOptimizerMock.Object, - _transactionWrapperMock.Object, + new NullTransactionWrapper(), new StorageOperationLocker()); } @@ -1117,4 +1112,4 @@ public void FileExists_WithInvalidFileName_ShouldThrowException(string fileName) Assert.Throws(Act); } } -} \ No newline at end of file +} diff --git a/FileFS.Client.Tests/FileFsClientTests/FileFsClientGenericTests.cs b/FileFS.Client.Tests/FileFsClientTests/FileFsClientGenericTests.cs index 0a9861c..ad128b4 100644 --- a/FileFS.Client.Tests/FileFsClientTests/FileFsClientGenericTests.cs +++ b/FileFS.Client.Tests/FileFsClientTests/FileFsClientGenericTests.cs @@ -1,7 +1,7 @@ using System; using System.Text; using FileFS.Client.Exceptions; -using FileFS.Client.Transactions.Abstractions; +using FileFS.Client.Transactions; using FileFS.DataAccess; using FileFS.DataAccess.Constants; using FileFS.DataAccess.Entities; @@ -26,7 +26,6 @@ public class FileFsClientGenericTests private readonly Mock _entryRepositoryMock; private readonly Mock _fileRepositoryMock; private readonly Mock _directoryRepositoryMock; - private readonly Mock _transactionWrapperMock; private readonly FileFsClient _client; public FileFsClientGenericTests() @@ -39,17 +38,13 @@ public FileFsClientGenericTests() .Setup(r => r.Exists(PathConstants.RootDirectoryName)) .Returns(true); - _transactionWrapperMock = new Mock(); - _transactionWrapperMock.Setup(t => t.BeginTransaction()); - _transactionWrapperMock.Setup(t => t.EndTransaction()); - _client = new FileFsClient( _fileRepositoryMock.Object, _directoryRepositoryMock.Object, _entryRepositoryMock.Object, null, null, - _transactionWrapperMock.Object, + new NullTransactionWrapper(), new StorageOperationLocker()); } @@ -403,4 +398,4 @@ public void GetEntriesInfo_WhenThereAreNoFiles_ShouldReturnEmptyCollection() Assert.Empty(files); } } -} \ No newline at end of file +} diff --git a/FileFS.Client.Tests/Transactions/TransactionWrapperTests.cs b/FileFS.Client.Tests/Transactions/TransactionWrapperTests.cs new file mode 100644 index 0000000..fcfbdc7 --- /dev/null +++ b/FileFS.Client.Tests/Transactions/TransactionWrapperTests.cs @@ -0,0 +1,46 @@ +using System; +using System.Threading.Tasks; +using FileFS.Client.Transactions; +using Serilog; +using Xunit; + +// Missing XML comment for publicly visible type or member... +#pragma warning disable 1591 + +// Elements should be documented +#pragma warning disable SA1600 + +namespace FileFS.Client.Tests.Transactions +{ + public class TransactionWrapperTests + { + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Execute_WhenOperationThrows_ShouldReleaseMutex(bool useGenericOverload) + { + // Arrange + var logger = new LoggerConfiguration().CreateLogger(); + using var transactionWrapper = new TransactionWrapper($"{Guid.NewGuid()}.filefs", logger); + var expectedException = new InvalidOperationException(); + + // Act + void Act() + { + if (useGenericOverload) + { + transactionWrapper.Execute(() => throw expectedException); + } + else + { + transactionWrapper.Execute(() => throw expectedException); + } + } + + // Assert + Assert.Same(expectedException, Assert.Throws(Act)); + var task = Task.Run(() => transactionWrapper.Execute(() => { })); + Assert.True(task.Wait(TimeSpan.FromSeconds(5))); + } + } +} diff --git a/FileFS.Client/FileFsClient.cs b/FileFS.Client/FileFsClient.cs index dfaf3e5..9933156 100644 --- a/FileFS.Client/FileFsClient.cs +++ b/FileFS.Client/FileFsClient.cs @@ -66,31 +66,30 @@ public FileFsClient( /// Throws if file already exists. public void CreateDirectory(string name) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(name, () => + _transactionWrapper.Execute(() => { - if (!NameValid(name)) + _storageOperationLocker.LockEntry(name, () => { - throw new InvalidNameException(name); - } + if (!NameValid(name)) + { + throw new InvalidNameException(name); + } - if (ExistsInternal(name)) - { - throw new EntryAlreadyExistsException(name); - } + if (ExistsInternal(name)) + { + throw new EntryAlreadyExistsException(name); + } - var parentDirectoryName = name.GetParentFullName(); - if (!DirectoryExistsInternal(parentDirectoryName)) - { - throw new DirectoryNotFoundException(name); - } + var parentDirectoryName = name.GetParentFullName(); + if (!DirectoryExistsInternal(parentDirectoryName)) + { + throw new DirectoryNotFoundException(name); + } - var directoryEntry = CreateDirectoryEntry(name); - _directoryRepository.Create(directoryEntry); + var directoryEntry = CreateDirectoryEntry(name); + _directoryRepository.Create(directoryEntry); + }); }); - - _transactionWrapper.EndTransaction(); } /// @@ -109,40 +108,39 @@ public void CreateFile(string fileName) /// Throws if data is empty. public void CreateFile(string fileName, byte[] data) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(fileName, () => + _transactionWrapper.Execute(() => { - if (!NameValid(fileName)) + _storageOperationLocker.LockEntry(fileName, () => { - throw new InvalidNameException(fileName); - } - - if (ExistsInternal(fileName)) - { - throw new EntryAlreadyExistsException(fileName); - } - - var parentDirectoryName = fileName.GetParentFullName(); - - _storageOperationLocker.LockEntry(parentDirectoryName, () => - { - if (!DirectoryExistsInternal(parentDirectoryName)) + if (!NameValid(fileName)) { - throw new DirectoryNotFoundException(parentDirectoryName); + throw new InvalidNameException(fileName); } - if (data is null) + if (ExistsInternal(fileName)) { - throw new DataIsNullException(fileName); + throw new EntryAlreadyExistsException(fileName); } - var parentDirectory = _directoryRepository.Find(parentDirectoryName); - _fileRepository.Create(new FileEntry(fileName, parentDirectory.Id, data)); + var parentDirectoryName = fileName.GetParentFullName(); + + _storageOperationLocker.LockEntry(parentDirectoryName, () => + { + if (!DirectoryExistsInternal(parentDirectoryName)) + { + throw new DirectoryNotFoundException(parentDirectoryName); + } + + if (data is null) + { + throw new DataIsNullException(fileName); + } + + var parentDirectory = _directoryRepository.Find(parentDirectoryName); + _fileRepository.Create(new FileEntry(fileName, parentDirectory.Id, data)); + }); }); }); - - _transactionWrapper.EndTransaction(); } /// @@ -152,40 +150,39 @@ public void CreateFile(string fileName, byte[] data) /// Throws if data is empty. public void CreateFile(string fileName, Stream sourceStream, int length) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(fileName, () => + _transactionWrapper.Execute(() => { - if (!NameValid(fileName)) - { - throw new InvalidNameException(fileName); - } - - if (ExistsInternal(fileName)) - { - throw new EntryAlreadyExistsException(fileName); - } - - var parentDirectoryName = fileName.GetParentFullName(); - - _storageOperationLocker.LockEntry(parentDirectoryName, () => + _storageOperationLocker.LockEntry(fileName, () => { - if (!DirectoryExistsInternal(parentDirectoryName)) + if (!NameValid(fileName)) { - throw new DirectoryNotFoundException(fileName); + throw new InvalidNameException(fileName); } - if (sourceStream is null) + if (ExistsInternal(fileName)) { - throw new DataIsNullException(fileName); + throw new EntryAlreadyExistsException(fileName); } - var parentDirectory = _directoryRepository.Find(parentDirectoryName); - _fileRepository.Create(new StreamedFileEntry(fileName, parentDirectory.Id, sourceStream, length)); + var parentDirectoryName = fileName.GetParentFullName(); + + _storageOperationLocker.LockEntry(parentDirectoryName, () => + { + if (!DirectoryExistsInternal(parentDirectoryName)) + { + throw new DirectoryNotFoundException(fileName); + } + + if (sourceStream is null) + { + throw new DataIsNullException(fileName); + } + + var parentDirectory = _directoryRepository.Find(parentDirectoryName); + _fileRepository.Create(new StreamedFileEntry(fileName, parentDirectory.Id, sourceStream, length)); + }); }); }); - - _transactionWrapper.EndTransaction(); } /// @@ -194,35 +191,34 @@ public void CreateFile(string fileName, Stream sourceStream, int length) /// Throws if data is empty. public void Update(string fileName, byte[] newData) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(fileName, () => + _transactionWrapper.Execute(() => { - if (!NameValid(fileName)) + _storageOperationLocker.LockEntry(fileName, () => { - throw new InvalidNameException(fileName); - } + if (!NameValid(fileName)) + { + throw new InvalidNameException(fileName); + } - if (!FileExistsInternal(fileName)) - { - throw new FileNotFoundException(fileName); - } + if (!FileExistsInternal(fileName)) + { + throw new FileNotFoundException(fileName); + } - if (newData is null) - { - throw new DataIsNullException(fileName); - } + if (newData is null) + { + throw new DataIsNullException(fileName); + } - var parentDirectoryName = fileName.GetParentFullName(); + var parentDirectoryName = fileName.GetParentFullName(); - _storageOperationLocker.LockEntry(parentDirectoryName, () => - { - var parentDirectory = _directoryRepository.Find(parentDirectoryName); - _fileRepository.Update(new FileEntry(fileName, parentDirectory.Id, newData)); + _storageOperationLocker.LockEntry(parentDirectoryName, () => + { + var parentDirectory = _directoryRepository.Find(parentDirectoryName); + _fileRepository.Update(new FileEntry(fileName, parentDirectory.Id, newData)); + }); }); }); - - _transactionWrapper.EndTransaction(); } /// @@ -231,35 +227,34 @@ public void Update(string fileName, byte[] newData) /// Throws if data is empty. public void Update(string fileName, Stream sourceStream, int length) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(fileName, () => + _transactionWrapper.Execute(() => { - if (!NameValid(fileName)) + _storageOperationLocker.LockEntry(fileName, () => { - throw new InvalidNameException(fileName); - } + if (!NameValid(fileName)) + { + throw new InvalidNameException(fileName); + } - if (!FileExistsInternal(fileName)) - { - throw new FileNotFoundException(fileName); - } + if (!FileExistsInternal(fileName)) + { + throw new FileNotFoundException(fileName); + } - if (sourceStream is null) - { - throw new DataIsNullException(fileName); - } + if (sourceStream is null) + { + throw new DataIsNullException(fileName); + } - var parentDirectoryName = fileName.GetParentFullName(); + var parentDirectoryName = fileName.GetParentFullName(); - _storageOperationLocker.LockEntry(parentDirectoryName, () => - { - var parentDirectory = _directoryRepository.Find(parentDirectoryName); - _fileRepository.Update(new StreamedFileEntry(fileName, parentDirectory.Id, sourceStream, length)); + _storageOperationLocker.LockEntry(parentDirectoryName, () => + { + var parentDirectory = _directoryRepository.Find(parentDirectoryName); + _fileRepository.Update(new StreamedFileEntry(fileName, parentDirectory.Id, sourceStream, length)); + }); }); }); - - _transactionWrapper.EndTransaction(); } /// @@ -267,26 +262,21 @@ public void Update(string fileName, Stream sourceStream, int length) /// Throws if file not found. public byte[] Read(string fileName) { - _transactionWrapper.BeginTransaction(); - - var data = _storageOperationLocker.LockEntry(fileName, () => - { - if (!NameValid(fileName)) + return _transactionWrapper.Execute(() => + _storageOperationLocker.LockEntry(fileName, () => { - throw new InvalidNameException(fileName); - } - - if (!FileExistsInternal(fileName)) - { - throw new FileNotFoundException(fileName); - } - - return _fileRepository.Read(fileName).Data; - }); + if (!NameValid(fileName)) + { + throw new InvalidNameException(fileName); + } - _transactionWrapper.EndTransaction(); + if (!FileExistsInternal(fileName)) + { + throw new FileNotFoundException(fileName); + } - return data; + return _fileRepository.Read(fileName).Data; + })); } /// @@ -295,29 +285,28 @@ public byte[] Read(string fileName) /// Throws if file not found. public void Read(string fileName, Stream destinationStream) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(fileName, () => + _transactionWrapper.Execute(() => { - if (!NameValid(fileName)) + _storageOperationLocker.LockEntry(fileName, () => { - throw new InvalidNameException(fileName); - } + if (!NameValid(fileName)) + { + throw new InvalidNameException(fileName); + } - if (!FileExistsInternal(fileName)) - { - throw new FileNotFoundException(fileName); - } + if (!FileExistsInternal(fileName)) + { + throw new FileNotFoundException(fileName); + } - if (destinationStream is null) - { - throw new ArgumentNonValidException($"Argument cannot be null: {nameof(destinationStream)}"); - } + if (destinationStream is null) + { + throw new ArgumentNonValidException($"Argument cannot be null: {nameof(destinationStream)}"); + } - _fileRepository.ReadData(fileName, destinationStream); + _fileRepository.ReadData(fileName, destinationStream); + }); }); - - _transactionWrapper.EndTransaction(); } /// @@ -325,103 +314,101 @@ public void Read(string fileName, Stream destinationStream) /// Throws if entry not found. public void Rename(string currentName, string newName) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(currentName, () => + _transactionWrapper.Execute(() => { - _storageOperationLocker.LockEntry(newName, () => + _storageOperationLocker.LockEntry(currentName, () => { - if (!NameValid(currentName)) - { - throw new InvalidNameException(currentName); - } - - if (!NameValid(newName)) - { - throw new InvalidNameException(newName); - } - - if (!ExistsInternal(currentName)) - { - throw new EntryNotFoundException(currentName); - } - - if (ExistsInternal(newName)) - { - throw new EntryAlreadyExistsException(newName); - } - - var currentParentName = currentName.GetParentFullName(); - var newParentName = newName.GetParentFullName(); - - if (currentParentName != newParentName) + _storageOperationLocker.LockEntry(newName, () => { - throw new ArgumentNonValidException( - $"New name of an entry should be inside same directory as current name, expected '{currentParentName}', got '{newParentName}'"); - } - - if (currentName == PathConstants.RootDirectoryName) - { - throw new OperationIsInvalid("Rename of root directory is not allowed"); - } - - _entryRepository.Rename(currentName, newName); + if (!NameValid(currentName)) + { + throw new InvalidNameException(currentName); + } + + if (!NameValid(newName)) + { + throw new InvalidNameException(newName); + } + + if (!ExistsInternal(currentName)) + { + throw new EntryNotFoundException(currentName); + } + + if (ExistsInternal(newName)) + { + throw new EntryAlreadyExistsException(newName); + } + + var currentParentName = currentName.GetParentFullName(); + var newParentName = newName.GetParentFullName(); + + if (currentParentName != newParentName) + { + throw new ArgumentNonValidException( + $"New name of an entry should be inside same directory as current name, expected '{currentParentName}', got '{newParentName}'"); + } + + if (currentName == PathConstants.RootDirectoryName) + { + throw new OperationIsInvalid("Rename of root directory is not allowed"); + } + + _entryRepository.Rename(currentName, newName); + }); }); }); - - _transactionWrapper.EndTransaction(); } /// public void Move(string from, string to) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(from, () => + _transactionWrapper.Execute(() => { - _storageOperationLocker.LockEntry(to, () => + _storageOperationLocker.LockEntry(from, () => { - if (!NameValid(from)) + _storageOperationLocker.LockEntry(to, () => { - throw new InvalidNameException(from); - } - - if (!NameValid(to)) - { - throw new InvalidNameException(to); - } - - if (!ExistsInternal(from)) - { - throw new EntryNotFoundException(from); - } - - if (ExistsInternal(to)) - { - throw new EntryAlreadyExistsException(to); - } - - var newParentName = to.GetParentFullName(); - if (!DirectoryExistsInternal(newParentName)) - { - throw new DirectoryNotFoundException(newParentName); - } - - if (from == PathConstants.RootDirectoryName) - { - throw new OperationIsInvalid("Move of root directory is not allowed"); - } - - if (from.IsParentTo(to)) - { - throw new OperationIsInvalid("Move of parent entry inside it child is not allowed"); - } + if (!NameValid(from)) + { + throw new InvalidNameException(from); + } + + if (!NameValid(to)) + { + throw new InvalidNameException(to); + } + + if (!ExistsInternal(from)) + { + throw new EntryNotFoundException(from); + } + + if (ExistsInternal(to)) + { + throw new EntryAlreadyExistsException(to); + } + + var newParentName = to.GetParentFullName(); + if (!DirectoryExistsInternal(newParentName)) + { + throw new DirectoryNotFoundException(newParentName); + } + + if (from == PathConstants.RootDirectoryName) + { + throw new OperationIsInvalid("Move of root directory is not allowed"); + } + + if (from.IsParentTo(to)) + { + throw new OperationIsInvalid("Move of parent entry inside it child is not allowed"); + } + }); }); - }); - MoveInternal(from, to); - - _transactionWrapper.EndTransaction(); + MoveInternal(from, to); + }); } /// @@ -472,29 +459,28 @@ public void Copy(string from, string to) /// Throws if file not found. public void Delete(string name) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(name, () => + _transactionWrapper.Execute(() => { - if (!NameValid(name)) + _storageOperationLocker.LockEntry(name, () => { - throw new InvalidNameException(name); - } + if (!NameValid(name)) + { + throw new InvalidNameException(name); + } - if (!ExistsInternal(name)) - { - throw new EntryNotFoundException(name); - } + if (!ExistsInternal(name)) + { + throw new EntryNotFoundException(name); + } - if (name == PathConstants.RootDirectoryName) - { - throw new ArgumentNonValidException("Delete of root directory is not allowed"); - } + if (name == PathConstants.RootDirectoryName) + { + throw new ArgumentNonValidException("Delete of root directory is not allowed"); + } - DeleteInternal(name); + DeleteInternal(name); + }); }); - - _transactionWrapper.EndTransaction(); } /// @@ -503,30 +489,29 @@ public void Delete(string name) /// Throws if external file was not found. public void ImportFile(string externalPath, string fileName) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(fileName, () => + _transactionWrapper.Execute(() => { - if (!NameValid(fileName)) + _storageOperationLocker.LockEntry(fileName, () => { - throw new InvalidNameException(fileName); - } + if (!NameValid(fileName)) + { + throw new InvalidNameException(fileName); + } - if (ExistsInternal(fileName)) - { - throw new EntryAlreadyExistsException(fileName); - } + if (ExistsInternal(fileName)) + { + throw new EntryAlreadyExistsException(fileName); + } - if (!_externalFileManager.Exists(externalPath)) - { - throw new ExternalFileNotFoundException(externalPath); - } + if (!_externalFileManager.Exists(externalPath)) + { + throw new ExternalFileNotFoundException(externalPath); + } - using var externalFileStream = _externalFileManager.OpenReadStream(externalPath); - CreateFileInternal(fileName, externalFileStream, (int)externalFileStream.Length); + using var externalFileStream = _externalFileManager.OpenReadStream(externalPath); + CreateFileInternal(fileName, externalFileStream, (int)externalFileStream.Length); + }); }); - - _transactionWrapper.EndTransaction(); } /// @@ -535,93 +520,77 @@ public void ImportFile(string externalPath, string fileName) /// Throws if external file already exists. public void ExportFile(string fileName, string externalPath) { - _transactionWrapper.BeginTransaction(); - - _storageOperationLocker.LockEntry(fileName, () => + _transactionWrapper.Execute(() => { - if (!NameValid(fileName)) + _storageOperationLocker.LockEntry(fileName, () => { - throw new InvalidNameException(fileName); - } + if (!NameValid(fileName)) + { + throw new InvalidNameException(fileName); + } - if (!FileExistsInternal(fileName)) - { - throw new FileNotFoundException(fileName); - } + if (!FileExistsInternal(fileName)) + { + throw new FileNotFoundException(fileName); + } - if (_externalFileManager.Exists(externalPath)) - { - throw new ExternalFileAlreadyExistsException(externalPath); - } + if (_externalFileManager.Exists(externalPath)) + { + throw new ExternalFileAlreadyExistsException(externalPath); + } - using var externalFileStream = _externalFileManager.OpenWriteStream(externalPath); - ReadInternal(fileName, externalFileStream); + using var externalFileStream = _externalFileManager.OpenWriteStream(externalPath); + ReadInternal(fileName, externalFileStream); + }); }); - - _transactionWrapper.EndTransaction(); } /// /// Throws if filename is invalid. public bool FileExists(string fileName) { - _transactionWrapper.BeginTransaction(); - - var exists = _storageOperationLocker.LockEntry(fileName, () => - { - if (!NameValid(fileName)) + return _transactionWrapper.Execute(() => + _storageOperationLocker.LockEntry(fileName, () => { - throw new InvalidNameException(fileName); - } - - return FileExistsInternal(fileName); - }); - - _transactionWrapper.EndTransaction(); + if (!NameValid(fileName)) + { + throw new InvalidNameException(fileName); + } - return exists; + return FileExistsInternal(fileName); + })); } /// /// Throws if name is invalid. public bool DirectoryExists(string fileName) { - _transactionWrapper.BeginTransaction(); - - var exists = _storageOperationLocker.LockEntry(fileName, () => - { - if (!NameValid(fileName)) + return _transactionWrapper.Execute(() => + _storageOperationLocker.LockEntry(fileName, () => { - throw new InvalidNameException(fileName); - } - - return DirectoryExistsInternal(fileName); - }); - - _transactionWrapper.EndTransaction(); + if (!NameValid(fileName)) + { + throw new InvalidNameException(fileName); + } - return exists; + return DirectoryExistsInternal(fileName); + })); } /// /// Throws if name is invalid. public bool Exists(string name) { - _transactionWrapper.BeginTransaction(); - - var exists = _storageOperationLocker.LockEntry(name, () => - { - if (!NameValid(name)) + return _transactionWrapper.Execute(() => + _storageOperationLocker.LockEntry(name, () => { - throw new InvalidNameException(name); - } - - return ExistsInternal(name); - }); - - _transactionWrapper.EndTransaction(); + if (!NameValid(name)) + { + throw new InvalidNameException(name); + } - return exists; + return ExistsInternal(name); + })); } /// @@ -629,28 +598,23 @@ public bool Exists(string name) /// Throws if directory is not found. public IEnumerable GetEntries(string directoryName) { - _transactionWrapper.BeginTransaction(); - - var result = _storageOperationLocker.LockEntry(directoryName, () => - { - if (!NameValid(directoryName)) - { - throw new InvalidNameException(directoryName); - } - - if (!DirectoryExistsInternal(directoryName)) - { - throw new DirectoryNotFoundException(directoryName); - } - - return _entryRepository - .GetEntriesInfo(directoryName) - .Where(entryInfo => entryInfo.EntryName != PathConstants.RootDirectoryName); - }); + return _transactionWrapper.Execute(() => + _storageOperationLocker.LockEntry(directoryName, () => + { + if (!NameValid(directoryName)) + { + throw new InvalidNameException(directoryName); + } - _transactionWrapper.EndTransaction(); + if (!DirectoryExistsInternal(directoryName)) + { + throw new DirectoryNotFoundException(directoryName); + } - return result; + return _entryRepository + .GetEntriesInfo(directoryName) + .Where(entryInfo => entryInfo.EntryName != PathConstants.RootDirectoryName); + })); } /// @@ -658,38 +622,28 @@ public IEnumerable GetEntries(string directoryName) /// If there is no entry with such name. public bool IsDirectory(string name) { - _transactionWrapper.BeginTransaction(); + return _transactionWrapper.Execute(() => + _storageOperationLocker.LockEntry(name, () => + { + if (!NameValid(name)) + { + throw new InvalidNameException(name); + } - var result = _storageOperationLocker.LockEntry(name, () => - { - if (!NameValid(name)) - { - throw new InvalidNameException(name); - } - - if (!ExistsInternal(name)) - { - throw new EntryNotFoundException(name); - } + if (!ExistsInternal(name)) + { + throw new EntryNotFoundException(name); + } - return _directoryRepository.Exists(name); - }); - - _transactionWrapper.EndTransaction(); - - return result; + return _directoryRepository.Exists(name); + })); } /// public int ForceOptimize() { - _transactionWrapper.BeginTransaction(); - - var bytesOptimized = _storageOperationLocker.GlobalLock(_optimizer.Optimize); - - _transactionWrapper.EndTransaction(); - - return bytesOptimized; + return _transactionWrapper.Execute( + () => _storageOperationLocker.GlobalLock(_optimizer.Optimize)); } /// @@ -914,4 +868,4 @@ private DirectoryEntry CreateDirectoryEntry(string name) return directoryEntry; } } -} \ No newline at end of file +} diff --git a/FileFS.Client/Transactions/Abstractions/ITransactionWrapper.cs b/FileFS.Client/Transactions/Abstractions/ITransactionWrapper.cs index aff195e..9b246fa 100644 --- a/FileFS.Client/Transactions/Abstractions/ITransactionWrapper.cs +++ b/FileFS.Client/Transactions/Abstractions/ITransactionWrapper.cs @@ -3,19 +3,23 @@ namespace FileFS.Client.Transactions.Abstractions { /// - /// Abstraction for transaction wrapper, service, which guaranties that all operations between and would have exclusive + /// Abstraction for transaction wrapper, service, which guarantees that operation would have exclusive /// access to FileFS storage. /// public interface ITransactionWrapper : IDisposable { /// - /// Starts new transaction to FileFS storage. + /// Executes operation with exclusive access to FileFS storage. /// - void BeginTransaction(); + /// Operation to execute. + void Execute(Action operation); /// - /// Ends transaction to FileFS storage. + /// Executes operation with exclusive access to FileFS storage. /// - void EndTransaction(); + /// Type of operation result. + /// Operation to execute. + /// Result of the operation. + T Execute(Func operation); } } \ No newline at end of file diff --git a/FileFS.Client/Transactions/NullTransactionWrapper.cs b/FileFS.Client/Transactions/NullTransactionWrapper.cs index 31178cc..566ef1c 100644 --- a/FileFS.Client/Transactions/NullTransactionWrapper.cs +++ b/FileFS.Client/Transactions/NullTransactionWrapper.cs @@ -1,4 +1,5 @@ -using System.Diagnostics.CodeAnalysis; +using System; +using System.Diagnostics.CodeAnalysis; using FileFS.Client.Transactions.Abstractions; namespace FileFS.Client.Transactions @@ -10,13 +11,15 @@ namespace FileFS.Client.Transactions public class NullTransactionWrapper : ITransactionWrapper { /// - public void BeginTransaction() + public void Execute(Action operation) { + operation(); } /// - public void EndTransaction() + public T Execute(Func operation) { + return operation(); } /// @@ -24,4 +27,4 @@ public void Dispose() { } } -} \ No newline at end of file +} diff --git a/FileFS.Client/Transactions/TransactionWrapper.cs b/FileFS.Client/Transactions/TransactionWrapper.cs index 5baceff..df1683f 100644 --- a/FileFS.Client/Transactions/TransactionWrapper.cs +++ b/FileFS.Client/Transactions/TransactionWrapper.cs @@ -10,7 +10,7 @@ namespace FileFS.Client.Transactions { /// - /// Service which guaranties that all operations between and would have exclusive + /// Service which guarantees that specified operation would have exclusive /// access to FileFS storage. /// [ExcludeFromCodeCoverage] @@ -34,18 +34,45 @@ public TransactionWrapper(string fileFsStoragePath, ILogger logger) } /// - public void BeginTransaction() + public void Execute(Action operation) { - _logger.Information($"Waiting for mutex {_mutexName}"); - _mutex.WaitOne(); - _logger.Information($"Mutex {_mutexName} acquired"); + Execute(() => + { + operation(); + return default; + }); } /// - public void EndTransaction() + public T Execute(Func operation) { - _mutex.ReleaseMutex(); - _logger.Information($"Mutex {_mutexName} released"); + _logger.Information($"Waiting for mutex {_mutexName}"); + var mutexAcquired = false; + + try + { + try + { + _mutex.WaitOne(); + mutexAcquired = true; + } + catch (AbandonedMutexException) + { + mutexAcquired = true; + throw; + } + + _logger.Information($"Mutex {_mutexName} acquired"); + return operation(); + } + finally + { + if (mutexAcquired) + { + _mutex.ReleaseMutex(); + _logger.Information($"Mutex {_mutexName} released"); + } + } } /// @@ -64,4 +91,4 @@ private static string GetMutexName(string fileFsStoragePath) return mutexName; } } -} \ No newline at end of file +}