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:
- Remove the old package:
dotnet remove package RecurPixel.EasyMessages.AspNetCore - Ensure Core package is installed:
dotnet add package RecurPixel.EasyMessages --version 1.0.0 - 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);
- 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); ```
.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
- Core Concepts - Deep dive into messages
- ASP.NET Core Patterns - API response patterns
- Examples - Real-world examples
Questions? Ask in Discussions