This blog post is part of the 2024 C# Advent Calendar. Go check out the other 49 great posts after you read mine, of course.
๐๐ Ho Ho Ho! Welcome to Santa's Coding Workshop! ๐ง๐ป
'Tis the season to be jolly... and to code! Grab your candy cane stylus and put on your coding mittens because you're about to embark on a magical journey to the North Pole's most high-tech workshop!
In this merry tutorial, you'll craft a real-time web application that brings Santa's workshop to life, powered by the enchanting duo of SignalR and .NET 9. Imagine a digital snow globe where you can peek inside and see:
๐งธ Elves busily crafting toys
๐ Gift-wrapping stations buzzing with activity
๐ฆ Reindeer munching on magical carrots
๐ช Ovens full of Mrs. Claus's famous cookies
๐ The Naughty-or-Nice list being checked twice
All updating in real-time, faster than you can say "Jingle Bells"!Our festive application showcases modern web development practices, all wrapped up in a bow of Christmas cheer. Through a clean, simple interface that would make even Frosty the Snowman proud, users can:
๐งฎ Watch the toy count climb towards Santa's goal
๐ Spy on the activities of elves like Buddy, Jingle, Sparkle, Twinkle, and Holly
๐ Keep tabs on Santa's status
And the best part? No need to refresh the page - it's all as smooth as a sleigh ride!
As you journey through the code, you'll see how to harness the power of WebSocket connections, sprinkled in some C# 13 magic dust, and leveraged SignalR's real-time capabilities to create an engaging and interactive workshop experience.
So, hang your stockings by the chimney with care, and dive into the code to see how the magic happens! Remember, in Santa's workshop, every bug is just a feature in disguise, and every successful compile is like finding a present under the tree.
Ready to code up some Christmas cheer? Jingle all the way to a winter wonderland of web development! ๐
๐ Getting the Workshop Ready! ๐ ๐
Kick off the festive coding adventure by setting up Santa's workshop project! First, you must create a shiny new ASP.NET Core Web Application using the magic of .NET 9. Grab your coding sleigh and run this command:
dotnet new web -n SantasWorkshopTracker -f net9.0
Dash into the project directory, where all the holiday magic will unfold. Once you're there, its time to add the SignalR client library to bring the elves to life:
cd SantasWorkshopTracker
dotnet add package Microsoft.AspNetCore.SignalR.Client
With these steps, you're laying the foundation for a good time in Santas Workshop! Get ready to watch the magic unfold in an interactive experience thatll make even the North Pole proud! ๐๐ป
๐๐ SantasWorkshopHub.cs: Santa's Communication Network ๐๐
Create a new file, SantasWorkshopHub.cs
, with the following content:
using Microsoft.AspNetCore.SignalR;
using System.Diagnostics;
namespace SantasWorkshopTracker;
public record ToyUpdate(int Count);
public record ElfActivity(string Name, string Activity);
public record SantaStatus(string Status);
public class SantasWorkshopHub : Hub
{
private static readonly ActivitySource ActivitySource = new("SantasWorkshop.SignalR");
private int _totalToys;
public async Task UpdateToyProduction(ToyUpdate update)
{
using var activity = ActivitySource.StartActivity();
activity?.SetTag("ToyCount", update.Count);
var localTotal = _totalToys;
await Task.Delay(100); // Simulating some async work
localTotal += update.Count;
_totalToys = localTotal;
await Clients.All.SendAsync("ReceiveToyUpdate", new ToyUpdate(_totalToys));
}
public Task UpdateElfActivity(ElfActivity elfActivity)
{
using var activity = ActivitySource.StartActivity();
activity?.SetTag("ElfName", elfActivity.Name);
activity?.SetTag("Activity", elfActivity.Activity);
return Clients.All.SendAsync("ReceiveElfUpdate", elfActivity);
}
public Task UpdateSantaStatus(SantaStatus status)
{
using var activity = ActivitySource.StartActivity();
activity?.SetTag("Status", status.Status);
return Clients.All.SendAsync("ReceiveSantaUpdate", status);
}
}
๐ Unwrapping the Festive Features ๐
Record-Breaking Christmas Records :
Our ToyUpdate, ElfActivity, and SantaStatus are wrapped up neatly as records. They're like perfectly packed presents, immutable and ready to spread joy!
Santa's Magic Telescope (ActivitySource):
private static readonly ActivitySource ActivitySource = new("SantasWorkshop.SignalR");
It isn't just any telescope; it's Santa's special way of watching all the workshop activities! ๐ญ
Tagging Toys and Elves :
Each method uses ActivitySource to create activities and set tags. It's like Santa putting name tags on all the presents and elves!
activity?.SetTag("ToyCount", update.Count);activity?.SetTag("ElfName", elfActivity.Name);
Async Christmas Magic :
Our UpdateToyProduction method works asynchronously, just like elves working through the night to prepare all the toys!
SignalR's Christmas Broadcast :
Clients.All.SendAsync is like Santa's magical announcement system, keeping everyone in the North Pole updated!
๐ Why This Code Sleighs ๐
Improved Tracing : With ActivitySource, you can track every toy made, every elf's activity, and Santa's status. It's like having a magical Christmas logbook! ๐
Real-time Updates : SignalR ensures everyone gets updates faster than Rudolph can fly! ๐ฆ๐จ
Clean and Concise : Using records and modern C# features makes our code as neat as a wrapped gift!
This SantasWorkshopHub isn't just a piece of code; it's the heart of the virtual North Pole, pumping Christmas cheer and toy updates to all!๐๐ ๐ป๐ง
๐ง Elf Code Workshop: The Magical WorkshopSimulator ๐ง
Create a new file, WorkshopSimulator.cs
:
using Microsoft.AspNetCore.SignalR;
namespace SantasWorkshopTracker;
public partial class WorkshopSimulator
{
private int _toyCount;
public partial int ToyCount { get; }
}
public partial class WorkshopSimulator(IHubContext<SantasWorkshopHub> hubContext) : BackgroundService
{
private readonly Random _random = new();
private const int ToyGoal = 100;
private readonly Lock _wrappingStationLock = new();
private int _availableWrappingStations = 3;
public partial int ToyCount => _toyCount;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
string[] elfNames = ["Buddy", "Jingle", "Sparkle", "Twinkle", "Holly"];
string[] elfActivities = ["making toys", "feeding reindeer", "baking cookies", "checking the list"];
while (!stoppingToken.IsCancellationRequested && _toyCount < ToyGoal)
{
var tasks = new List<Task>();
for (var i = 0; i < 5; i++) // Process 5 elves concurrently
{
var elfName = elfNames[_random.Next(elfNames.Length)];
var activity = elfActivities[_random.Next(elfActivities.Length)];
tasks.Add(ProcessElfActivity(elfName, activity, stoppingToken));
}
await Task.WhenAll(tasks);
await Task.Delay(TimeSpan.FromSeconds(2), stoppingToken);
}
if (_toyCount >= ToyGoal)
{
await hubContext.Clients.All.SendAsync("ReceiveSantaStatus", new SantaStatus("Ho Ho Ho! All toys are ready! Santa has departed! Merry Christmas!"), stoppingToken);
}
}
private async Task ProcessElfActivity(string elfName, string activity, CancellationToken stoppingToken)
{
if (activity == "making toys")
{
var toysProduced = _random.Next(1, 15);
Interlocked.Add(ref _toyCount, toysProduced);
await hubContext.Clients.All.SendAsync("ReceiveToyUpdate", new ToyUpdate(_toyCount), stoppingToken);
await WrapToy(elfName, stoppingToken);
}
else
{
await hubContext.Clients.All.SendAsync("ReceiveElfUpdate", new ElfActivity(elfName, activity), stoppingToken);
}
}
private async Task WrapToy(string elfName, CancellationToken stoppingToken)
{
var wrapped = false;
while (!wrapped && !stoppingToken.IsCancellationRequested)
{
using (var lockScope = _wrappingStationLock.EnterScope())
{
if (_availableWrappingStations > 0)
{
_availableWrappingStations--;
wrapped = true;
}
}
if (wrapped)
{
await hubContext.Clients.All.SendAsync("ReceiveElfUpdate", new ElfActivity(elfName, "wrapping a gift"), stoppingToken);
await Task.Delay(3000, stoppingToken); // Simulating wrapping time
using var lockScope = _wrappingStationLock.EnterScope();
_availableWrappingStations++;
}
else
{
await hubContext.Clients.All.SendAsync("ReceiveElfUpdate", new ElfActivity(elfName, "waiting for a wrapping station"), stoppingToken);
await Task.Delay(1000, stoppingToken); // Wait before trying again
}
}
}
}
๐ Unwrapping the Festive Features ๐
Magical Lock Spell : The _wrappingStationLock is like Santa's magic key, ensuring orderly access to gift-wrapping stations!
Partial Properties - A Christmas Miracle : Youve split the ToyCount property like sharing a Christmas cookie. Half the declaration, twice the joy!
Collection Expressions - A Sack Full of Goodies : The elf names and activities are now as neatly packed as Santa's gift bag, thanks to the new collection expression syntax!
Concurrent Elf Magic : Youre processing five elves at once, spreading holiday cheer faster than Rudolph on Christmas Eve!
Festive Randomness : Just like you never know what's in a Christmas cracker, the _random adds a sprinkle of surprise to elf activities!
This code isn't just functional; it's a winter wonderland of C# 13 features! It's as exciting as finding the biggest present under the tree on Christmas morning! ๐๐
๐ ๐๐ Program.cs: The North Pole's Command Center
Update your Program.cs
file to add SignalR services and map the hub:
using SantasWorkshopTracker;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSignalR();
builder.Services.AddHostedService<WorkshopSimulator>();
var app = builder.Build();
app.MapHub<SantasWorkshopHub>("/santasworkshop");
app.UseStaticFiles();
app.MapFallbackToFile("index.html");
app.Run();
This jolly Program.cs sets up the North Pole operations center, connecting Santa's workshop to the digital world faster than you can say "Ho Ho Ho!" ๐ ๐ป๐
๐ Crafting Santa's Digital Dashboard ๐ฅ
Create a festive index.html in the wwwroot
folder:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Santa's Workshop Live Tracker</title>
<style>
body { font-family: Arial, sans-serif; background-color: #f0f0f0; }
.container { max-width: 800px; margin: 0 auto; padding: 20px; background-color: #fff; border-radius: 10px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
h1 { color: #c41e3a; font-size: 24px; text-align: center; }
.update-section { margin-bottom: 20px; }
</style>
</head>
<body>
<div class="container">
<h1>Santa's Workshop Live Tracker</h1>
<div class="update-section">
<h2>Toy Production</h2>
<p id="toyCount">Toys made: 0</p>
</div>
<div class="update-section">
<h2>Elf Activities</h2>
<ul id="elfActivities"></ul>
</div>
<div class="update-section">
<h2>Santa's Status</h2>
<p id="santaStatus">Preparing for Christmas Eve</p>
</div>
<div class="update-section">
<h2>Workshop Updates</h2>
<ul id="workshopUpdates"></ul>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/6.0.1/signalr.min.js"></script>
<script>
const connection = new signalR.HubConnectionBuilder()
.withUrl("/santasworkshop")
.configureLogging(signalR.LogLevel.Information)
.build();
connection.on("ReceiveToyUpdate", (update) => {
document.getElementById("toyCount").textContent = `Toys made: ${update.count}`;
});
connection.on("ReceiveElfUpdate", (activity) => {
const li = document.createElement("li");
li.textContent = `${activity.name} is ${activity.activity}`;
document.getElementById("elfActivities").appendChild(li);
});
connection.on("ReceiveSantaStatus", (status) => {
const santaStatus = document.getElementById("santaStatus");
santaStatus.textContent = status.status;
if (status.status.includes("departed")) {
santaStatus.style.color = "#c41e3a";
santaStatus.style.fontWeight = "bold";
connection.stop();
}
});
connection.on("ReceiveMessage", (message) => {
const li = document.createElement("li");
li.textContent = message;
document.getElementById("workshopUpdates").appendChild(li);
});
connection.start()
.then(() => {
console.log("Connected to Santa's Workshop!");
document.getElementById("connectionStatus").textContent = "Connected";
})
.catch((err) => {
console.error(`Connection error: ${err.toString()}`);
document.getElementById("connectionStatus").textContent = "Connection failed";
alert("Failed to connect to Santa's Workshop. Please try refreshing the page.");
});
connection.onclose((error) => {
console.error(`Connection closed: ${error ? error.message : "Unknown error"}`);
document.getElementById("connectionStatus").textContent = "Disconnected";
alert("Connection to Santa's Workshop was lost. Please refresh the page to reconnect.");
});
</script>
</body>
</html>
This magical interface brings Santa's workshop to life, updating faster than Rudolph's nose blinks! ๐
๐ Launching Santa's Workshop! ๐
Fire up the magic with:
dotnet run
Then, dash over to http://localhost:5000 in your browser to watch Santa's Workshop Live Tracker sparkle into action! ๐
You should see something that looks an awful lot like this!
๐ Wrapping Up the Workshop
๐๐ Ho ho ho! Time to unwrap the festive .NET 9 features for each class.
๐ ๐๐ Program.cs: The North Pole's Command Center
๐ Native AOT compilation: Faster than Rudolph on Christmas Eve!
๐ Improved performance: Santa's sleigh just got a turbo boost!
๐ Enhanced security: Keeping those presents safe from the Grinch!
๐ง Elf Code Workshop: The Magical WorkshopSimulator ๐ง
JIT enhancements: Elves are working faster than ever!
๐งฎ Loop optimizations: Counting toys quicker than you can say "Ho Ho Ho!"
๐จ PGO improvements: Painting toys with precision!
๐ SantasWorkshopHub.cs: Santa's Communication Network
๐ก Improved tracing: Tracking elves better than Santa's naughty-or-nice list!
๐ Native AOT compatibility: Messages flying faster than reindeer!
๐งฉ Polymorphic hub method arguments: Handling different types of Christmas wishes!
๐ General .NET 9 Goodies:
๐ง AI building blocks: Helping elves make smarter toys!
๐ New Tensor types: Wrapping presents in multi-dimensional joy!
๐ Improved cryptography: Keeping Santa's magic safe and sound!
๐ฆ Enhanced ASP.NET Core: Delivering web presents smoother than ever!
These magical .NET 9 features will make this the most productive Christmas season yet for developers worldwide! ๐๐
๐ง Troubleshooting
Here are some common issues developers might encounter and how to resolve them:
- SignalR Connection IssuesProblem : SignalR fails to establish a connection. Solution : Ensure your firewall isn't blocking WebSocket connections. Check the console for any CORS-related errors and update your CORS policy if necessary.
// In Program.cs
builder.Services.AddCors(options =>
{
options.AddPolicy("CorsPolicy", builder => builder
.WithOrigins("http://localhost:5000")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
-
Performance Bottlenecks in Toy ProductionProblem : Toy production simulation runs slowly.
Solution : Utilize parallel processing for elf activities and consider using
System.Threading.Channels
for efficient producer-consumer scenarios.
// In WorkshopSimulator.cs
private Channel<ElfActivity> _elfActivityChannel = Channel.CreateUnbounded<ElfActivity>();
// Producer
await _elfActivityChannel.Writer.WriteAsync(new ElfActivity(elfName, activity));
// Consumer
while (await _elfActivityChannel.Reader.WaitToReadAsync())
{
if (_elfActivityChannel.Reader.TryRead(out var elfActivity))
{
await ProcessElfActivity(elfActivity);
}
}
- Memory Leaks in Long-Running SimulationsProblem : Application memory usage grows over time. Solution : Implement proper disposal of resources and use weak references for long-lived objects.
// In WorkshopSimulator.cs
private WeakReference<List<ElfActivity>> _recentActivities = new WeakReference<List<ElfActivity>>(new List<ElfActivity>());
// Usage
if (_recentActivities.TryGetTarget(out var activities))
{
activities.Add(new ElfActivity(elfName, activity));
}
Important Tip *: Always monitor your application's performance using tools like dotnet-counters or Application Insights to catch and address issues early.*
Warning *: Be cautious when implementing parallel processing in SignalR hubs. Ensure thread-safety and consider using*
SemaphoreSlim
for controlled concurrency.
]]>
Top comments (0)