NetEvolve.Pulse.SourceGeneration 0.67.36

Prefix Reserved
dotnet add package NetEvolve.Pulse.SourceGeneration --version 0.67.36
                    
NuGet\Install-Package NetEvolve.Pulse.SourceGeneration -Version 0.67.36
                    
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="NetEvolve.Pulse.SourceGeneration" Version="0.67.36">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="NetEvolve.Pulse.SourceGeneration" Version="0.67.36" />
                    
Directory.Packages.props
<PackageReference Include="NetEvolve.Pulse.SourceGeneration">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
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 NetEvolve.Pulse.SourceGeneration --version 0.67.36
                    
#r "nuget: NetEvolve.Pulse.SourceGeneration, 0.67.36"
                    
#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 NetEvolve.Pulse.SourceGeneration@0.67.36
                    
#: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=NetEvolve.Pulse.SourceGeneration&version=0.67.36
                    
Install as a Cake Addin
#tool nuget:?package=NetEvolve.Pulse.SourceGeneration&version=0.67.36
                    
Install as a Cake Tool

NetEvolve.Pulse.SourceGeneration

NuGet Version NuGet Downloads License

NetEvolve.Pulse.SourceGeneration is a Roslyn source generator for the Pulse CQRS mediator library. It automatically generates DI registration code for handler classes annotated with [PulseHandler], [PulseHandler<TMessage>], or [PulseGenericHandler], eliminating manual service registrations and catching missing or duplicate registrations at compile time.

Features

  • Compile-Time Code Generation: Emits IServiceCollection extension methods with TryAdd* registrations for all annotated handlers
  • Closed Open-Generic Handler Support: [PulseHandler<TMessage>] closes open-generic handler classes for specific message types at compile time; multiple attributes on the same class register it for multiple message types
  • Pure Open-Generic Handler Support: [PulseGenericHandler] registers an open-generic handler class directly as an open-generic DI service (e.g. services.TryAddScoped(typeof(ICommandHandler<,>), typeof(MyHandler<,>))), allowing the DI container to resolve any closed variant at runtime
  • Incremental Generator: Uses ForAttributeWithMetadataName for fast, IDE-friendly discovery
  • Configurable Lifetimes: Supports Singleton, Scoped (default), and Transient via PulseServiceLifetime enum
  • Assembly-Derived Method Name: Generated method name is derived from AssemblyName with dots removed and PulseHandlers appended (e.g., MyProjectAddMyProjectPulseHandlers)
  • Root Namespace Support: Generated namespace uses the consuming project's RootNamespace
  • Multi-Interface Instance Sharing: Handlers implementing multiple interfaces are registered as the concrete type once; each interface resolves via a factory delegate so all share the same instance within the configured lifetime
  • Diagnostics: PULSE001–PULSE006 covering missing handler interfaces, duplicate registrations, open-generic type annotations, and invalid or incompatible explicit message type arguments
  • Fully Qualified Names: All generated code uses global:: prefixed type names to avoid namespace conflicts

Installation

NuGet Package Manager

Install-Package NetEvolve.Pulse.SourceGeneration

.NET CLI

dotnet add package NetEvolve.Pulse.SourceGeneration

PackageReference

<PackageReference Include="NetEvolve.Pulse.SourceGeneration" Version="x.x.x" />

Quick Start

using NetEvolve.Pulse.Attributes;
using NetEvolve.Pulse.Extensibility;

// 1. Annotate your handler classes
[PulseHandler]
public class CreateOrderHandler
    : ICommandHandler<CreateOrderCommand, OrderResult>
{
    public Task<OrderResult> HandleAsync(
        CreateOrderCommand command,
        CancellationToken cancellationToken) =>
        Task.FromResult(new OrderResult(Guid.NewGuid()));
}

// 2. Call the generated extension method in your startup code
// Method name is derived from your assembly name
services.AddMyProjectPulseHandlers();

Usage

Handler Registration

Annotate handler classes with [PulseHandler] and the generator emits TryAddScoped, TryAddSingleton, or TryAddTransient calls based on the configured lifetime:

[PulseHandler] // Scoped (default)
public class CreateOrderHandler : ICommandHandler<CreateOrderCommand, OrderResult> { ... }

[PulseHandler(Lifetime = PulseServiceLifetime.Singleton)]
public class GetCachedDataHandler : IQueryHandler<GetCachedDataQuery, CachedData> { ... }

[PulseHandler(Lifetime = PulseServiceLifetime.Transient)]
public class NotificationHandler : IEventHandler<OrderCreatedEvent> { ... }

Closed Open-Generic Handler Registration

Use [PulseHandler<TMessage>] to close an open-generic handler class for a specific message type. Apply the attribute multiple times to register the same class for several message types:

// Register the generic handler for two concrete command types
[PulseHandler<CreateOrderCommand>]
[PulseHandler<CancelOrderCommand>]
public class GenericCommandHandler<TCmd, TResult> : ICommandHandler<TCmd, TResult>
    where TCmd : ICommand<TResult>
{
    public Task<TResult> HandleAsync(TCmd command, CancellationToken cancellationToken) =>
        Task.FromResult(default(TResult)!);
}

// Register with a non-default lifetime
[PulseHandler<OrderShippedEvent>(Lifetime = PulseServiceLifetime.Singleton)]
public class GenericAuditEventHandler<TEvent> : IEventHandler<TEvent>
    where TEvent : IEvent
{
    public Task HandleAsync(TEvent message, CancellationToken cancellationToken) =>
        Task.CompletedTask;
}

Pure Open-Generic Handler Registration

Use [PulseGenericHandler] when you want a single open-generic class to handle any closed variant of a message type, resolved by the DI container at runtime. The generator emits a typeof()-based registration instead of a closed-type one:

// Handles ICommandHandler<TCommand, TResult> for any TCommand : ICommand<TResult>
[PulseGenericHandler]
public class GenericCommandHandler<TCommand, TResult> : ICommandHandler<TCommand, TResult>
    where TCommand : ICommand<TResult>
{
    public Task<TResult> HandleAsync(TCommand command, CancellationToken cancellationToken) =>
        Task.FromResult(default(TResult)!);
}

// Generated: services.TryAddScoped(
//     typeof(ICommandHandler<,>), typeof(GenericCommandHandler<,>));

// Handles IEventHandler<TEvent> for any TEvent : IEvent, registered as Singleton
[PulseGenericHandler(Lifetime = PulseServiceLifetime.Singleton)]
public class GenericAuditEventHandler<TEvent> : IEventHandler<TEvent>
    where TEvent : IEvent
{
    public Task HandleAsync(TEvent message, CancellationToken cancellationToken) =>
        Task.CompletedTask;
}

// Generated: services.TryAddSingleton(
//     typeof(IEventHandler<>), typeof(GenericAuditEventHandler<>));

Note: [PulseHandler] on an open-generic class produces a PULSE004 error — use [PulseGenericHandler] instead when you need a true open-generic DI registration.

Supported Handler Interfaces

Interface Description
ICommandHandler<TCommand> Void command handler (single type parameter)
ICommandHandler<TCommand, TResponse> Command handler with response
IQueryHandler<TQuery, TResponse> Query handler
IEventHandler<TEvent> Event handler (multiple handlers per event are valid)
IStreamQueryHandler<TQuery, TResponse> Streaming query handler

Diagnostics

Id Severity Description
PULSE001 Error Type is annotated with [PulseHandler] but does not implement any known Pulse handler interface.
PULSE002 Warning Multiple [PulseHandler] types implement the same command or query handler contract. Events are excluded — multiple event handlers are valid.
PULSE004 Error Type annotated with [PulseHandler] is an open generic type and cannot be automatically registered. Use [PulseHandler<TMessage>] for closed registrations or [PulseGenericHandler] for open-generic DI registrations.
PULSE005 Error The type argument T passed to [PulseHandler<T>] does not implement any known Pulse message interface (ICommand, ICommand<T>, IQuery<T>, IEvent, or IStreamQuery<T>).
PULSE006 Error A closed registration for the given message type cannot be constructed because the handler does not implement a compatible handler interface or not all type parameters can be inferred from the message type.

Requirements

  • .NET 8.0, .NET 9.0, or .NET 10.0
  • NetEvolve.Pulse.Attributes package for the [PulseHandler] attribute
  • NetEvolve.Pulse.Extensibility package for handler interfaces

Documentation

For complete documentation, please visit the official documentation.

Contributing

Contributions are welcome! Please read the Contributing Guidelines before submitting a pull request.

Support

License

This project is licensed under the MIT License - see the LICENSE file for details.


Made with ❤️ by the NetEvolve Team

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has 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
0.67.36 105 5/10/2026