Benday.AzureStorage 1.0.1-alpha

This is a prerelease version of Benday.AzureStorage.
dotnet add package Benday.AzureStorage --version 1.0.1-alpha
                    
NuGet\Install-Package Benday.AzureStorage -Version 1.0.1-alpha
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Benday.AzureStorage" Version="1.0.1-alpha" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Benday.AzureStorage" Version="1.0.1-alpha" />
                    
Directory.Packages.props
<PackageReference Include="Benday.AzureStorage" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Benday.AzureStorage --version 1.0.1-alpha
                    
#r "nuget: Benday.AzureStorage, 1.0.1-alpha"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Benday.AzureStorage@1.0.1-alpha
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Benday.AzureStorage&version=1.0.1-alpha&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Benday.AzureStorage&version=1.0.1-alpha&prerelease
                    
Install as a Cake Tool

Benday.AzureStorage

Simplified Azure Storage access for .NET -- blob repositories, Table Storage repositories, typed queue clients, queue health checks, and DI helpers.

Targets .NET 8.0, 9.0, and 10.0.

Written by Benjamin Day Pluralsight Author | Microsoft MVP https://www.benday.com https://www.honestcheetah.com info@benday.com YouTube: https://www.youtube.com/@_benday

Installation

dotnet add package Benday.AzureStorage --version 1.0.0-alpha

Configuration

Add an AzureStorage section to your appsettings.json:

{
  "AzureStorage": {
    "ConnectionString": "your-connection-string",
    "CreateStructures": true
  }
}

For local development with Azurite:

{
  "AzureStorage": {
    "UseDevelopmentStorage": true,
    "CreateStructures": true
  }
}

For Managed Identity (DefaultAzureCredential):

{
  "AzureStorage": {
    "AccountName": "mystorageaccount",
    "UseDefaultAzureCredential": true,
    "CreateStructures": true
  }
}

Setting CreateStructures to true automatically creates containers, tables, and queues if they don't exist.

Service Registration

Register core Azure Storage services in Program.cs:

builder.Services.AddBendayAzureStorage(builder.Configuration);

This registers AzureStorageConfig, BlobServiceClient, TableServiceClient, QueueClientFactory, and IMimeTypeUtil as singletons.

Then register the specific resources you need:

// Blob container
builder.Services.AddBlobRepository("my-container");

// Table Storage repository
builder.Services.AddTableRepository<CustomerRepository, CustomerEntity>();

// Typed queue
builder.Services.AddTypedQueue<OrderMessage>("order-queue", opts =>
{
    opts.MaxDequeueCount = 3;
});

Table Storage

Define an Entity

Table Storage entities must implement both ITableEntity (from Azure.Data.Tables) and IEntityIdentity<string> (from Benday.Common.Interfaces):

using Azure;
using Azure.Data.Tables;
using Benday.Common.Interfaces;

public class CustomerEntity : ITableEntity, IEntityIdentity<string>
{
    public string Id { get; set; } = string.Empty;
    public string PartitionKey { get; set; } = "default";
    public string RowKey { get; set; } = string.Empty;
    public DateTimeOffset? Timestamp { get; set; }
    public ETag ETag { get; set; }

    // Your custom properties
    public string Name { get; set; } = string.Empty;
    public string Email { get; set; } = string.Empty;
    public string Region { get; set; } = string.Empty;
}

Create a Repository

Extend TableRepository<T> and provide the table name:

using Benday.AzureStorage.Tables;

public class CustomerRepository : TableRepository<CustomerEntity>
{
    public CustomerRepository(
        TableServiceClient serviceClient,
        AzureStorageConfig config,
        ILogger<CustomerRepository> logger)
        : base(serviceClient, config, logger)
    {
    }

    protected override string TableName => "customers";

    // Optional: override the default partition key
    protected override string DefaultPartitionKey => "default";
}

Use the Repository

public class CustomerService
{
    private readonly ITableRepository<CustomerEntity> _repo;

    public CustomerService(ITableRepository<CustomerEntity> repo)
    {
        _repo = repo;
    }

    public async Task CreateCustomerAsync(string name, string email, string region)
    {
        var customer = new CustomerEntity
        {
            Id = Guid.NewGuid().ToString(),
            RowKey = Guid.NewGuid().ToString(),
            PartitionKey = region,
            Name = name,
            Email = email,
            Region = region
        };

        await _repo.SaveAsync(customer);
    }

    public async Task<CustomerEntity?> GetCustomerAsync(string region, string id)
    {
        return await _repo.GetByIdAsync(partitionKey: region, rowKey: id);
    }

    public async Task<IList<CustomerEntity>> GetCustomersByRegionAsync(string region)
    {
        return await _repo.GetAllAsync(partitionKey: region);
    }

    public async Task<IList<CustomerEntity>> SearchByNameAsync(string namePrefix)
    {
        return await _repo.QueryAsync(c => c.Name.CompareTo(namePrefix) >= 0);
    }

    public async Task DeleteCustomerAsync(string region, string id)
    {
        await _repo.DeleteAsync(partitionKey: region, rowKey: id);
    }
}

The overloads that take only a string id (no partition key) use the DefaultPartitionKey value automatically.

Blob Repository

public class ReportService
{
    private readonly IBlobRepository _blobs;

    public ReportService(IBlobRepository blobs)
    {
        _blobs = blobs;
    }

    public async Task<string> UploadReportAsync(string path, Stream content)
    {
        return await _blobs.UploadAsync(path, content);
    }

    public async Task<byte[]> DownloadReportAsync(string path)
    {
        return await _blobs.DownloadBytesAsync(path);
    }

    public async Task<bool> ReportExistsAsync(string path)
    {
        return await _blobs.ExistsAsync(path);
    }

    public Uri GetReportUrl(string path)
    {
        return _blobs.GetSasUri(path, expiry: TimeSpan.FromHours(1));
    }
}

Typed Queues

public class OrderMessage
{
    public string OrderId { get; set; } = string.Empty;
    public string CustomerId { get; set; } = string.Empty;
    public decimal Total { get; set; }
}
public class OrderQueueService
{
    private readonly TypedQueueClient<OrderMessage> _queue;

    public OrderQueueService(TypedQueueClient<OrderMessage> queue)
    {
        _queue = queue;
    }

    public async Task EnqueueOrderAsync(string orderId, string customerId, decimal total)
    {
        await _queue.SendAsync(new OrderMessage
        {
            OrderId = orderId,
            CustomerId = customerId,
            Total = total
        });
    }

    public async Task ProcessNextAsync()
    {
        var message = await _queue.ReceiveAsync(visibilityTimeout: TimeSpan.FromMinutes(5));

        if (message == null)
            return; // Queue is empty

        // Check for poison messages first
        if (await _queue.MoveToPoisonIfNeededAsync(message))
            return; // Moved to poison queue

        try
        {
            Console.WriteLine($"Processing order {message.Body!.OrderId}");
            await _queue.CompleteAsync(message);
        }
        catch
        {
            // Release back to queue for retry
            await _queue.ReleaseAsync(message, visibilityDelay: TimeSpan.FromSeconds(30));
            throw;
        }
    }
}

Messages are JSON-serialized and Base64-encoded automatically. Messages that exceed MaxDequeueCount are moved to a poison queue (default name: {queueName}-poison).

Health Checks

Monitor queue depth with ASP.NET Core health checks:

builder.Services.AddHealthChecks()
    .AddAzureQueueCheck("order-queue-health", opts =>
    {
        opts.QueueName = "order-queue";
        opts.DegradedThreshold = 50;    // Degraded when >= 50 messages
        opts.UnhealthyThreshold = 200;  // Unhealthy when >= 200 messages
        opts.PoisonIsUnhealthy = true;  // Any poison messages = unhealthy
    });

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Benday.AzureStorage:

Package Downloads
Benday.BlobStorage

Connects entities with blob attachments in Azure Storage. Works with any storage backend — Cosmos DB, Table Storage, EF Core — as long as the entity implements IBlobOwner.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1-alpha 30 4/4/2026
1.0.0-alpha 37 4/3/2026

Initial alpha release. Blob repositories, Table Storage repositories, typed queue clients, queue health checks, and DI helpers for Azure Storage.