Modern Azure Blob Storage Helper for Dynamics 365 Finance & Operations – Fully Compatible with Latest SDK

As Microsoft continues to evolve the Azure SDKs, developers working with Dynamics 365 Finance and Operations (D365FO) need updated and future-proof approaches for managing blob storage operations — especially when integrating with external C# libraries via .NET assemblies.
In this article, I’ll introduce a modern C# helper class built using the new Azure.Storage.Blobs SDK (which replaces the deprecated Microsoft.Azure.Storage.* libraries). This helper is designed to be used from X++ code via a reference to a compiled .NET assembly.

✅ Why Use This Helper?

The AzureBlobStorageHelper class is:

  • 100% compliant with the latest Azure.Storage.Blobs SDK.
  • Designed with compatibility in mind for X++ runtime usage in D365FO.
  • Lightweight, reliable, and free from deprecated APIs.
  • Covers all common blob operations: upload, download, delete, copy, and list.
  • Provides advanced support for working with blobs as MemoryStream, byte[], or directly via BlobClient.

🔧 Setup: C# Helper Class

The core class is defined in a .NET Framework Class Library project (recommended: .NET Framework 4.7.2 for compatibility). Here’s the signature:

public class AzureBlobStorageHelper
{
    public AzureBlobStorageHelper(string connectionString);
    
    public bool UploadBlobFromString(string containerName, string blobName, string content);
    public string DownloadBlobAsString(string containerName, string blobName);
    public bool DeleteBlob(string containerName, string blobName);
    public List<BlobClient> ListBlobsAsBlobClient(string containerName, string prefix);
    public MemoryStream DownloadBlobAsMemoryStream(string containerName, string blobName);
    public bool UploadBlobFromMemoryStream(string containerName, string blobName, MemoryStream stream);
    // ... and many more
}

The class internally uses:

using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;

This ensures compatibility with all current and future Azure Storage enhancements.

📄 Full Source Code – AzureBlobStorageHelper.cs

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Azure;

namespace AzureBlobStorageHelperNS
{
    /// <summary>
    /// Helper class per operazioni Azure Storage da utilizzare con X++
    /// </summary>
    public class AzureBlobStorageHelper
    {
        private readonly BlobServiceClient _blobServiceClient;
        private readonly string _connectionString;

        /// <summary>
        /// Costruttore con connection string
        /// </summary>
        /// <param name="connectionString">Connection string di Azure Storage</param>
        public AzureBlobStorageHelper(string connectionString)
        {
            _connectionString = connectionString;
            _blobServiceClient = new BlobServiceClient(connectionString);
        }

        /// <summary>
        /// Verifica se un container esiste
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <returns>True se il container esiste</returns>
        public bool ContainerExists(string containerName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                return containerClient.Exists();
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel verificare l'esistenza del container: {ex.Message}");
            }
        }

        /// <summary>
        /// Crea un container se non esiste
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <returns>True se il container è stato creato o esisteva già</returns>
        public bool CreateContainerIfNotExists(string containerName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var response = containerClient.CreateIfNotExists(PublicAccessType.None);
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nella creazione del container: {ex.Message}");
            }
        }

        /// <summary>
        /// Carica un blob da stringa
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <param name="content">Contenuto da caricare</param>
        /// <returns>True se il caricamento è riuscito</returns>
        public bool UploadBlobFromString(string containerName, string blobName, string content)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(content)))
                {
                    blobClient.Upload(stream, overwrite: true);
                }

                return true;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel caricamento del blob: {ex.Message}");
            }
        }

        /// <summary>
        /// Carica un blob da file
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <param name="filePath">Percorso del file locale</param>
        /// <returns>True se il caricamento è riuscito</returns>
        public bool UploadBlobFromFile(string containerName, string blobName, string filePath)
        {
            try
            {
                if (!File.Exists(filePath))
                {
                    throw new FileNotFoundException($"File non trovato: {filePath}");
                }

                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                blobClient.Upload(filePath, overwrite: true);

                return true;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel caricamento del blob da file: {ex.Message}");
            }
        }

        /// <summary>
        /// Scarica un blob come stringa
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <returns>Contenuto del blob come stringa</returns>
        public string DownloadBlobAsString(string containerName, string blobName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                if (!blobClient.Exists())
                {
                    throw new Exception($"Blob non trovato: {blobName}");
                }

                var response = blobClient.DownloadContent();
                return response.Value.Content.ToString();
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel download del blob: {ex.Message}");
            }
        }

        /// <summary>
        /// Scarica un blob in un file
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <param name="downloadPath">Percorso dove salvare il file</param>
        /// <returns>True se il download è riuscito</returns>
        public bool DownloadBlobToFile(string containerName, string blobName, string downloadPath)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                if (!blobClient.Exists())
                {
                    throw new Exception($"Blob non trovato: {blobName}");
                }

                blobClient.DownloadTo(downloadPath);
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel download del blob su file: {ex.Message}");
            }
        }

        /// <summary>
        /// Elimina un blob
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <returns>True se l'eliminazione è riuscita</returns>
        public bool DeleteBlob(string containerName, string blobName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                // Verifica l'esistenza del blob
                var exists = blobClient.Exists();

                if (!exists.Value)
                {
                    return false; // Il blob non esiste
                }

                // Elimina il blob
                blobClient.Delete();
                return true;
            }
            catch (RequestFailedException ex)
            {
                // Gestione specifica per Azure
                throw new Exception($"Errore Azure durante eliminazione blob: {ex.Message}");
            }
            catch (Exception ex)
            {
                // Gestione generica
                throw new Exception($"Errore generico durante eliminazione blob: {ex.Message}");
            }
        }

        /// <summary>
        /// Verifica se un blob esiste
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <returns>True se il blob esiste</returns>
        public bool BlobExists(string containerName, string blobName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                return blobClient.Exists();
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel verificare l'esistenza del blob: {ex.Message}");
            }
        }

        /// <summary>
        /// Lista tutti i blob in un container
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <returns>Lista dei nomi dei blob</returns>
        public List<string> ListBlobs(string containerName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobNames = new List<string>();

                foreach (var blobItem in containerClient.GetBlobs())
                {
                    blobNames.Add(blobItem.Name);
                }

                return blobNames;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel listare i blob: {ex.Message}");
            }
        }

        /// <summary>
        /// Lista i blob in un container con un prefisso specificato e restituisce i blob come MemoryStream
        /// </summary>
        /// <param name="containerName"></param>
        /// <param name="prefix"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        public List<MemoryStream> ListBlobsAsMemoryStreams(string containerName, string prefix)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var memoryStreams = new List<MemoryStream>();

                IEnumerable<BlobItem> blobs = containerClient.GetBlobs(prefix: prefix);

                // Itera e mostra i blob trovati
                System.Collections.IEnumerator enumerator = blobs.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    BlobItem blobItem = enumerator.Current as BlobItem;
                    if (blobItem != null)
                    {
                        var blobClient = containerClient.GetBlobClient(blobItem.Name); 
                        if (blobClient.Exists())
                        {
                            var memoryStream = new MemoryStream();
                            blobClient.DownloadTo(memoryStream);
                            memoryStream.Position = 0; // Reset position for reading
                            memoryStreams.Add(memoryStream);
                        }
                    }
                }
                return memoryStreams;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel listare i blob come MemoryStream: {ex.Message}");
            }
        }



        public List<BlobClient> ListBlobsAsBlobClient(string containerName, string prefix)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClients = new List<BlobClient>();

                IEnumerable<BlobItem> blobs = containerClient.GetBlobs(prefix: prefix);

                // Itera e mostra i blob trovati
                System.Collections.IEnumerator enumerator = blobs.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    BlobItem blobItem = enumerator.Current as BlobItem;
                    if (blobItem != null)
                    {
                        var blobClient = containerClient.GetBlobClient(blobItem.Name);
                        if (blobClient.Exists())
                        {
                            blobClients.Add(blobClient);
                        }
                    }
                }
                return blobClients;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel listare i blob come BlobClient: {ex.Message}");
            }
        }



        /// <summary>
        /// Ottiene le proprietà di un blob
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <returns>Stringa con le proprietà del blob</returns>
        public string GetBlobProperties(string containerName, string blobName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                if (!blobClient.Exists())
                {
                    throw new Exception($"Blob non trovato: {blobName}");
                }

                var properties = blobClient.GetProperties();
                var props = properties.Value;

                return $"ContentType: {props.ContentType}, " +
                       $"ContentLength: {props.ContentLength}, " +
                       $"LastModified: {props.LastModified}, " +
                       $"ETag: {props.ETag}";
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel recuperare le proprietà del blob: {ex.Message}");
            }
        }

        /// <summary>
        /// Scarica un blob come MemoryStream
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <returns>MemoryStream contenente i dati del blob</returns>
        public MemoryStream DownloadBlobAsMemoryStream(string containerName, string blobName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                if (!blobClient.Exists())
                {
                    throw new Exception($"Blob non trovato: {blobName}");
                }

                var memoryStream = new MemoryStream();
                blobClient.DownloadTo(memoryStream);
                memoryStream.Position = 0; // Reset position per la lettura

                return memoryStream;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel download del blob come MemoryStream: {ex.Message}");
            }
        }

        /// <summary>
        /// Scarica un blob come array di byte
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <returns>Array di byte contenente i dati del blob</returns>
        public byte[] DownloadBlobAsBytes(string containerName, string blobName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                if (!blobClient.Exists())
                {
                    throw new Exception($"Blob non trovato: {blobName}");
                }

                using (var memoryStream = new MemoryStream())
                {
                    blobClient.DownloadTo(memoryStream);
                    return memoryStream.ToArray();
                }
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel download del blob come byte array: {ex.Message}");
            }
        }

        /// <summary>
        /// Carica un blob da MemoryStream
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <param name="stream">MemoryStream contenente i dati da caricare</param>
        /// <returns>True se il caricamento è riuscito</returns>
        public bool UploadBlobFromMemoryStream(string containerName, string blobName, MemoryStream stream)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                stream.Position = 0; // Reset position per la lettura
                blobClient.Upload(stream, overwrite: true);

                return true;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel caricamento del blob da MemoryStream: {ex.Message}");
            }
        }

        /// <summary>
        /// Carica un blob da array di byte
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <param name="data">Array di byte contenente i dati da caricare</param>
        /// <returns>True se il caricamento è riuscito</returns>
        public bool UploadBlobFromBytes(string containerName, string blobName, byte[] data)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                using (var stream = new MemoryStream(data))
                {
                    blobClient.Upload(stream, overwrite: true);
                }

                return true;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel caricamento del blob da byte array: {ex.Message}");
            }
        }

        /// <summary>
        /// Ottiene la dimensione di un blob in byte
        /// </summary>
        /// <param name="containerName">Nome del container</param>
        /// <param name="blobName">Nome del blob</param>
        /// <returns>Dimensione del blob in byte</returns>
        public long GetBlobSize(string containerName, string blobName)
        {
            try
            {
                var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
                var blobClient = containerClient.GetBlobClient(blobName);

                if (!blobClient.Exists())
                {
                    throw new Exception($"Blob non trovato: {blobName}");
                }

                var properties = blobClient.GetProperties();
                return properties.Value.ContentLength;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nel recuperare la dimensione del blob: {ex.Message}");
            }
        }

        /// <summary>
        /// Copia un blob
        /// </summary>
        /// <param name="sourceContainerName">Container sorgente</param>
        /// <param name="sourceBlobName">Nome blob sorgente</param>
        /// <param name="destinationContainerName">Container destinazione</param>
        /// <param name="destinationBlobName">Nome blob destinazione</param>
        /// <returns>True se la copia è riuscita</returns>
        public bool CopyBlob(string sourceContainerName, string sourceBlobName,
                           string destinationContainerName, string destinationBlobName)
        {
            try
            {
                var sourceContainerClient = _blobServiceClient.GetBlobContainerClient(sourceContainerName);
                var sourceBlobClient = sourceContainerClient.GetBlobClient(sourceBlobName);

                var destinationContainerClient = _blobServiceClient.GetBlobContainerClient(destinationContainerName);
                var destinationBlobClient = destinationContainerClient.GetBlobClient(destinationBlobName);

                if (!sourceBlobClient.Exists())
                {
                    throw new Exception($"Blob sorgente non trovato: {sourceBlobName}");
                }

                destinationBlobClient.StartCopyFromUri(sourceBlobClient.Uri);
                return true;
            }
            catch (Exception ex)
            {
                throw new Exception($"Errore nella copia del blob: {ex.Message}");
            }
        }
    }
}

🧩How to Use in Dynamics 365 F&O (X++)

Once the helper DLL is compiled and referenced from D365FO (via Visual Studio), you can call it from X++.

Here’s a full example that:

  1. Lists blobs by prefix.
  2. Downloads each blob.
  3. Stores it in D365FO using SharedServiceUnitStorage.
  4. Deletes the blob from Azure after processing.
AzureBlobStorageHelper azureHelper = new  AzureBlobStorageHelper("azureStorageConnectionString");
info('Azure Storage Helper initialized');
System.Collections.IEnumerable blobMemoryList = azureHelper.ListBlobsAsBlobClient("azureStorageContainerName", "azureStorageContainerPrefix");
System.Collections.IEnumerator lstBlobEnumerator = blobMemoryList.GetEnumerator();
while (lstBlobEnumerator.MoveNext())
{
    BlobClient blobClientImp = lstBlobEnumerator.Current as BlobClient;

    using (var dataStream = new System.IO.MemoryStream())
    {
        blobClientImp.DownloadTo(dataStream);
        dataStream.Position = 0; // Reset the stream position to the beginning                        

        StorageContext storageContext = SharedServiceUnitStorage::GetDefaultStorageContext();
        SharedServiceUnitStorage blobStorageService = new SharedServiceUnitStorage(storageContext);

        var blobInfo = new SharedServiceUnitStorageData();
        blobInfo.Id = guid2Str(newGuid());
        blobInfo.Category = destinationStorageFolder;
        blobInfo.Name = System.IO.Path::GetFileName(blobClientImp.Name);
        blobInfo.Accessibility = Accessibility::Public;
        blobInfo.Retention = Retention::Permanent;
 
        blobStorageService.UploadData(blobInfo, dataStream);
        info(strFmt("Upload %1 XML succeeded.", System.IO.Path::GetFileName(blobClientImp.Name)));

        // Delete the blob after processing
        str azureStorageContainerNameStr = azureStorageContainerName;
        azureHelper.DeleteBlob(azureStorageContainerNameStr, blobClientImp.Name);
    }
}

🔄Replacing Deprecated APIs

Microsoft officially deprecated the Microsoft.Azure.Storage.Blob and WindowsAzure.Storage libraries. The new Azure.Storage.Blobs SDK is the only one receiving security updates and feature enhancements.

DeprecatedUse Instead
CloudBlobClientBlobServiceClient
CloudBlobContainerBlobContainerClient
CloudBlockBlobBlobClient

With this helper, your code is already future-ready and doesn’t require rewriting once the old SDKs are fully phased out.

🚀Benefits for D365FO Developers

  • Seamless integration with X++.
  • Simplified .NET assembly deployment.
  • No need to manage low-level HTTP requests or shared access signatures.
  • Easy testing and extension in Visual Studio.
  • Makes your solution Azure-native and future-proof.

📝 Conclusion

Whether you’re building integration points, document import/export pipelines, or automated processing of files in Azure from D365FO, this helper class gives you a robust foundation that aligns with Microsoft’s latest standards.

Lascia un commento