Published on

azure emulators in dotnet aspire development

Authors

azure emulators provide developers with a way to test their .net aspire applications locally, without incurring cloud costs or requiring an internet connection. these emulators replicate the behavior of azure services, allowing developers to write and debug code as if it were running in the cloud.

let's explore how to use azure emulators in a practical .net aspire application. we'll create a service that uploads contacts from an excel file to a postgresql database, utilizing azure blob storage and queue storage.

first, let's look at the apphost configuration:

App Host:

using Microsoft.Extensions.Hosting;

var builder = DistributedApplication.CreateBuilder(args);

var postgres = builder.AddPostgres("postgres");
var contactDb = postgres.AddDatabase("contactdb");

var storage = builder.AddAzureStorage("Storage");

if (builder.Environment.IsDevelopment())
{
    storage.RunAsEmulator(c => c.WithImageTag("3.31.0"));
}

var blobs = storage.AddBlobs("BlobConnection");
var queues = storage.AddQueues("QueueConnection");

builder.AddProject<Projects.Contact_Api>("contact-api")
    .WithReference(blobs)
    .WithReference(queues)
    .WithReference(contactDb);

builder.AddProject<Projects.Contact_Worker>("contact-worker")
    .WithReference(blobs)
    .WithReference(queues)
    .WithReference(contactDb);

builder.Build().Run();

in this configuration, we set up postgresql, azure blob storage, and azure queue storage. notice the RunAsEmulator method, which is used to run the azure storage emulator locally. it's important to specify a compatible version; in this case, we're using emulator version 3.31.0 for aspire version 8.1.0.

the application consists of an api service for uploading files and a background worker for processing them. the api service uploads the file to blob storage and sends a message to the queue:

private static async Task<IResult> Upload(IFormFile file, QueueServiceClient queueServiceClient, BlobServiceClient blobServiceClient, CancellationToken cancellationToken)
{
    var docsContainer = blobServiceClient.GetBlobContainerClient("fileuploads");
    var queueClient = queueServiceClient.GetQueueClient("contacts");

    var fileKey = Guid.NewGuid().ToString();
    using var fileStream = file.OpenReadStream();
    await docsContainer.UploadBlobAsync(fileKey, fileStream, cancellationToken);
    await queueClient.SendMessageAsync(fileKey, cancellationToken);

    return Results.Ok();
}

the background worker then processes the queue messages, downloads the files from blob storage, and saves the contact information to the database:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    // ... (initialization code)

    while (!stoppingToken.IsCancellationRequested)
    {
        QueueMessage[] messages = await queueClient.ReceiveMessagesAsync(maxMessages: 10, cancellationToken: stoppingToken);

        foreach (var message in messages)
        {
            var fileKey = message.MessageText;
            // ... (process the file and save to database)
        }

        await Task.Delay(5000, stoppingToken);
    }
}

it's important to note that while emulators provide a great development experience, they may not perfectly replicate all aspects of the live azure environment. developers should still perform final testing against actual azure services before deployment.

for the complete source code of this example, you can visit: https://github.com/eminvergil/aspire-samples