Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Foundatio.AzureStorage.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@
<File Path="docker-compose.yml" />
<File Path="README.md" />
<File Path="tests/Directory.Build.props" />
<File Path="samples/Directory.Build.props" />
</Folder>
<Folder Name="/Tests/">
<Project Path="tests/Foundatio.AzureStorage.Tests/Foundatio.AzureStorage.Tests.csproj" />
</Folder>
<Folder Name="/Samples/">
<Project Path="samples/Foundatio.AzureStorage.Enqueue/Foundatio.AzureStorage.Enqueue.csproj" />
<Project Path="samples/Foundatio.AzureStorage.Dequeue/Foundatio.AzureStorage.Dequeue.csproj" />
</Folder>
<Project Path="src/Foundatio.AzureStorage/Foundatio.AzureStorage.csproj" />
</Solution>
2 changes: 1 addition & 1 deletion build/common.props
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
<PackageReference Include="AsyncFixer" Version="2.1.0" PrivateAssets="All" />
<PackageReference Include="MinVer" Version="6.1.0" PrivateAssets="All" />
<PackageReference Include="MinVer" Version="7.0.0" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
services:
azurite:
image: mcr.microsoft.com/azure-storage/azurite:3.35.0
command: azurite --skipApiVersionCheck --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0
ports:
- 10000:10000
- 10001:10001
Expand Down
8 changes: 8 additions & 0 deletions samples/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>
<Import Project="..\build\common.props" />
<PropertyGroup>
<TargetFrameworks>net8.0</TargetFrameworks>
<OutputType>Exe</OutputType>
<IsPackable>False</IsPackable>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\..\src\Foundatio.AzureStorage\Foundatio.AzureStorage.csproj" />
<ProjectReference Include="..\Foundatio.AzureStorage.Enqueue\Foundatio.AzureStorage.Enqueue.csproj" />
<PackageReference Include="System.CommandLine" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
</ItemGroup>
</Project>
156 changes: 156 additions & 0 deletions samples/Foundatio.AzureStorage.Dequeue/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
using System;
using System.CommandLine;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Foundatio.AzureStorage.Samples;
using Foundatio.Queues;
using Microsoft.Extensions.Logging;

// Azure Storage Emulator connection string
const string EmulatorConnectionString = "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;QueueEndpoint=http://localhost:10001/devstoreaccount1;";

// Define options
var connectionStringOption = new Option<string>("--connection-string", "-c")
{
Description = "Azure Storage connection string (defaults to emulator)"
};

var queueOption = new Option<string>("--queue", "-q")
{
Description = "Queue name",
DefaultValueFactory = _ => "sample-queue"
};

var modeOption = new Option<AzureStorageQueueCompatibilityMode>("--mode")
{
Description = "Compatibility mode (Default or Legacy)",
DefaultValueFactory = _ => AzureStorageQueueCompatibilityMode.Default
};

var countOption = new Option<int>("--count")
{
Description = "Number of messages to process (0 = infinite)",
DefaultValueFactory = _ => 1
};

// Create root command
var rootCommand = new RootCommand("Azure Storage Queue Dequeue Sample");
rootCommand.Options.Add(connectionStringOption);
rootCommand.Options.Add(queueOption);
rootCommand.Options.Add(modeOption);
rootCommand.Options.Add(countOption);

// Set handler
rootCommand.SetAction(async parseResult =>
{
var connectionString = parseResult.GetValue(connectionStringOption) ??
Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING") ??
EmulatorConnectionString;

var queueName = parseResult.GetValue(queueOption);
var mode = parseResult.GetValue(modeOption);
var count = parseResult.GetValue(countOption);

Console.WriteLine($"Using connection: {(connectionString == EmulatorConnectionString ? "Azure Storage Emulator" : "Custom connection string")}");
Console.WriteLine($"Mode: {mode}");
Console.WriteLine($"Queue: {queueName}");
Console.WriteLine($"To process: {(count == 0 ? "infinite messages" : $"{count} message(s)")}");
Console.WriteLine();
Console.WriteLine("Press Ctrl+C to stop...");
Console.WriteLine();

await DequeueMessages(connectionString, queueName, mode, count);
return 0;
});

// Parse and invoke
return await rootCommand.Parse(args).InvokeAsync();

static async Task DequeueMessages(string connectionString, string queueName, AzureStorageQueueCompatibilityMode mode, int count)
{
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Information));
var logger = loggerFactory.CreateLogger("Dequeue");
using var cts = new CancellationTokenSource();

Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
try
{
cts.Cancel();
}
catch
{
// ignored
}

logger.LogInformation("Cancellation requested...");
};

logger.LogInformation("Creating queue with mode: {Mode}", mode);

using var queue = new AzureStorageQueue<SampleMessage>(options => options
.ConnectionString(connectionString)
.Name(queueName)
.CompatibilityMode(mode)
.LoggerFactory(loggerFactory));

int processed = 0;
bool infinite = count == 0;

logger.LogInformation("Waiting for messages... (Press Ctrl+C to stop)");

try
{
while (!cts.Token.IsCancellationRequested && (infinite || processed < count))
{
var entry = await queue.DequeueAsync(cts.Token);

if (entry == null)
{
if (!infinite && processed >= count)
break;

continue;
}

try
{
processed++;

logger.LogInformation("Dequeued message {MessageId}: '{Message}' from '{Source}' at {Timestamp}",
entry.Id, entry.Value.Message, entry.Value.Source, entry.Value.Timestamp);

logger.LogInformation(" CorrelationId: '{CorrelationId}'", entry.CorrelationId ?? "<none>");

if (entry.Properties != null && entry.Properties.Count > 0)
{
logger.LogInformation(" Properties: [{Properties}]",
string.Join(", ", entry.Properties.Select(p => $"{p.Key}={p.Value}")));
}
else
{
logger.LogInformation(" Properties: <none>");
}

// Simulate processing time
await Task.Delay(100, cts.Token);

await entry.CompleteAsync();
logger.LogInformation(" Completed message {MessageId}", entry.Id);
}
catch (Exception ex)
{
logger.LogError(ex, "Error processing message {MessageId}", entry.Id);
await entry.AbandonAsync();
}
}
}
catch (OperationCanceledException ex)
{
logger.LogInformation(ex, "Operation was cancelled");
}

logger.LogInformation("Processed {ProcessedCount} message(s)", processed);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\..\src\Foundatio.AzureStorage\Foundatio.AzureStorage.csproj" />
<PackageReference Include="System.CommandLine" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
</ItemGroup>
</Project>
137 changes: 137 additions & 0 deletions samples/Foundatio.AzureStorage.Enqueue/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.Linq;
using System.Threading.Tasks;
using Foundatio.AzureStorage.Samples;
using Foundatio.Queues;
using Microsoft.Extensions.Logging;

// Azure Storage Emulator connection string
const string EmulatorConnectionString = "DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://localhost:10000/devstoreaccount1;QueueEndpoint=http://localhost:10001/devstoreaccount1;";

// Define options
var connectionStringOption = new Option<string>("--connection-string", "-c")
{
Description = "Azure Storage connection string (defaults to emulator)"
};

var queueOption = new Option<string>("--queue", "-q")
{
Description = "Queue name",
DefaultValueFactory = _ => "sample-queue"
};

var messageOption = new Option<string>("--message", "-m")
{
Description = "Message to send",
DefaultValueFactory = _ => "Hello World"
};

var correlationIdOption = new Option<string>("--correlation-id")
{
Description = "Correlation ID for the message"
};

var propertiesOption = new Option<string[]>("--property")
{
Description = "Custom properties in key=value format",
DefaultValueFactory = _ => Array.Empty<string>()
};

var modeOption = new Option<AzureStorageQueueCompatibilityMode>("--mode")
{
Description = "Compatibility mode (Default or Legacy)",
DefaultValueFactory = _ => AzureStorageQueueCompatibilityMode.Default
};

var countOption = new Option<int>("--count")
{
Description = "Number of messages to send",
DefaultValueFactory = _ => 1
};

// Create root command
var rootCommand = new RootCommand("Azure Storage Queue Enqueue Sample");
rootCommand.Options.Add(connectionStringOption);
rootCommand.Options.Add(queueOption);
rootCommand.Options.Add(messageOption);
rootCommand.Options.Add(correlationIdOption);
rootCommand.Options.Add(propertiesOption);
rootCommand.Options.Add(modeOption);
rootCommand.Options.Add(countOption);

// Set handler
rootCommand.SetAction(async parseResult =>
{
var connectionString = parseResult.GetValue(connectionStringOption) ??
Environment.GetEnvironmentVariable("AZURE_STORAGE_CONNECTION_STRING") ??
EmulatorConnectionString;

var queueName = parseResult.GetValue(queueOption);
var message = parseResult.GetValue(messageOption);
var correlationId = parseResult.GetValue(correlationIdOption);
var properties = parseResult.GetValue(propertiesOption);
var mode = parseResult.GetValue(modeOption);
var count = parseResult.GetValue(countOption);

Console.WriteLine($"Using connection: {(connectionString == EmulatorConnectionString ? "Azure Storage Emulator" : "Custom connection string")}");
Console.WriteLine($"Mode: {mode}");
Console.WriteLine();

await EnqueueMessages(connectionString, queueName, message, correlationId, properties, mode, count);
return 0;
});

// Parse and invoke
return await rootCommand.Parse(args).InvokeAsync();

static async Task EnqueueMessages(string connectionString, string queueName, string message, string correlationId, string[] properties, AzureStorageQueueCompatibilityMode mode, int count)
{
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole().SetMinimumLevel(LogLevel.Information));
var logger = loggerFactory.CreateLogger("Enqueue");

logger.LogInformation("Creating queue with mode: {Mode}", mode);

using var queue = new AzureStorageQueue<SampleMessage>(options => options
.ConnectionString(connectionString)
.Name(queueName)
.CompatibilityMode(mode)
.LoggerFactory(loggerFactory));

var queueProperties = new Dictionary<string, string>();
if (properties != null)
{
foreach (var prop in properties)
{
var parts = prop.Split('=', 2);
if (parts.Length == 2)
{
queueProperties[parts[0]] = parts[1];
}
}
}

for (int i = 0; i < count; i++)
{
var sampleMessage = new SampleMessage
{
Message = count > 1 ? $"{message} #{i + 1}" : message,
Source = "Enqueue Sample"
};

var entryOptions = new QueueEntryOptions
{
CorrelationId = correlationId,
Properties = queueProperties.Count > 0 ? queueProperties : null
};

var messageId = await queue.EnqueueAsync(sampleMessage, entryOptions);

logger.LogInformation("Enqueued message {MessageId}: '{Message}' with CorrelationId: '{CorrelationId}' Properties: [{Properties}]",
messageId, sampleMessage.Message, correlationId ?? "<none>",
string.Join(", ", queueProperties.Select(p => $"{p.Key}={p.Value}")));
}

logger.LogInformation("Successfully enqueued {Count} message(s)", count);
}
10 changes: 10 additions & 0 deletions samples/Foundatio.AzureStorage.Enqueue/SampleMessage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;

namespace Foundatio.AzureStorage.Samples;

public record SampleMessage
{
public string Message { get; init; } = string.Empty;
public DateTime Timestamp { get; init; } = DateTime.UtcNow;
public string Source { get; init; } = string.Empty;
}
Loading