MagicCSharp 0.0.7

There is a newer version of this package available.
See the version list below for details.
dotnet add package MagicCSharp --version 0.0.7
                    
NuGet\Install-Package MagicCSharp -Version 0.0.7
                    
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="MagicCSharp" Version="0.0.7" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MagicCSharp" Version="0.0.7" />
                    
Directory.Packages.props
<PackageReference Include="MagicCSharp" />
                    
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 MagicCSharp --version 0.0.7
                    
#r "nuget: MagicCSharp, 0.0.7"
                    
#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 MagicCSharp@0.0.7
                    
#: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=MagicCSharp&version=0.0.7
                    
Install as a Cake Addin
#tool nuget:?package=MagicCSharp&version=0.0.7
                    
Install as a Cake Tool

MagicCSharp

Enterprise-grade infrastructure for clean, maintainable C# applications

MagicCSharp provides the foundational building blocks for building scalable, maintainable, and testable enterprise applications using clean architecture principles. Stop writing boilerplate infrastructure code and focus on your business logic.

Why MagicCSharp?

Use Case Pattern - Organize business logic with clear separation of concerns

Automatic Service Registration - Use cases register themselves automatically

Testable Time - Mock time in your tests with ease

Distributed Locking - Built-in support for distributed locks

Drift-Free Scheduling - Background services that stay on schedule

Request Tracking - Track requests across async boundaries

Production-Ready - Battle-tested patterns from real-world applications

Installation

dotnet add package MagicCSharp

Three-Layer Architecture

MagicCSharp enforces clean separation of concerns with a three-layer architecture:

Layer Responsibility HTTP Concerns Business Logic Technical Implementation
Controllers API routing & DTOs ✅ Yes ❌ No ❌ No
Use Cases Business workflows ❌ No ✅ Yes ❌ No
Services External APIs & protocols ❌ No ❌ No ✅ Yes

Data Flow

HTTP Request
    ↓
Controller (HTTP concerns)
    ├── Extract parameters from HTTP
    ├── Validate authentication
    ├── Map DTO → Request
    ↓
Use Case (Business logic)
    ├── Validate business rules
    ├── Call Services (technical operations)
    ├── Call Repositories (data access)
    ├── Call Other Use Cases (workflows)
    ├── Return business result
    ↓
Controller (HTTP concerns)
    ├── Map Result → DTO
    ├── Return HTTP status + response
    ↓
HTTP Response

Quick Start

1. Define a Use Case

using MagicCSharp.UseCases;

// Request - Input data
public record CreateUserRequest
{
    public string Email { get; init; } = string.Empty;
    public string Name { get; init; } = string.Empty;
}

// Result - Output data
public record CreateUserResult
{
    public long UserId { get; init; }
    public bool Success { get; init; }
}

// Interface
public interface ICreateUserUseCase : IUseCase
{
    Task<CreateUserResult> Execute(CreateUserRequest request);
}

// Implementation
[UseCase]
public class CreateUserUseCase(
    IUserRepository userRepository,
    IEmailService emailService) : ICreateUserUseCase
{
    public async Task<CreateUserResult> Execute(CreateUserRequest request)
    {
        // Business logic only - no HTTP concerns
        var user = await userRepository.Create(new UserEdit
        {
            Email = request.Email,
            Name = request.Name
        });

        // Coordinate services
        await emailService.SendWelcomeEmail(user.Email);

        return new CreateUserResult
        {
            UserId = user.Id,
            Success = true
        };
    }
}

2. Register Use Cases

// In your Startup.cs or Program.cs
services.AddMagicUseCases();

Important for Multi-Assembly Projects:

If your use case interfaces are defined in separate C# libraries, you must ensure those assemblies are loaded before calling AddMagicUseCases(). C# loads assemblies JIT (Just-In-Time) - the .dll won't be loaded into the domain until code that references it is executed.

// Force assembly loading by referencing a type from each library
_ = typeof(MyLibrary1.ISomeUseCase);
_ = typeof(MyLibrary2.IAnotherUseCase);

// Now register use cases - all assemblies are loaded
services.AddMagicUseCases();

Without forcing the assembly to load first, use cases in other libraries won't be discovered during registration, even if you use them later in your application.

3. Use in Controller

[ApiController]
[Route("api/users")]
public class UserController : ControllerBase
{
    [HttpPost]
    public async Task<ActionResult<UserDto>> Create(
        [FromServices] ICreateUserUseCase useCase,
        [FromBody] CreateUserDto dto)
    {
        try
        {
            // Controller handles HTTP concerns only
            var result = await useCase.Execute(new CreateUserRequest
            {
                Email = dto.Email,
                Name = dto.Name
            });

            return Ok(new UserDto { Id = result.UserId });
        }
        catch (NotFoundException ex)
        {
            return NotFound(new { error = ex.Message });
        }
    }
}

That's it! Your use case is automatically registered and follows clean architecture principles.

Features

🎯 Use Case Pattern

The use case pattern helps organize business logic into small, focused classes that are easy to test and maintain.

// Use cases implement IMagicUseCase
public class SendWelcomeEmailUseCase(IEmailService emailService) : IMagicUseCase
{
    public async Task Execute(string email)
    {
        await emailService.SendWelcomeEmail(email);
    }
}

// Control the lifetime with an attribute
[MagicUseCase(ServiceLifetime.Singleton)]
public class CacheWarmerUseCase : IMagicUseCase
{
    // This use case will be registered as a singleton
}

Automatic Registration - All classes implementing IMagicUseCase are automatically registered in your DI container.

Lifetime Control - Use [MagicUseCase(ServiceLifetime)] to control service lifetime (Scoped by default).

⏰ Testable Time

Stop using DateTime.Now and start using IClock for time that you can control in tests.

public class OrderService(IClock clock)
{
    public Order CreateOrder()
    {
        return new Order
        {
            CreatedAt = clock.Now(),
            ExpiresAt = clock.Now().AddDays(30)
        };
    }
}

// In production
services.AddSingleton<IClock, DateTimeClock>();

// In tests
var fakeClock = new FakeClock(new DateTime(2024, 1, 1));
var service = new OrderService(fakeClock);

🔒 Distributed Locking

Prevent concurrent execution across multiple instances using distributed locks powered by Medallion.Threading.

public class ScheduledReportGenerator(
    IDistributedLockProvider lockProvider) : ScheduledBackgroundService(...)
{
    protected override string LockName => "report-generator";

    protected override async Task ExecuteScheduledAsync(CancellationToken cancellationToken)
    {
        // Only one instance will execute at a time
        await GenerateReports();
    }
}

Supports Multiple Backends:

  • File-based locks (default, good for single-server)
  • Redis locks (for distributed systems)
  • SQL Server locks
  • Azure Blob Storage
  • And more via Medallion.Threading

📅 Scheduled Background Services

Run tasks on a schedule without drift using ScheduledBackgroundService.

public class DailyReportService(
    IServiceScopeFactory serviceScopeFactory,
    IClock clock,
    ILogger<DailyReportService> logger)
    : ScheduledBackgroundService(
        serviceScopeFactory,
        new TimeOfDaySchedule(new TimeOnly(2, 0)), // Run at 2 AM every day
        lockProvider: null, // Optional distributed lock
        clock: clock,
        logger: logger)
{
    protected override async Task ExecuteScheduledAsync(
        IServiceProvider serviceProvider,
        CancellationToken cancellationToken)
    {
        var reportGenerator = serviceProvider.GetRequiredService<IReportGenerator>();
        await reportGenerator.GenerateDailyReport();
    }
}

// Register as a hosted service
services.AddHostedService<DailyReportService>();

Two Schedule Types:

Time of Day Schedule - Run at specific times:

new TimeOfDaySchedule(new TimeOnly(2, 0))  // 2 AM daily

Interval Schedule - Run at regular intervals:

new IntervalSchedule(TimeSpan.FromMinutes(5))  // Every 5 minutes

Drift-Free Execution - Uses anchor points to prevent schedule drift over time.

🔍 Request ID Tracking

Track requests across async boundaries and correlated logs.

public class MyController(IRequestIdHandler requestIdHandler)
{
    public async Task<IActionResult> ProcessRequest()
    {
        using (requestIdHandler.SetRequestId())
        {
            // Request ID is now available in all nested calls
            var currentId = requestIdHandler.GetCurrentRequestId();

            await SomeAsyncOperation(); // Request ID flows through
        }
    }
}

Exception Handling

MagicCSharp provides base exception types for common scenarios:

// Throw when an entity is not found
throw new NotFoundIdException<User>(userId);
throw new NotFoundKeyException<Product>(productKey);

// Helper methods
var user = await userRepository.Get(id);
NotFoundException.ThrowIfNull(user, id);

MagicCSharp.Data - Repository pattern and data access MagicCSharp.Events - Event-driven architecture

License

MIT License - See LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  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 (2)

Showing the top 2 NuGet packages that depend on MagicCSharp:

Package Downloads
MagicCSharp.Events

Event dispatching and handling infrastructure for MagicCSharp applications

MagicCSharp.Data

Data access layer components for MagicCSharp. Includes repository patterns, Entity Framework Core base classes, and pagination support for enterprise C# applications.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.0.13 445 1/12/2026
0.0.12 169 1/12/2026
0.0.11 165 1/12/2026
0.0.9 171 1/12/2026
0.0.7 209 11/1/2025
0.0.6 189 11/1/2025
0.0.4 189 11/1/2025
0.0.2 186 11/1/2025