PrimusSaaS.Notifications 1.3.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package PrimusSaaS.Notifications --version 1.3.0
                    
NuGet\Install-Package PrimusSaaS.Notifications -Version 1.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="PrimusSaaS.Notifications" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="PrimusSaaS.Notifications" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="PrimusSaaS.Notifications" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add PrimusSaaS.Notifications --version 1.3.0
                    
#r "nuget: PrimusSaaS.Notifications, 1.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package PrimusSaaS.Notifications@1.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=PrimusSaaS.Notifications&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=PrimusSaaS.Notifications&version=1.3.0
                    
Install as a Cake Tool

PrimusSaaS.Notifications

Production-ready notification building blocks for Primus SaaS applications. The library provides SMTP email delivery, file-based templating with Fluid, an in-memory queue with a background worker, and instrumentation hooks for observability.

Features

  • SMTP email channel with retry/backoff and structured logging
  • File-based Liquid templates with caching and subject/body conventions
  • In-memory bounded queue plus background worker for async delivery
  • Lightweight diagnostics via NotificationMetrics and NotificationRuntimeStats
  • Opt-in logger channel for non-email scenarios or local development
  • NotificationResult surface that reports per-channel outcomes and can throw on failure to prevent silent drops

Installation

dotnet add package PrimusSaaS.Notifications --version 1.3.0

🚀 Minimal Complete Example (Copy-Paste Ready)

This is a complete, working Program.cs for a minimal ASP.NET Core app:

using PrimusSaaS.Notifications;
using PrimusSaaS.Notifications.Abstractions;

var builder = WebApplication.CreateBuilder(args);

// Configure notifications (logger mode for development)
builder.Services.AddPrimusNotifications(notifications =>
{
    notifications
        .UseSmtp(opts =>
        {
            opts.Host = "smtp.example.com";  // Replace with your SMTP host
            opts.Port = 587;
            opts.Username = "your-username"; // Or use builder.Configuration["Smtp:Username"]
            opts.Password = "your-password"; // Or use builder.Configuration["Smtp:Password"]
            opts.FromAddress = "no-reply@yourdomain.com";
            opts.FromName = "My App";
            opts.EnableSsl = true;
        })
        .UseLogger(); // Also logs notifications (useful for development)
});

var app = builder.Build();

// Send an email
app.MapPost("/send-email", async (INotificationService notifications) =>
{
    var result = await notifications.SendEmailAsync(
        "recipient@example.com",
        "Hello from PrimusSaaS!",
        "<h1>Welcome!</h1><p>Your account has been created.</p>"
    );
    
    return result.Success 
        ? Results.Ok("Email sent!") 
        : Results.Problem($"Failed: {result.FailureReason}");
});

// Send an SMS (logs by default, configure Twilio for real SMS)
app.MapPost("/send-sms", async (INotificationService notifications) =>
{
    var result = await notifications.SendSmsAsync(
        "+15551234567",
        "Your verification code is 123456"
    );
    
    return result.Success 
        ? Results.Ok("SMS sent!") 
        : Results.Problem($"Failed: {result.FailureReason}");
});

app.Run();
// In your controller or service, inject INotificationService:
public class MyController : ControllerBase
{
    private readonly INotificationService _notifications;
    
    public MyController(INotificationService notifications)
    {
        _notifications = notifications;
    }
    
    [HttpPost("welcome")]
    public async Task<IActionResult> SendWelcome(string email, string name)
    {
        var result = await _notifications.SendEmailAsync(
            email,
            $"Welcome, {name}!",
            $"<p>Hello {name}, thanks for joining!</p>"
        );
        
        return result.Success ? Ok() : StatusCode(500, result.FailureReason);
    }
}

Quick start (ASP.NET Core)

builder.Services.AddPrimusNotifications(notifications =>
{
    notifications
        .UseSmtp(opts =>
        {
            opts.Host = builder.Configuration["Smtp:Host"];
            opts.Port = 587;
            opts.Username = builder.Configuration["Smtp:Username"];
            opts.Password = builder.Configuration["Smtp:Password"];
            opts.FromAddress = "no-reply@primus.local";
            opts.FromName = "Primus Notifications";
            opts.EnableSsl = true;
        })
        .UseFileTemplates(Path.Combine(builder.Environment.ContentRootPath, "NotificationTemplates"))
        .UseInMemoryQueue(options =>
        {
            options.BoundedCapacity = 500;
            options.MaxParallelHandlers = 2;
        })
        .UseSms() // logs SMS payloads by default; swap ISmsSender for your provider
        .UseLogger();
});

Create a notification type:

public record WelcomeNotification(string Email, string Name) : INotification
{
    public string Type => "Welcome";
    public object Data => new { Name };
    public IEnumerable<string> Channels => new[] { "Email", "Logger" };
    public Recipient Recipient => new() { Email = Email, Name = Name };
}

Render templates from NotificationTemplates/Welcome/EmailSubject.liquid and NotificationTemplates/Welcome/EmailBody.liquid, then enqueue or send directly:

var notification = new WelcomeNotification("ada@example.com", "Ada");

// Async queue (recommended)
await queue.EnqueueAsync(notification, cancellationToken);

// Or immediate dispatch
var result = await notificationService.SendAsync(notification, cancellationToken);
if (!result.Success)
{
    // Log/return a 500 so the failure is visible
    logger.LogError("Notification failed: {Reason}", result.FailureReason);
}

Send one-off notifications without creating a custom INotification type:

await notificationService.SendEmailAsync("user@example.com", "Welcome", "<p>Thanks for signing up!</p>");
await notificationService.SendSmsAsync("+15551234567", "Your code is 123456");

Both helpers return NotificationResult and will throw NotificationFailedException when NotificationOptions.ThrowOnFailure is enabled (default) and nothing was delivered.

Plug in your SMS provider by implementing ISmsSender and registering it:

builder.Services.AddPrimusNotifications(notifications =>
{
    notifications.UseSms<MySmsSender>(); // replaces the default logging sender
});

Twilio SMS (Built-in)

The library includes a production-ready Twilio SMS sender. Configure it with your Twilio credentials:

Option 1: Inline configuration

builder.Services.AddPrimusNotifications(notifications =>
{
    notifications.UseTwilio(opts =>
    {
        opts.AccountSid = builder.Configuration["Twilio:AccountSid"];
        opts.AuthToken = builder.Configuration["Twilio:AuthToken"];
        opts.FromNumber = builder.Configuration["Twilio:FromNumber"];
    });
});

Option 2: From appsettings.json

builder.Services.AddPrimusNotifications(notifications =>
{
    notifications.UseTwilio(builder.Configuration);
});
{
  "Twilio": {
    "AccountSid": "your-account-sid",
    "AuthToken": "your-auth-token",
    "FromNumber": "+1234567890"
  }
}

Send SMS

await notificationService.SendSmsAsync("+15551234567", "Your verification code is 123456");

Environment variables (examples):

  • Twilio__AccountSid=ACxxxx
  • Twilio__AuthToken=...
  • Twilio__FromNumber=+16205538468

Notes: Twilio trial accounts require verifying each recipient number before sending. Keep AuthToken in secrets/Key Vault/env vars—never commit secrets. Twilio options are validated during startup (can be disabled with ValidateOnStartup = false).

SMTP configuration

SmtpOptions supports host, port, credentials, SSL, sender info, timeout, retry count, and exponential backoff base delay. Validation runs during DI configuration to catch missing host/port/from settings early.

Templates

  • File layout: {BasePath}/{NotificationType}/EmailSubject.liquid and EmailBody.liquid
  • SMS layout: {BasePath}/{NotificationType}/SmsBody.liquid (optional)
  • Fluid syntax with anonymous/POCO models
  • Templates are cached after first parse for performance
  • See TEMPLATE_GUIDE.md for conventions and examples

Direct helpers (SendEmailAsync(to, subject, body) / SendSmsAsync(phone, message)) skip template lookup entirely to avoid confusing failures when you just want to send raw content.

Background processing

Calling .UseInMemoryQueue() registers InMemoryNotificationQueue and NotificationBackgroundService to drain the queue using scoped NotificationService instances. Configure capacity, max parallel handlers, and retry/backoff per NotificationQueueOptions.

Diagnostics

Expose metrics via NotificationMetrics (System.Diagnostics.Metrics instruments) and quick in-process stats via NotificationRuntimeStats.GetSnapshot().

Building and packing

dotnet test sdk/dotnet/Primus.Notifications.Tests/Primus.Notifications.Tests.csproj
dotnet pack sdk/dotnet/Primus.Notifications/Primus.Notifications.csproj -c Release

Packages are emitted to nupkg/ with symbols and README included.

Troubleshooting

Common Issues

"The type or namespace name 'Notifications' does not exist in the namespace 'Primus'"

Make sure you're using the correct namespace:

// ✅ Correct
using PrimusSaaS.Notifications;

// ❌ Wrong (old namespace)
using Primus.Notifications;

"Cannot resolve INotificationService from DI container"

Ensure you've registered the notifications services in Program.cs:

builder.Services.AddPrimusNotifications(notifications =>
{
    notifications.UseSmtp(opts => { /* ... */ });
});

Email not being sent

  1. Check your SMTP credentials are correct
  2. Verify port 587 (TLS) or 465 (SSL) is open
  3. Check result.Success and result.FailureReason:
var result = await notifications.SendEmailAsync(...);
if (!result.Success)
{
    Console.WriteLine($"Failed: {result.FailureReason}");
}
Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 is compatible.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.1 26 3/11/2026
2.0.0 145 1/12/2026
1.4.2 350 11/30/2025
1.4.1 478 11/30/2025
1.3.0 239 11/29/2025
1.2.0 136 11/29/2025
1.1.0 123 11/29/2025
1.0.0 123 11/28/2025

v1.3.0: BREAKING - Namespace changed from Primus.Notifications to PrimusSaaS.Notifications. Added INotificationService interface for proper DI and mocking. Improved README with copy-paste ready code samples.