CasCap.Api.Azure.Storage
2.0.0
Prefix Reserved
dotnet add package CasCap.Api.Azure.Storage --version 2.0.0
NuGet\Install-Package CasCap.Api.Azure.Storage -Version 2.0.0
<PackageReference Include="CasCap.Api.Azure.Storage" Version="2.0.0" />
<PackageVersion Include="CasCap.Api.Azure.Storage" Version="2.0.0" />
<PackageReference Include="CasCap.Api.Azure.Storage" />
paket add CasCap.Api.Azure.Storage --version 2.0.0
#r "nuget: CasCap.Api.Azure.Storage, 2.0.0"
#:package CasCap.Api.Azure.Storage@2.0.0
#addin nuget:?package=CasCap.Api.Azure.Storage&version=2.0.0
#tool nuget:?package=CasCap.Api.Azure.Storage&version=2.0.0
CasCap.Api.Azure.Storage
Abstract Azure Blob, Queue and Table Storage services for .NET applications. Derive a concrete
service to reuse the corresponding
IAzBlobStorageBase,
IAzQueueStorageBase
or IAzTableStorageBase implementation.
Installation
Targets net8.0, net9.0 and net10.0.
dotnet add package CasCap.Api.Azure.Storage
Migrating to 2.0
IAzBlobStorageBase
adds two required members in 2.0. Direct implementers and test doubles must add:
Task<bool> UploadBlobIfNotExistsAsync(string blobName, Stream stream, AzBlobUploadArgs? args = null, CancellationToken cancellationToken = default)Task<IDictionary<string, string>?> GetBlobMetadataAsync(string blobName, CancellationToken cancellationToken = default)
Subclasses of AzBlobStorageBase inherit both implementations and need no additional members.
Use the create-only method when overwriting is forbidden; existing UploadBlob overloads continue
to overwrite. Callers still own the upload stream. See the behavior and example below.
Usage
Supply a connection string from secret-backed application configuration:
using CasCap.Services;
public sealed class DocumentStorage(string connectionString)
: AzBlobStorageBase(connectionString, "documents")
{
}
var storage = new DocumentStorage(connectionString);
await storage.UploadBlob("example.txt", "Hello"u8.ToArray(), cancellationToken);
byte[]? bytes = await storage.DownloadBlobAsync("example.txt", cancellationToken);
The connection-string and URI/credential blob constructors synchronously create the container if it
does not exist, so constructing or resolving them performs network I/O and requires creation
permission when the container is absent. Uploads overwrite existing block blobs.
DownloadBlobAsync returns null for a missing blob and buffers its entire contents.
Conditional Streaming Reads
Pass an existing BlobContainerClient to avoid constructor-time network I/O and container provisioning, while
preserving the caller's identity, transport, retry and service-version settings:
using Azure;
using Azure.Storage.Blobs;
using CasCap.Services;
public sealed class ExistingDocuments(BlobContainerClient client) : AzBlobStorageBase(client);
var storage = new ExistingDocuments(containerClient);
await using var stream = await storage.OpenBlobReadAsync(
"example.pdf", new ETag(admittedETag), cancellationToken);
await ProcessBoundedAsync(stream, cancellationToken);
OpenBlobReadAsync requires one exact, strong entity tag: no missing tag, wildcard, weak tag or
tag list. It uses the SDK's non-modifiable read stream with If-Match on the initial metadata
request and subsequent range reads. A changed source fails with Azure status 412, including after
reading has started; no newer revision is silently substituted. Missing sources and other Azure
failures also propagate without an existence probe or a success-shaped fallback.
The caller owns disposal and must pass cancellation to subsequent reads as well as opening. The SDK uses a 64 KiB read buffer and can read ahead; this is not a total download-size limit. Apply application-specific byte/page/content limits while consuming the stream. The streaming path does not log source names or document contents.
Create-Only Uploads
Back up or archive content without ever replacing an existing blob:
using Azure.Storage.Blobs.Models;
using CasCap.Messages;
var args = new AzBlobUploadArgs
{
ContentType = "image/jpeg",
Metadata = new Dictionary<string, string> { ["sha256"] = sha256 },
AccessTier = AccessTier.Cool,
};
await using var content = File.OpenRead(path);
bool created = await storage.UploadBlobIfNotExistsAsync("photos/example.jpg", content, args, cancellationToken);
IDictionary<string, string>? metadata = await storage.GetBlobMetadataAsync("photos/example.jpg", cancellationToken);
UploadBlobIfNotExistsAsync streams the content with If-None-Match: * and returns false, leaving
the blob unchanged, when one already exists, including one created concurrently. A missing container,
authorization failure and other Azure errors propagate. GetBlobMetadataAsync returns null for a
missing blob. Neither method logs blob names or metadata.
| Base service | Selected operations | Important behavior |
|---|---|---|
AzBlobStorageBase |
UploadBlob, UploadBlobIfNotExistsAsync, GetBlobMetadataAsync, DownloadBlobAsync, OpenBlobReadAsync, ListContainerBlobs, GetBlobPrefixes, DeleteBlob |
UploadBlob overwrites; create-only uploads never overwrite; metadata returns null for a missing blob; conditional reads bind one revision; lists are materialized |
AzQueueStorageBase |
Enqueue<T>, DequeueSingle<T>, DequeueMany<T> |
JSON with Base64 queue encoding; creates the queue on first operation |
AzTableStorageBase |
GetTables, GetTableClient, UpsertEntity, UploadData, DeleteData, GetEntities |
Batch operations and BatchCompletedEvent; queries return materialized collections |
StorageExtensions |
IsKeyValid, table existence and date/key conversion helpers |
Utility extensions, not service registration |
Queue Delivery Caution
Dequeue methods deserialize and delete messages before returning them, not after your
application processes them. DequeueSingle<T> also deletes malformed JSON messages and returns a
null object with the message metadata. Use the Azure SDK directly when you need explicit
acknowledgement, poison-message handling or processing retries.
Enqueue(List<T>) returns true when at least one send succeeds; it is not an all-items-success
guarantee. A deserialization failure logs one Error with the message type, message identifier and
queue name, never the payload. Blob names, prefixes and table partition keys are not logged; a
missing blob in DownloadBlobAsync logs at Debug and returns null, and table batch failures
propagate without being logged.
Authentication and Service Versions
All three base classes have connection-string and TokenCredential constructors. The additional
Blob client constructor above does not provision resources. Token constructors
take the appropriate service URI; Blob and Queue also take a container/queue name, which is appended
to that URI. Table takes a table-service endpoint. Grant the caller the storage data-plane roles
needed for its reads, writes and resource creation.
There is no options model or DI registration helper. Each base constructor accepts an optional SDK
ServiceVersion?, useful when an emulator supports an older service API:
using Azure.Storage.Blobs;
using CasCap.Services;
public sealed class EmulatorDocuments(string connectionString)
: AzBlobStorageBase(
connectionString, "documents", BlobClientOptions.ServiceVersion.V2024_11_04)
{
}
Omitting the version uses that SDK's default; it does not negotiate emulator compatibility. Store connection strings outside tracked configuration.
Documentation
| Product | Versions 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. |
-
net10.0
- Azure.Core (>= 1.64.0)
- Azure.Data.Tables (>= 12.13.0)
- Azure.Storage.Blobs (>= 12.30.1)
- Azure.Storage.Queues (>= 12.28.1)
- CasCap.Common.Extensions (>= 5.1.0)
- CasCap.Common.Logging (>= 5.1.0)
- CasCap.Common.Serialization.Json (>= 5.1.0)
-
net8.0
- Azure.Core (>= 1.64.0)
- Azure.Data.Tables (>= 12.13.0)
- Azure.Storage.Blobs (>= 12.30.1)
- Azure.Storage.Queues (>= 12.28.1)
- CasCap.Common.Extensions (>= 5.1.0)
- CasCap.Common.Logging (>= 5.1.0)
- CasCap.Common.Serialization.Json (>= 5.1.0)
- System.Linq.Async (>= 7.0.1)
-
net9.0
- Azure.Core (>= 1.64.0)
- Azure.Data.Tables (>= 12.13.0)
- Azure.Storage.Blobs (>= 12.30.1)
- Azure.Storage.Queues (>= 12.28.1)
- CasCap.Common.Extensions (>= 5.1.0)
- CasCap.Common.Logging (>= 5.1.0)
- CasCap.Common.Serialization.Json (>= 5.1.0)
- System.Linq.Async (>= 7.0.1)
NuGet packages (4)
Showing the top 4 NuGet packages that depend on CasCap.Api.Azure.Storage:
| Package | Downloads |
|---|---|
|
CasCap.Api.DoorBird.Sinks
Pluggable event sink implementations (Redis, Azure Tables, Azure Blob Storage) for the CasCap.Api.DoorBird door station integration library. |
|
|
CasCap.Api.Fronius.Sinks
Pluggable event sink implementations (Redis, Azure Tables) for the CasCap.Api.Fronius solar inverter integration library. |
|
|
CasCap.Api.Buderus.Sinks
Pluggable event sink implementations (Redis, Azure Tables) for the CasCap.Api.Buderus heating integration library. |
|
|
CasCap.Api.Knx.Sinks
Pluggable event sink implementations (Redis, Azure Tables) for the CasCap.Api.Knx building automation integration library. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2.0.0 | 38 | 11/5/2026 |
| 1.8.0 | 95 | 11/1/2026 |
| 1.7.1 | 240 | 10/24/2026 |
| 1.7.0 | 137 | 10/21/2026 |
| 1.6.4 | 176 | 10/4/2026 |
| 1.6.3 | 196 | 9/25/2026 |
| 1.6.2 | 188 | 9/23/2026 |
| 1.6.1 | 144 | 9/21/2026 |
| 1.6.0 | 302 | 9/19/2026 |
| 1.5.5 | 249 | 9/11/2026 |
| 1.5.4 | 133 | 9/1/2026 |
| 1.5.3 | 214 | 8/18/2026 |
| 1.5.2 | 244 | 7/15/2026 |
| 1.5.1 | 252 | 6/26/2026 |
| 1.5.0 | 332 | 6/12/2026 |
| 1.4.13 | 235 | 6/10/2026 |
| 1.4.12 | 248 | 5/27/2026 |
| 1.4.11 | 224 | 5/26/2026 |
| 1.4.10 | 180 | 5/21/2026 |
| 1.4.9 | 162 | 5/20/2026 |