Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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>
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.Legacy
};

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