Blackwing.Generator 1.0.0-beta.38

This is a prerelease version of Blackwing.Generator.
dotnet add package Blackwing.Generator --version 1.0.0-beta.38
                    
NuGet\Install-Package Blackwing.Generator -Version 1.0.0-beta.38
                    
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="Blackwing.Generator" Version="1.0.0-beta.38" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Blackwing.Generator" Version="1.0.0-beta.38" />
                    
Directory.Packages.props
<PackageReference Include="Blackwing.Generator" />
                    
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 Blackwing.Generator --version 1.0.0-beta.38
                    
#r "nuget: Blackwing.Generator, 1.0.0-beta.38"
                    
#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 Blackwing.Generator@1.0.0-beta.38
                    
#: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=Blackwing.Generator&version=1.0.0-beta.38&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Blackwing.Generator&version=1.0.0-beta.38&prerelease
                    
Install as a Cake Tool

Blackwing

Build Action Publish Action License .NET 8 .NET 9 Downloads

Blackwing is the name of your dependable Raven that delivers your requests to the correct handler and returns with his response.

The library allows you to use the mediator pattern while staying really minimal as it is a source generator that works only on the project you have it installed in combination with the contracts package.

Things the generator will do for you:

  • Generate wrapper handlers that execute your handler inside a pipeline.
  • Generate a DI registration method for all of the handlers generated in the current project.
  • Generate interceptors to intercept the sending of your commands and execute the efficient Send method of the IMediator interface allowing you to pass your request without specifying all of the generics.

Things the interceptor can't do:

  • Can't intercept methods that pass generic/abstract/interface requests as an argument to the Send method. Only concrete classes are supported. if this is breaking for you there is a fallback that can execute requests using reflection (not recommended) or you can use the fast method yourself which requires all of the generic arguments (preferred).

The goal is to be as minimal and straightforward as possible to give the best of both worlds. The library only generates Handlers, Interceptors and the Registration code.

Packages

.NET 9 SDK Required for the preview feature of interceptors.

Package Stable Pre Release
Blackwing.Contracts Contracts Contracts
Blackwing.Generator Generator Generator
Blackwing.Extensions.DependencyInjection Dependency Injection Extensions Dependency Injection Extensions

Usage

Description

  • Blackwing.Contracts
    • Contains the options, message, handler, pipeline and mediator types/interfaces respective to their domain folder.
    • The root of the project contains the sender/mediator interfaces.
    • The Options folder contains the configuration attribute that configures the source generator.
    • The Requests folder contains all of the message, handler and pipeline definitions for the IRequest concept.
  • Blackwing.Generator
    • Source Generator to generate handlers, registration and interception.
  • Blackwing.Extensions.DependencyInjection
    • Default IMediator & ISender implementations for the Microsoft.Extensions.DependencyInjection packages.

Installation:

  • Blackwing.Contracts in projects that you want to store you message objects.
  • Blackwing.Generator in projects that implement the message handlers and in projects that will use the IMediator to send messages (so that the interception is generated).
  • Blackwing.Extensions.DependencyInjection in the project that brings all your shared code together. This is usually the edge/outermost project (ASP.NET Core, Worker, Console etc.)

Configuration

The generator is configured through an assembly attribute eg:

[assembly:BlackwingOptions(ServicesClassPublic = true, ServicesClassName = "MyTestHandlers")]

The available options are:

  • HandlersClassPublic (bool): Allows you to define if the handler wrapper class should be public or internal (defaults to internal).
  • ServicesNamespace (string): Allows you to define the namespace of the service collection extension class (defaults to Microsoft.Extensions.DependencyInjection).
  • ServicesClassPublic (bool): Allows you to define if the service collection extension class should be public or internal (defaults to internal).
  • ServicesClassName (string): Allows you to define the service collection extension class name (defaults to BlackwingServiceCollectionExtensions).
  • ServicesMethodName (string): Allows you to define the service collection extension method name (defaults to AddBlackwingHandlers).
  • DisableInterceptor (bool): Allows you to disable the interceptors generation (defaults to false).

You can also disable the interceptors generation by setting the DisableBlackwingInterceptor MSBuild Property to true.

Getting Started

The section will describe how to get started with Blackwing using the Console sample project as the example.

The sample folder also contains more complex samples.

  1. Add all three packages
dotnet add package Blackwing.Contracts
dotnet add package Blackwing.Generator
dotnet add package Blackwing.Extensions.DependencyInjection
  1. Create IRequest<out TResponse> and Response types
public sealed record Ping(Guid Id) : IRequest<Pong>;

public sealed record Pong(Guid Id);
  1. Implement the IRequestHandler<TRequest, TResponse> interface to handle the request.
public sealed class PingHandler : IRequestHandler<Ping, Pong>
{
    public ValueTask<Pong> Handle(Ping request, CancellationToken cancellationToken)
    {
        Console.WriteLine("4) Returning pong!");
        return new ValueTask<Pong>(new Pong(request.Id));
    }
}
  1. Optionally implement some pipeline behaviors.
public sealed class GenericLoggerHandler<TRequest, TResponse> : IRequestPipeline<TRequest, TResponse> where TRequest : IRequest
{
    public async ValueTask<TResponse> Handle(TRequest message, IRequestPipelineDelegate<TRequest, TResponse> next, CancellationToken cancellationToken)
    {
        Console.WriteLine("1) Running logger handler");
        try
        {
            var response = await next(message, cancellationToken);
            Console.WriteLine("5) No error!");
            return response;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            throw;
        }
    }
}

public sealed class PingValidator : IRequestPipeline<Ping, Pong>
{
    public ValueTask<Pong> Handle(Ping request, IRequestPipelineDelegate<Ping, Pong> next, CancellationToken cancellationToken)
    {
        Console.WriteLine("2) Running ping validator");
        if (request is null || request.Id == default)
            throw new ArgumentException("Invalid input");
        else
            Console.WriteLine("3) Valid input!");

        return next(request, cancellationToken);
    }
}
  1. Register the default mediator implementation to the service collection.
services.AddBlackwing();
  1. Register the generated handlers to the service collection.
services.AddBlackwingHandlers();
  1. Send a message using the IMediator, ISender or Mediator types.
var mediator = serviceProvider.GetRequiredService<IMediator>();

var id = Guid.NewGuid();
var request = new Ping(id);
var response = await mediator.Send(request);

Contributing

For general contribution information you can read the Raven Tail Contributing document.

Local Development

To develop you need:

  1. dotnet 9.0 SDK
  2. Visual Studio or VS Code with the C# extension.
  3. Configured your IDE for the TUnit library.

Project Structure

The project is structured around the idea of feature folders. For example in contracts the root contains the IMediator and ISender interfaces as they are the main feature and the requests folder contains all the requests related interfaces. This is true throughout the project as seen in the generator too where the Requests related generator exists in the Generators/Requests folder.

Executing and Testing your code

As you are developing a new feature or a bug fix you need to test and execute your code and for this you need to get familiar with the tests because this is the easier way of testing the generator output and help in verifying your work.

The tests are split in 3 projects

  • Blackwing.Test: Base project testing generator output using the Verify library. Using the TestBase base class you only need to call the Verify() method and it will search for a file in resources. The convention is this Resources/{Test Class Name}/{Test Method Name}.cs and the verify snapshots will be created at the Snapshots/{Test Class Name}/{Test Method Name}/ directory. The purpose of this is to make sure the generator will always generate the same output for the same input text. For new texts this output will need to be verified as described in the verify library.
  • Blackwing.Test.Integration: Contains tests that use the generator to execute and test it's generated code.
  • Blackwing.Test.NugetIntegration: Contains everything from the Integrations project but with a custom script will publish the generator locally as a nuget package to execute all the integration tests again ensuring nuget packages still work.

Credits

This project was inspired by the Mediator project and it got boostraped using some of it's samples and test cases. I would like to express my gratitude to all the contributors at that project as it helped me shape this project.

Additionally, the project wouldn't be as it is without the excellent articles by Andrew Lock on source generators, which helped a lot especially on testing with Verify.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

  • .NETStandard 2.0

    • No dependencies.

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
1.0.0-beta.38 78 2/27/2025
1.0.0-beta.34 72 2/26/2025