In this section, you'll find descriptions of exceptions that may arise while using 'Pipelines' and guidance on how to debug the source generated dispatcher.
-
1. Pipeline Builder Exceptions
- ProvidedTypeIsNotInterfaceException
- HandlerMethodNotFoundException
- MultipleHandlerMethodsException
- MethodShouldHaveAtLeastOneParameterException
- GenericArgumentsNotFoundException
- HandlerInputTypeMismatchException
- InvalidConstraintLengthException
- ExpectedMethodWithResultException
- ResultTypeCountMismatchException
- GenericTypeCountMismatchException
- GenericTypeMismatchException
- IsGenericMismatchException
- TypeMismatchException
- DispatcherMethodInputTypeMismatchException
- ParameterCountMismatchException
- ParameterTypeMismatchException
- TaskReturnTypeMismatchException
- VoidAndValueMethodMismatchException
- InterfaceImplementationException
- ConstructorValidationException
One of the types provided to the pipeline is not an interface.
public class SampleClass{}
services
.AddPipeline()
.AddInput(typeof(SampleClass))
.AddHandler(typeof(IHandler<>), handlersAssembly)
.AddDispatcher<IDispatcher>(dispatcherAssembly)
.Build();
...- Ensure all provided types are interfaces.
- Always use typeof when providing types.
public interface IInput{}
services
.AddPipeline()
.AddInput(typeof(IInput))
.AddHandler(typeof(IHandler<>), handlersAssembly)
.AddDispatcher<IDispatcher>(dispatcherAssembly)
.Build();
...A provided type doesn't define a handle method.
public interface IHandler<in TInput, TResult> where TInput : IInput<TResult> where TResult: class
{}
...Define a Handle method in the handler.
public interface IHandler<in TInput, TResult> where TInput : IInput<TResult> where TResult: class
{
public Task<TResult> HandleAsync(TInput input, CancellationToken token);
}...
Multiple methods were defined in the provided type. Each interface must contain only a single method.
public interface IHandler<in TInput, TResult> where TInput : IInput<TResult> where TResult: class
{
public Task<TResult> HandleAsync(TInput input, CancellationToken token);
public Task<TResult> HandleAsync(TInput input);
}Remove extra methods from the interface.
public interface IHandler<in TInput, TResult> where TInput : IInput<TResult> where TResult: class
{
public Task<TResult> HandleAsync(TInput input, CancellationToken token);
}The defined Handle method does not have any parameters.
public interface IHandler<in TInput, TResult> where TInput : IInput<TResult> where TResult: class
{
public Task<TResult> HandleAsync();
}Ensure the method has at least one parameter, which should be of the Input Type.
public interface IHandler<in TInput, TResult> where TInput : IInput<TResult> where TResult: class
{
public Task<TResult> HandleAsync(TInput input, CancellationToken token);
}The Handler lacks generic arguments. At least one generic argument is required, specifically for Input.
public interface IHandler
{
Task HandleAsync(IInput input, CancellationToken token);
}Ensure the Handler has at least one generic argument representing the Input.
public interface IHandler<in TInput> where TInput : IInput
{
Task HandleAsync(TInput input, CancellationToken token);
}The first generic argument doesn't have the Input type as its constraint.
public interface IInput
{ }
public interface IHandlerWithResult<in TInput, TResult>
where TInput : IInputWithResult<TResult>
where TResult : class
{
Task<TResult> HandleAsync(TInput input, CancellationToken token);
}The first generic argument of the handler must use the Input type, specified in the AddInput() method, as its constraint.
public interface IInput
{ }
public interface IHandler<in TInput> where TInput : IInput
{
Task HandleAsync(TInput input, CancellationToken token);
}The first generic argument lacks constraints. At least one constraint is required, specifically for the Input type.
public interface IInput
{ }
public interface IHandler<in TInput>
{
public Task HandleAsync(TInput input, CancellationToken token);
}Ensure the first generic argument of the handler has the Input type as its constraint.
public interface IInput
{ }
public interface IHandler<in TInput> where TInput : IInput
{
Task HandleAsync(TInput input, CancellationToken token);
}Given the Input generic arguments, it was anticipated that the Handler/Dispatcher would have a method returning a result, but a void was found instead.
public interface IInput<TResult>
{}
public interface IDispatcher
{
public Task SendAsync<TResult>(IInput<TResult> request, CancellationToken token);
}
public interface IHandler<in TInput, TResult> where TInput : IInput<TResult>
{
public Task HandleAsync(TInput command, CancellationToken token);
}Ensure that result types align with the Input generic arguments.
public interface IInput<TResult>
{}
public interface IDispatcher
{
public Task<TResult> SendAsync<TResult>(IInput<TResult> request, CancellationToken token);
}
public interface IHandler<in TInput, TResult> where TInput : IInput<TResult>
{
public Task<TResult> HandleAsync(TInput command, CancellationToken token);
}The number of result types defined by the Input generic arguments does not match the number of result types found in the Dispatcher/Handler method.
public interface IInputWithResult<TResult>
{ }
public interface IHandler<in TInput, TResult>
where TInput : IInputWithResult<TResult> where TResult : class
{
public Task<(TResult, bool)> HandleAsync(TInput command, CancellationToken token);
}
public interface IDispatcher
{
public Task<(TResult, TResult2)> SendAsync<TResult,TResult2>(IInputWithResult<TResult> request, CancellationToken token);
}Ensure that the Dispatcher/Handler method's result types align with the Input generic arguments.
public interface IInputWithResult<TResult>
{ }
public interface IHandler<in TInput, TResult>
where TInput : IInputWithResult<TResult> where TResult : class
{
public Task<TResult> HandleAsync(TInput command, CancellationToken token);
}
public interface IDispatcher
{
public Task<TResult> SendAsync<TResult>(IInputWithResult<TResult> request, CancellationToken token);
}The number of constraints in the result types defined in the Handler or Dispatcher does not match.
public interface IInputType
{}
public interface IHandler<in TInput, TResultOne, TResultTwo> where TInput : IInputType
where TResultOne : IResultOne
where TResultTwo : IResultTwo
{
public Task<(TResultOne,TResultTwo)> HandleAsync(TInput command, CancellationToken token);
}
public interface IDispatcher
{
public Task<(TResult, TResultTwo)> SendAsync<TResult, TResultTwo>(IInputType inputType)
where TResult : IResultOne;
}Ensure that the number of constraints on result types in the Handler or Dispatcher matches.
public interface IInputType
{}
public interface IHandler<in TInput, TResultOne, TResultTwo> where TInput : IInputType
where TResultOne : IResultOne
where TResultTwo : IResultTwo
{
public Task<(TResultOne,TResultTwo)> HandleAsync(TInput command, CancellationToken token);
}
public interface IDispatcher
{
public Task<(TResult, TResultTwo)> SendAsync<TResult, TResultTwo>(IInputType inputType)
where TResult : IResultOne
where TResultTwo : IResultTwo;
}There is a mismatch in the generic type constraints between the Handler and the Dispatcher, or between the Input result types constraint and the Handler or Dispatcher.
public interface IInputType
{ }
public interface IHandler<in TInput, TResultOne, TResultTwo> where TInput : IInputType
where TResultOne : IResultOne
where TResultTwo : IResultTwo
{
public Task<(TResultOne,TResultTwo)> HandleAsync(TInput command, CancellationToken token);
}
public interface IDispatcher
{
public Task<(TResult, TResultTwo)> SendAsync<TResult, TResultTwo>(IInputType inputType, CancellationToken token)
where TResult : IResultOne where TResultTwo : struct;
}Ensure that both the Handler and Dispatcher are consistent in their return types, and their generic type constraints match accordingly.
public interface IHandler<in TInput, TResultOne, TResultTwo> where TInput : IInputType
where TResultOne : IResultOne
where TResultTwo : IResultTwo
{
public Task<(TResultOne,TResultTwo)> HandleAsync(TInput command, CancellationToken token);
}
public interface IDispatcher
{
public Task<(TResult, TResultTwo)> SendAsync<TResult, TResultTwo>(IInputType inputType, CancellationToken token)
where TResult : IResultOne where TResultTwo : IResultTwo;
}There's a mismatch between the result types in the Handler and Dispatcher: one is generic, while the other is not.
public interface IHandler<in TInput, TResult> where TInput : IInputType
where TResult : IResult
{
public Task<TResult> HandleAsync(TInput input, CancellationToken token);
}
public interface IDispatcher
{
public Task<string> SendAsync(IInputType inputType, CancellationToken token);
}Ensure that both the Handler and Dispatcher return the same type of result.
public interface IHandler<in TInput, TResult> where TInput : IInputType
where TResult : IResult
{
public Task<TResult> HandleAsync(TInput input, CancellationToken token);
}
public interface IDispatcher
{
public Task<TResult> SendAsync<TResult>(IInputType inputType, CancellationToken token);
}There's a mismatch between the non generic result types in the Handler and Dispatcher.
public interface IHandler<in TInput> where TInput : IInputType
{
public int HandleAsync(TInput input, CancellationToken token);
}
public interface IDispatcher
{
public string SendAsync(IInputType input);
}Ensure that both the Handler and Dispatcher return the same type of result.
public interface IHandler<in TInput> where TInput : IInputType
{
public int HandleAsync(TInput input, CancellationToken token);
}
public interface IDispatcher
{
public int SendAsync(IInputType input);
}The Dispatcher's Handle method does not use the expected Input type as its first parameter.
public interface IInput
{ }
public interface IDispatcher
{
public Task SendAsync(int request, CancellationToken token);
}Ensure that the Input type is used as the first parameter in the method.
public interface IInput
{ }
public interface IDispatcher
{
public Task SendAsync(IInput request, CancellationToken token);
}The Handler and Dispatcher methods have a mismatch in the number of parameters.
public interface IHandler<in TInput> where TInput : IInputType
{
public Task<string> HandleAsync(TInput input, CancellationToken token);
}
public interface IDispatcher
{
public Task<string> SendAsync(IInputType inputType, int index, CancellationToken token);
}Ensure that the Handler and Dispatcher methods have the same number and type of parameters.
public interface IHandler<in TInput> where TInput : IInputType
{
public Task<string> HandleAsync(TInput input, int index, CancellationToken token);
}
public interface IDispatcher
{
public Task<string> SendAsync(IInputType inputType, int index, CancellationToken token);
}There's a mismatch in the type of a parameter between the Handler and Dispatcher methods.
public interface IHandler<in TInput> where TInput : IInputType
{
public Task<string> HandleAsync(TInput command, int index, CancellationToken token);
}
public interface IDispatcher
{
public Task<string> SendAsync(IInputType inputType, string text, CancellationToken token);
}Ensure that the parameters in both the Handler and Dispatcher methods are of the same type and order.
public interface IHandler<in TInput> where TInput : IInputType
{
public Task<string> HandleAsync(TInput command, string text, CancellationToken token);
}
public interface IDispatcher
{
public Task<string> SendAsync(IInputType inputType, string text, CancellationToken token);
}There's a mismatch in the return type of the Handler and Dispatcher methods. One method returns a Task<> type, while the other does not.
public interface IHandler<in TInput> where TInput : IInputType
{
public string HandleAsync(TInput command, CancellationToken token);
}
public interface IDispatcher
{
public Task<string> SendAsync(IInputType inputType, CancellationToken token);
}Ensure that both the Handler and Dispatcher methods have consistent return types. If one returns a Task<>, the other should too.
public interface IHandler<in TInput> where TInput : IInputType
{
public Task<string> HandleAsync(TInput command, CancellationToken token);
}
public interface IDispatcher
{
public Task<string> SendAsync(IInputType inputType, CancellationToken token);
}There's an inconsistency in the return types between the Handler and Dispatcher methods. One method returns a value, while the other returns void.
public interface IHandler<in TInput> where TInput : IInputType
{
public string HandleAsync(TInput input, CancellationToken token);
}
public interface IDispatcherVoid
{
public void SendAsync(IInputType inputType);
}Ensure both the Handler and Dispatcher methods have matching return types. Either both should return a value, or both should return void.
public interface IHandler<in TInput> where TInput : IInputType
{
public Task<string> HandleAsync(TInput input, CancellationToken token);
}
public interface IDispatcher
{
public Task<string> SendAsync(IInputType inputType);
}The decorator class is not implementing the expected interface, leading to a type mismatch between the expected and actual generic types.
public interface IHandler<in TInput> where TInput : IInput
{
public Task HandleAsync(TInput input, CancellationToken token);
}
public class Decorator : IDifferentHandler<InputWithResult, Result>
{
private readonly IDifferentHandler<InputWithResult, Result> _handler;
public Decorator(IDifferentHandler<InputWithResult, Result> handler)
{
_handler = handler;
}
public async Task<Result> HandleAsync(InputWithResult input, CancellationToken token)
{
return await _handler.HandleAsync(input, token);
}
}Ensure that the decorator class implements the correct interface, which it's intended to decorate. The generic type parameters of the decorator should align with those of the interface it should implement.
public class Decorator : IHandler<IInput, Result>
{
private readonly IHandler<IInput, Result> _handler;
public Decorator(IHandler<IInput, Result> handler)
{
_handler = handler;
}
public async Task<Result> HandleAsync(IInput input, CancellationToken token)
{
return await _handler.HandleAsync(input, token);
}
}The decorator's constructor does not have the required handler dependency (either it's missing or invalid).
public interface IHandler<in TInput> where TInput : IInput
{
public Task HandleAsync(TInput input, CancellationToken token);
}
public class Decorator : IHandler<IInput, Result>
{
public Decorator()
{ }
public async Task<Result> HandleAsync(IInput input, CancellationToken token)
{
return Task.CompletedTask;
}
}Include the required handler dependency in the decorator's constructor and use it in the HandleAsync method:
public class Decorator : IHandler<IInput, Result>
{
private readonly IHandler<IInput, Result> _handler;
public Decorator(IHandler<IInput, Result> handler)
{
_handler = handler;
}
public async Task<Result> HandleAsync(IInput input, CancellationToken token)
{
return await _handler.HandleAsync(input, token);
}
}The dispatcher registration is missing from the Dependency Injection Container. By default, the AddDispatcher() method use the Dispatcher Generated by Source generator. The primary reason the Generated Dispatcher isn't found is due to the absence of package in project Pipelines.WrapperDispatcherGenerator.
Add package DumplingsDevs.Pipelines.WrapperDispatcherGenerator or change Dispatcher options to UseReflectionProxyImplementation=true
dotnet add package DumplingsDevs.Pipelines.WrapperDispatcherGenerator
or
services
.AddPipeline()
.AddInput(typeof(ICommand<>))
.AddHandler(typeof(ICommandHandler<,>), assembly)
.AddDispatcher<ICommandDispatcher>(new DispatcherOptions(true)
.Build()Another possible issue could be related to problems during the generation of the dispatcher. In this situation, we recommend creating an issue on our GitHub repository: https://github.com/DumplingsDevs/Pipelines/issues. Please provide a detailed description of your Pipelines configuration to help us assist you better.
The handler implementation for the provided input was not found. In most cases, this suggests that while the input has been defined, its corresponding handler implementation is missing.
...
public record ExampleCommand2(string Value) : IInput<ExampleCommandResult>;
...
var request = new ExampleCommand2("My test request");
var result = await _dispatcher.SendAsync(request, new CancellationToken());For every defined Input, ensure that a corresponding Handler is implemented.
public record ExampleCommand2(string Value) : IInput<ExampleCommandResult>;
public class ExampleHandler : IHandler<ExampleCommand2, ExampleCommandResult>
{
public Task<ExampleCommandResult> HandleAsync(ExampleCommand2 input, CancellationToken token)
{
return Task.FromResult(new ExampleCommandResult(input.Value));
}
}
...
var request = new ExampleCommand2("My test request");
var result = await _dispatcher.SendAsync(request, new CancellationToken());The dispatcher was invoked with a null value instead of an actual input object.
var result = await _dispatcher.SendAsync(null, new CancellationToken());Ensure that you provide a valid input object to the method and avoid passing null values.
var request = new ExampleCommand2("My test request");
var result = await _dispatcher.SendAsync(request, new CancellationToken());Something went wrong with source generator and generated Dispatcher. Please create issue on Github with types and builder description.
At least one assembly is not provided to AddHandler() method or WithDecorators
services
.AddPipeline()
.AddInput(typeof(ICommand<>))
.AddHandler(typeof(ICommandHandler<,>))
.AddDispatcher<ICommandDispatcher>(new DispatcherOptions(true,dispatcherAssembly))
.Build()services
.AddPipeline()
.AddInput(typeof(ICommand<>))
.AddHandler(typeof(ICommandHandler<,>), assembly)
.AddDispatcher<ICommandDispatcher>(new DispatcherOptions(true, dispatcherAssembly))
.WithDecorators(x => x.WithNameContaining("Attribute"))
.Build()Ensure that you provided at least one assembly to AddHandler() or WithDecorators.
services
.AddPipeline()
.AddInput(typeof(ICommand<>))
.AddHandler(typeof(ICommandHandler<,>), handlersAssembly)
.AddDispatcher<ICommandDispatcher>(new DispatcherOptions(true,dispatcherAssembly))
.Build()services
.AddPipeline()
.AddInput(typeof(ICommand<>))
.AddHandler(typeof(ICommandHandler<,>), handlersAssembly)
.AddDispatcher<ICommandDispatcher>(new DispatcherOptions(true, dispatcherAssembly))
.WithDecorators(x => x.WithNameContaining("Attribute")), decoratorsAssembly)
.Build()In some cases, the Pipelines source generator may generate code that cannot be compiled. This can happen when users create pipelines builders with objects that are not supported by the Pipelines library. If you suspect that this is the issue you're facing, there are specific symptoms to look out for and steps to diagnose and resolve the problem.
- Build Result Contains Errors in "Pipelines.WrapperDispatcherGenerator" Files:
- One indication of a problem with the generated dispatcher is the presence of build errors that are logged in files with "Pipelines.WrapperDispatcherGenerator" in the file path. These errors typically relate to issues within the generated code.
- Build Errors Disappear After Removing the "Pipelines.WrapperDispatcherGenerator" NuGet Package:
- Another significant clue is that when you remove the "Pipelines.WrapperDispatcherGenerator" NuGet package from your project, the build errors related to it no longer appear. This suggests that the issue is indeed connected to the generated dispatcher.
Additionally, if the problem you're facing is a blocker for your project, a workaround is to remove the "Pipelines.WrapperDispatcherGenerator" package and utilize the built-in ProxyImplementation that uses reflection to handle dispatcher functionality. To enable the proxy, set the option UseReflectionProxyImplementation to true. Here's a code example:
services
.AddPipeline()
.AddInput(typeof(ICommand<>))
.AddHandler(typeof(ICommandHandler<,>), assembly)
.AddDispatcher<ICommandDispatcher>(new DispatcherOptions(true)
.Build()We encourage you to report any bug as a GitHub issue on our repository at https://github.com/DumplingsDevs/Pipelines/issues. Your feedback helps us improve the library and assists other users who may encounter similar problems.