Building modern web applications requires not only handling user requests but also monitoring the system, performing background tasks, and notifying admins of failures. In this tutorial, we’ll build a complete full-stack project combining:
Hosted Services – background task execution
Health Checks – monitoring application and dependencies
Email Alerts – notifying admins on failures
Angular Dashboard – frontend interface to visualize system status
We will use ASP.NET Core for the backend and Angular for the frontend.
Project Overview
The system will:
Run background tasks to simulate system jobs.
Periodically check application health (database and custom checks).
Send email alerts if a health check fails.
Display live health status and task logs on an Angular dashboard.
Backend Implementation (ASP.NET Core)
1. Create the Project
dotnet new webapi -n MonitoringApp
cd MonitoringApp
Install required packages:
dotnet add package Microsoft.AspNetCore.Diagnostics.HealthChecks
dotnet add package MailKit
dotnet add package AspNetCore.HealthChecks.SqlServer
2. Configure SMTP Settings
Add email configuration to appsettings.json:
"Email": {
"From": "[email protected]",
"SmtpServer": "smtp.gmail.com",
"Port": 587,
"Username": "[email protected]",
"Password": "your-email-password"
}
3. Create Email Service
Services/EmailService.cs:
using MailKit.Net.Smtp;
using MimeKit;
public class EmailService
{
private readonly IConfiguration _config;
public EmailService(IConfiguration config) => _config = config;
public async Task SendEmailAsync(string subject, string message)
{
var email = new MimeMessage();
email.From.Add(MailboxAddress.Parse(_config["Email:From"]));
email.To.Add(MailboxAddress.Parse(_config["Email:From"]));
email.Subject = subject;
email.Body = new TextPart("html") { Text = message };
using var smtp = new SmtpClient();
await smtp.ConnectAsync(_config["Email:SmtpServer"], int.Parse(_config["Email:Port"]), false);
await smtp.AuthenticateAsync(_config["Email:Username"], _config["Email:Password"]);
await smtp.SendAsync(email);
await smtp.DisconnectAsync(true);
}
}
4. Configure Health Checks
Program.cs:
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
builder.Services.AddHealthChecks()
.AddSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), name: "SQL Server")
.AddCheck<CustomHealthCheck>("Custom Check");
app.MapHealthChecks("/health", new HealthCheckOptions
{
Predicate = _ => true,
ResponseWriter = async (context, report) =>
{
context.Response.ContentType = "application/json";
var result = new
{
status = report.Status.ToString(),
checks = report.Entries.Select(e => new { name = e.Key, status = e.Value.Status.ToString() })
};
await context.Response.WriteAsJsonAsync(result);
}
});
5. Custom Health Check
Services/CustomHealthCheck.cs:
using Microsoft.Extensions.Diagnostics.HealthChecks;
public class CustomHealthCheck : IHealthCheck
{
public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
{
bool isHealthy = DateTime.Now.Second % 2 == 0; // simulate success/failure
return Task.FromResult(isHealthy
? HealthCheckResult.Healthy("Service is running")
: HealthCheckResult.Unhealthy("Service failure"));
}
}

Join the conversation! Your thoughts help the community grow.