My application using Azure SQL and Azure Blob Storage for some business requirements, most of the case need to support Atomic Transaction for both DB & Blob, if DB entry fails should rollback Blob as well(go all or no go), for DB side can use TransactionScope but Blob don't have any direct options, so decided to go 2 phase commit with help of IEnlistmentNotification interface, it working as expected but am trying to created common class/implementation to support all operations or at least few most used operations in Blob storage(upload, delete, SetMetadata ...), I don't get any idea about how to create some implementation, is this possible and any code samples available will help me lot.
Resource Manager
public class AzureBlobStorageResourceManager : IEnlistmentNotification, IDisposable
{
private List<AzureBlobStore> _operations;
private bool _disposedValue;
public void EnlistOperation(AzureBlobStore operation)
{
if (_operations is null)
{
var currentTransaction = Transaction.Current;
currentTransaction?.EnlistVolatile(this, EnlistmentOptions.None);
_operations = new List<AzureBlobStore>();
}
_operations.Add(operation);
}
public void Commit(Enlistment enlistment)
{
foreach (var blobOperation in _operations)
{
blobOperation.Dispose();
}
enlistment.Done();
}
public void InDoubt(Enlistment enlistment)
{
foreach (var blobOperation in _operations)
{
blobOperation.RollBack().ConfigureAwait(false);
}
enlistment.Done();
}
public void Prepare(PreparingEnlistment preparingEnlistment)
{
try
{
foreach (var blobOperation in _operations)
{
blobOperation.DoWork().ConfigureAwait(false);
}
preparingEnlistment.Prepared();
}
catch
{
preparingEnlistment.ForceRollback();
}
}
public void Rollback(Enlistment enlistment)
{
foreach (var blobOperation in _operations)
{
blobOperation.RollBack().ConfigureAwait(false);
}
enlistment.Done();
}
public void Dispose() => Dispose(true);
protected virtual void Dispose(bool disposing)
{
if (_disposedValue) return;
if (disposing)
{
foreach (var operation in _operations)
operation.Dispose();
}
_disposedValue = true;
}
~AzureBlobStorageResourceManager() => Dispose(false);
}
Actual Blob Operation
public class AzureBlobStore : IDisposable
{
private string _backupPath;
private readonly string _blobName;
private Stream _content;
private bool _disposedValue;
private BlobClient _blobClient;
public AzureBlobStore(BlobContainerClient containerClient, string blobName, Stream content)
{
(_blobName, _content, _blobClient) = (blobName, content, containerClient.GetBlobClient(blobName));
}
public async Task DoWork()
{
_content.Position = 0;
await _blobClient.UploadAsync(_content).ConfigureAwait(false);
/*
await _blobClient.DeleteAsync(Azure.Storage.Blobs.Models.DeleteSnapshotsOption.IncludeSnapshots).ConfigureAwait(false);
*/
}
public async Task RollBack()
{
// Compensation logic for Upload
await _blobClient.DeleteIfExistsAsync(Azure.Storage.Blobs.Models.DeleteSnapshotsOption.IncludeSnapshots).ConfigureAwait(false);
// Compensation logic for Delete
/* await _blobClient.UploadAsync(_backupPath); */
}
public void Dispose() => Dispose(true);
protected virtual void Dispose(bool disposing)
{
if (_disposedValue) return;
if (disposing)
{
_blobClient.DeleteIfExistsAsync(Azure.Storage.Blobs.Models.DeleteSnapshotsOption.IncludeSnapshots);
}
_disposedValue = true;
}
~AzureBlobStore() => Dispose(false);
}
Code inside /* */ is another one Blob operation, am looking for common way to solve this.
Here you have a sample of how to implement a BlobStorage solution integrated with .NET Transactions.
First you have the Resource Manager. This handles all the operations and rollback if the transaction fails. It is almost the same as you have.
Then you have the normal BlobStorage as you would have with no transactions, but with some tweaks to use transactional operations.
Then you have each operation separated in one class each. Example:
The usage is like this:
I think you are getting confused in the AzureBlobStore class, where you are trying to have several operations with their respective rollbacks. I think it's better to have them separated.
More information/full sample https://github.com/michaeltg17/TransactionalBlobStorage