Skip to main content

Building a C# Client

C# is an industry standard for enterprise applications and Windows-based service architectures.

A simple file poller

This console application uses a simple loop to monitor the SUB_DIR for new, non-hidden files.

using System;
using System.IO;
using System.Linq;
using System.Threading;

// 🔥 set this to the path configured in your hypermass-config.yaml subscription-targets
const string SUB_DIR = @"<<YOUR PATH HERE>>";

Console.WriteLine($"Monitoring Hypermass folder {SUB_DIR}...");

while (true)
{
ScanAndProcess();
Thread.Sleep(5000); // Wait 5 seconds
}

void ScanAndProcess()
{
var dir = new DirectoryInfo(SUB_DIR);
if (!dir.Exists) return;

// List files, ignoring hidden files (like .hypermass metadata)
// Sort by LastWriteTime (oldest first)
var files = dir.GetFiles()
.Where(f => !f.Attributes.HasFlag(FileAttributes.Hidden) && !f.Name.StartsWith("."))
.OrderBy(f => f.LastWriteTime)
.ToList();

foreach (var file in files)
{
Console.WriteLine($"Processing: {file.Name}");

try
{
// ⚠️⚠️⚠️ YOUR LOGIC HERE ⚠️⚠️⚠️
// string content = File.ReadAllText(file.FullName);

// Delete once processed
file.Delete();
}
catch (Exception ex)
{
Console.WriteLine($"Error processing {file.Name}: {ex.Message}");
// Break to preserve order - try again on next scan
break;
}
}
}

About this code

We suggest starting with this and modifying it to suit your needs. Some notes about the approach;

  • LINQ Filtering: We use .Where to easily filter out hidden files including the .hypermass metadata folder.
  • LastWriteTime Sorting: Using OrderBy(f => f.LastWriteTime) ensures that data is processed in the sequence it was received on disk.
  • Atomic Safety: Because the Hypermass CLI performs an atomic "Move" into the directory, file.Delete() will not interfere with active writes.
note

The hypermass sync command relies on the hidden .hypermass directory for tracking progress. The logic above ensures your code ignores this system folder while keeping it intact.

How to productionize this code example

Moving from a Console App to a robust production service:

Small file subscriptions

For standard JSON or XML files, File.ReadAllText() or File.ReadAllBytes() is usually sufficient for files up to 50MB at low message cadences.

Large file subscriptions

If you are processing larger files, use FileStream to avoid memory exhaustion:

  • JSON: Use System.Text.Json with Utf8JsonReader for high-performance streaming.
  • XML: Use XmlReader for a memory-efficient "forward-only" stream.

Stop on fail

In the example above, the break inside the catch block is intentional - you may want to go further and stop the poller completely. In many enterprise scenarios, if payload #5 fails, you should not move on to payload #6 until the issue is resolved to maintain data integrity.

Structure

You'll likely want to convert this over to a class based approach so that you can more easily control and interact with various instances.

Using Worker Services

For a modern .NET approach to persistent background services, you may want to wrap this logic in a BackgroundService.

This simplifies the management of the background thread and integrates well with the program life cycle.

public class HypermassSubscriptionWorker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
ScanAndProcess();
await Task.Delay(5000, stoppingToken);
}
}
}