Console vs Web Applications

Understand the differences between using EasyMessages in console applications versus ASP.NET Core web applications. EasyMessages v1.0.0 ships as a single package for all application types.


Quick Comparison

Feature Console Apps ASP.NET Core Apps
Package RecurPixel.EasyMessages RecurPixel.EasyMessages
Message Creation [✓] Msg.* [✓] Msg.*
Console Output [✓] .ToConsole() Warning: Available but not typical
JSON/XML Output [✓] .ToJson(), .ToXml() [✓] .ToJson(), .ToXml()
API Response [ ] Use .ToJsonObject() manually [✓] StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject())
Logging [✓] .Log(ILogger) [✓] .Log(ILogger)
DI Registration [ ] Not needed [ ] Not needed

Console Applications

When to Use EasyMessages in Console Apps

Use RecurPixel.EasyMessages for:

  • [✓] Command-line tools and utilities
  • [✓] Background workers and Windows Services
  • [✓] Data processing and ETL jobs
  • [✓] Scheduled tasks and cron jobs
  • [✓] Desktop applications (WPF, WinForms)
  • [✓] Class libraries (non-web)
  • [✓] Azure Functions with output bindings
  • [✓] AWS Lambda functions
  • [✓] Test projects
  • [✓] Migration scripts

Installation

dotnet add package RecurPixel.EasyMessages --version 1.0.0

Typical Usage Pattern

using RecurPixel.EasyMessages;

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Processing data...");

        try
        {
            // Process data
            var result = ProcessData();

            // Output to console
            Msg.System.OperationCompleted()
                .WithData(new { RecordsProcessed = result.Count })
                .ToConsole(useColors: true);

            // Or save to file as JSON
            var json = Msg.System.OperationCompleted()
                .WithData(result)
                .ToJson();

            File.WriteAllText("result.json", json);
        }
        catch (Exception ex)
        {
            // Error to console
            Msg.System.Error()
                .WithMetadata("error", ex.Message)
                .ToConsole(useColors: true);

            Environment.Exit(1);
        }
    }
}

Features Available

1. Console Output with Colors

// Success messages (green)
Msg.System.OperationCompleted().ToConsole(useColors: true);

// Error messages (red)
Msg.System.Error().ToConsole(useColors: true);

// Warning messages (yellow)
Msg.Validation.Failed().ToConsole(useColors: true);

// Info messages (blue)
Msg.System.Processing().ToConsole(useColors: true);

// Disable colors
Msg.System.OperationCompleted().ToConsole(useColors: false);

2. File Output

// Save as JSON
var json = Msg.Crud.Created("Record")
    .WithData(myData)
    .ToJson();
File.WriteAllText("output.json", json);

// Save as XML
var xml = Msg.Crud.Created("Record")
    .WithData(myData)
    .ToXml();
File.WriteAllText("output.xml", xml);

// Save as plain text
var text = Msg.System.OperationCompleted()
    .WithData(myData)
    .ToPlainText();
File.WriteAllText("output.txt", text);

3. Custom Messages

// Load custom messages from file
MessageRegistry.LoadCustomMessages("messages/custom.json");

// Use custom messages
Msg.Custom("MY_CUSTOM_001")
    .WithParams(new { field = "value" })
    .ToConsole();

4. Logging

// Use .Log(ILogger) directly on any message
using Microsoft.Extensions.Logging;

var loggerFactory = LoggerFactory.Create(builder =>
{
    builder.AddConsole();
});

var logger = loggerFactory.CreateLogger<Program>();

// Log with EasyMessages - log level mapped automatically from message type
var message = Msg.System.Error();
message.Log(logger); // Logs at Error level

5. Formatter Configuration

using RecurPixel.EasyMessages.Configuration;

// Configure global formatting
FormatterConfiguration.Configure(options =>
{
    options.IncludeTimestamp = true;
    options.IncludeCorrelationId = true;
    options.IncludeMetadata = true;
    options.IncludeData = true;
    options.IncludeNullFields = false;
});

// Or use presets
FormatterConfiguration.SetDefaultOptions(FormatterConfiguration.Minimal);
FormatterConfiguration.SetDefaultOptions(FormatterConfiguration.Verbose);
FormatterConfiguration.SetDefaultOptions(FormatterConfiguration.Debug);

Complete Console Example

using RecurPixel.EasyMessages;
using RecurPixel.EasyMessages.Configuration;

Console.WriteLine("=== Data Processing Tool ===\n");

// Configure formatting
FormatterConfiguration.SetDefaultOptions(FormatterConfiguration.Verbose);

try
{
    // Start processing
    Msg.System.Processing()
        .WithParams(new { task = "Data Import" })
        .ToConsole(useColors: true);

    // Load data
    var records = LoadDataFromCsv("data.csv");

    Msg.System.OperationCompleted()
        .WithData(new { RecordsLoaded = records.Count })
        .ToConsole(useColors: true);

    // Process each record
    int successCount = 0;
    int errorCount = 0;

    foreach (var record in records)
    {
        try
        {
            ProcessRecord(record);
            successCount++;
        }
        catch (Exception ex)
        {
            Msg.System.Error()
                .WithData(new { Record = record.Id })
                .WithMetadata("error", ex.Message)
                .ToConsole(useColors: true);

            errorCount++;
        }
    }

    // Summary
    Console.WriteLine($"\nProcessing complete:");
    Console.WriteLine($"  Success: {successCount}");
    Console.WriteLine($"  Errors: {errorCount}");

    // Save results
    var summary = new
    {
        TotalRecords = records.Count,
        Successful = successCount,
        Failed = errorCount,
        ProcessedAt = DateTime.UtcNow
    };

    var json = Msg.System.OperationCompleted()
        .WithData(summary)
        .ToJson();

    File.WriteAllText("processing-summary.json", json);

    Msg.File.UploadSuccessful("processing-summary.json")
        .ToConsole(useColors: true);
}
catch (Exception ex)
{
    Msg.System.Error()
        .WithMetadata("error", ex.Message)
        .WithMetadata("stackTrace", ex.StackTrace)
        .ToConsole(useColors: true);

    Environment.Exit(1);
}

ASP.NET Core Applications

When to Use EasyMessages in Web Apps

Use RecurPixel.EasyMessages for:

  • [✓] ASP.NET Core Web APIs
  • [✓] REST APIs and GraphQL APIs
  • [✓] MVC Applications
  • [✓] Minimal APIs
  • [✓] Blazor Server applications
  • [✓] gRPC services (with HTTP endpoints)
  • [✓] SignalR hubs (with HTTP endpoints)

Installation

dotnet add package RecurPixel.EasyMessages --version 1.0.0

No DI registration is required. Use EasyMessages directly.

Typical Usage Pattern — Controllers

// Controller
using Microsoft.AspNetCore.Mvc;
using RecurPixel.EasyMessages;

[ApiController]
[Route("api/[controller]")]
public class ProductsController : ControllerBase
{
    private readonly ILogger<ProductsController> _logger;
    private readonly IProductService _service;

    public ProductsController(
        ILogger<ProductsController> logger,
        IProductService service)
    {
        _logger = logger;
        _service = service;
    }

    [HttpGet("{id}")]
    public IActionResult GetById(int id)
    {
        var product = _service.GetById(id);

        if (product == null)
        {
            var notFound = Msg.Crud.NotFound("Product")
                .WithMetadata("productId", id)
                .Log(_logger);
            return StatusCode(notFound.HttpStatusCode ?? 404, notFound.ToJsonObject()); // Returns 404
        }

        var msg = Msg.Crud.Retrieved("Product")
            .WithData(product)
            .Log(_logger);
        return StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject()); // Returns 200
    }

    [HttpPost]
    public IActionResult Create(CreateProductDto dto)
    {
        if (!ModelState.IsValid)
        {
            var val = Msg.Validation.Failed().WithData(ModelState);
            return StatusCode(val.HttpStatusCode ?? 422, val.ToJsonObject()); // Returns 422
        }

        var product = _service.Create(dto);

        var msg = Msg.Crud.Created("Product")
            .WithData(product)
            .Log(_logger);
        return StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject()); // Returns 200
    }
}

Typical Usage Pattern — Minimal APIs

using RecurPixel.EasyMessages;

var app = builder.Build();

app.MapGet("/products/{id}", (int id, IProductService service, ILogger<Program> logger) =>
{
    var product = service.GetById(id);

    if (product == null)
    {
        var notFound = Msg.Crud.NotFound("Product")
            .WithMetadata("productId", id)
            .Log(logger);
        return Results.Json(notFound.ToJsonObject(), statusCode: notFound.HttpStatusCode ?? 404);
    }

    var msg = Msg.Crud.Retrieved("Product").WithData(product).Log(logger);
    return Results.Json(msg.ToJsonObject(), statusCode: msg.HttpStatusCode ?? 200);
});

app.MapPost("/products", (CreateProductDto dto, IProductService service, ILogger<Program> logger) =>
{
    var product = service.Create(dto);
    var msg = Msg.Crud.Created("Product").WithData(product).Log(logger);
    return Results.Json(msg.ToJsonObject(), statusCode: msg.HttpStatusCode ?? 200);
});

Features Available

1. API Response Conversion

// Automatic HTTP status code mapping
var msg = Msg.Auth.Unauthorized();
return StatusCode(msg.HttpStatusCode ?? 403, msg.ToJsonObject()); // 403 Forbidden

var notFound = Msg.Crud.NotFound("User");
return StatusCode(notFound.HttpStatusCode ?? 404, notFound.ToJsonObject()); // 404 Not Found

var val = Msg.Validation.Failed();
return StatusCode(val.HttpStatusCode ?? 422, val.ToJsonObject()); // 422 Unprocessable Entity

var err = Msg.System.Error();
return StatusCode(err.HttpStatusCode ?? 500, err.ToJsonObject()); // 500 Internal Server Error

// Custom status code
var tooMany = Msg.Auth.LoginFailed().WithStatusCode(429);
return StatusCode(429, tooMany.ToJsonObject());

2. Logging Integration

// Log level mapped automatically from message type
var created = Msg.Crud.Created("User")
    .WithData(user)
    .Log(_logger); // Logs at Information level
return StatusCode(created.HttpStatusCode ?? 200, created.ToJsonObject());

var err = Msg.System.Error()
    .WithMetadata("error", ex.Message)
    .Log(_logger); // Logs at Error level
return StatusCode(err.HttpStatusCode ?? 500, err.ToJsonObject());

var warn = Msg.Validation.Failed()
    .WithData(errors)
    .Log(_logger); // Logs at Warning level
return StatusCode(warn.HttpStatusCode ?? 422, warn.ToJsonObject());

Complete API Example

// Program.cs - no special EasyMessages registration needed
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
// Controllers/UsersController.cs
using Microsoft.AspNetCore.Mvc;
using RecurPixel.EasyMessages;

[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
    private readonly ILogger<UsersController> _logger;
    private readonly IUserService _userService;

    public UsersController(
        ILogger<UsersController> logger,
        IUserService userService)
    {
        _logger = logger;
        _userService = userService;
    }

    [HttpGet]
    public IActionResult GetAll()
    {
        var users = _userService.GetAll();
        var msg = Msg.Crud.Retrieved("Users").WithData(users).Log(_logger);
        return StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject());
    }

    [HttpGet("{id}")]
    public IActionResult GetById(int id)
    {
        var user = _userService.GetById(id);
        if (user == null)
        {
            var notFound = Msg.Crud.NotFound("User")
                .WithMetadata("userId", id)
                .Log(_logger);
            return StatusCode(notFound.HttpStatusCode ?? 404, notFound.ToJsonObject());
        }

        var msg = Msg.Crud.Retrieved("User").WithData(user);
        return StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject());
    }

    [HttpPost]
    public IActionResult Create(CreateUserDto dto)
    {
        if (!ModelState.IsValid)
        {
            var val = Msg.Validation.Failed().WithData(ModelState);
            return StatusCode(val.HttpStatusCode ?? 422, val.ToJsonObject());
        }

        try
        {
            var user = _userService.Create(dto);
            var msg = Msg.Crud.Created("User").WithData(user).Log(_logger);
            return StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject());
        }
        catch (DuplicateEmailException ex)
        {
            var dup = Msg.Validation.Failed()
                .WithMetadata("field", "email")
                .WithMetadata("error", ex.Message)
                .Log(_logger);
            return StatusCode(dup.HttpStatusCode ?? 422, dup.ToJsonObject());
        }
    }

    [HttpPut("{id}")]
    public IActionResult Update(int id, UpdateUserDto dto)
    {
        if (!_userService.Exists(id))
        {
            var notFound = Msg.Crud.NotFound("User").WithMetadata("userId", id);
            return StatusCode(notFound.HttpStatusCode ?? 404, notFound.ToJsonObject());
        }

        var user = _userService.Update(id, dto);
        var msg = Msg.Crud.Updated("User").WithData(user).Log(_logger);
        return StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject());
    }

    [HttpDelete("{id}")]
    public IActionResult Delete(int id)
    {
        if (!_userService.Exists(id))
        {
            var notFound = Msg.Crud.NotFound("User").WithMetadata("userId", id);
            return StatusCode(notFound.HttpStatusCode ?? 404, notFound.ToJsonObject());
        }

        _userService.Delete(id);
        var msg = Msg.Crud.Deleted("User").WithMetadata("userId", id).Log(_logger);
        return StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject());
    }
}

Side-by-Side Comparison

Console App: Data Processing

// Console - RecurPixel.EasyMessages
using RecurPixel.EasyMessages;

static void ProcessFile(string path)
{
    try
    {
        var data = File.ReadAllLines(path);

        Msg.System.Processing()
            .WithData(new { File = path, Lines = data.Length })
            .ToConsole(useColors: true);

        // Process...

        Msg.System.OperationCompleted()
            .WithData(new { Processed = data.Length })
            .ToConsole(useColors: true);
    }
    catch (Exception ex)
    {
        Msg.System.Error()
            .WithMetadata("error", ex.Message)
            .ToConsole(useColors: true);

        Environment.Exit(1);
    }
}

Web API: File Upload

// ASP.NET Core - RecurPixel.EasyMessages
using Microsoft.AspNetCore.Mvc;
using RecurPixel.EasyMessages;

[ApiController]
[Route("api/files")]
public class FilesController : ControllerBase
{
    private readonly ILogger<FilesController> _logger;

    [HttpPost("upload")]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        try
        {
            if (file == null || file.Length == 0)
            {
                var req = Msg.Validation.RequiredField("file");
                return StatusCode(req.HttpStatusCode ?? 422, req.ToJsonObject());
            }

            var path = await SaveFileAsync(file);

            var msg = Msg.File.UploadSuccessful(file.FileName)
                .WithData(new
                {
                    FileName = file.FileName,
                    Size = file.Length,
                    Path = path
                })
                .Log(_logger);
            return StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject());
        }
        catch (Exception ex)
        {
            var err = Msg.System.Error()
                .WithMetadata("error", ex.Message)
                .Log(_logger);
            return StatusCode(err.HttpStatusCode ?? 500, err.ToJsonObject());
        }
    }
}

Migration from v0.x

If you used the old RecurPixel.EasyMessages.AspNetCore package:

  1. Remove the old package:
    dotnet remove package RecurPixel.EasyMessages.AspNetCore
    
  2. Ensure Core package is installed:
    dotnet add package RecurPixel.EasyMessages --version 1.0.0
    
  3. Remove old usings: ```csharp // Remove: // using RecurPixel.EasyMessages.AspNetCore; // using RecurPixel.EasyMessages.Core;

// Keep or add: using RecurPixel.EasyMessages;


4. **Remove DI registration from Program.cs:**
```csharp
// Remove:
// builder.Services.AddEasyMessages(builder.Configuration);
  1. Replace output methods: ```csharp // Before: return msg.ToApiResponse(); // After: return StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject());

// Before (Minimal API): return msg.ToMinimalApiResult(); // After: return Results.Json(msg.ToJsonObject(), statusCode: msg.HttpStatusCode ?? 200); ```

  1. .Log(ILogger) now works directly from Core - no change needed.

Best Practices

Console Applications

[✓] DO:

  • Use colored console output for better readability
  • Save important results to JSON/XML files
  • Include timestamps and correlation IDs for debugging
  • Handle exceptions and exit with appropriate codes
  • Use FormatterConfiguration for consistent output

[ ] DON’T:

  • Mix console and file output without clear separation

Web Applications

[✓] DO:

  • Always use StatusCode(msg.HttpStatusCode ?? 200, msg.ToJsonObject()) for controllers
  • Use Results.Json(msg.ToJsonObject(), statusCode: msg.HttpStatusCode ?? 200) for Minimal APIs
  • Chain .Log(_logger) for important operations
  • Let HTTP status codes be set automatically
  • Use .WithMetadata() to add debugging context

[ ] DON’T:

  • Use .ToConsole() in API endpoints (use logging instead)
  • Forget to log errors and warnings
  • Return raw message objects without ToJsonObject()

Next Steps


Questions? Ask in Discussions