Benday.AzureStorage 1.2.0

dotnet add package Benday.AzureStorage --version 1.2.0
                    
NuGet\Install-Package Benday.AzureStorage -Version 1.2.0
                    
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.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Benday.AzureStorage" Version="1.2.0" />
                    
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.2.0
                    
#r "nuget: Benday.AzureStorage, 1.2.0"
                    
#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.2.0
                    
#: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.2.0
                    
Install as a Cake Addin
#tool nuget:?package=Benday.AzureStorage&version=1.2.0
                    
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.2.0 39 7/3/2026
1.1.0-alpha 73 5/15/2026
1.0.1-alpha 86 4/4/2026
1.0.0-alpha 111 4/3/2026

Merged the former Benday.BlobStorage package into Benday.AzureStorage. BlobBridge (for connecting IBlobOwner entities to blob attachments) now lives in the Benday.AzureStorage.Blobs namespace. Also includes blob repositories, Table Storage repositories, typed queue clients, queue health checks, and DI helpers.