ivanpaulovich / FluentMediator

:twisted_rightwards_arrows: FluentMediator is an unobtrusive library that allows developers to build custom pipelines for Commands, Queries and Events.
http://paulovich.net/FluentMediator/
Apache License 2.0
192 stars 17 forks source link

Support Nested Pipelines #7

Open ivanpaulovich opened 5 years ago

ivanpaulovich commented 5 years ago

It would be cool if the "Return" operator could start a second pipeline using the "Response" as the Request for the following pipeline.

qpippop commented 4 years ago

I'm trying to implement this. Here's some code how it may work

services.AddFluentMediator(m =>
{
    m.On<PingRequest>().PipelineAsync()
        .Return<PingResponse, IPingHandler>(
            (handler, req) => handler.MyCustomFooBarAsync(req))
        .WithContinuation<PingResponse>() // returns IPipelineAsyncBuilder<PingResponse>
            .Return<SecondPingResponse, ISecondPingHandler>(
                (ISecondPingHandler handler, PingResponse req) => handler.MySecondFooBarAsync(req));
});
// ... some code ...
var response = await mediator.SendAsync<SecondPingResponse>(ping); // We specify the last type from pipe chain

In Pipeline.cs

// ... some code ...
private readonly IPipelineAsync? _nestedPipe;
// ... some code ...
public Pipeline(IMethodCollection<Method<Func<object, object, Task>>> methods, IDirect? direct, Type requestType, string? name, IPipelineAsync? nestedPipe)
{
    _methods = methods;
    _direct = direct;
    RequestType = requestType;
    Name = name;
    _nestedPipe = nestedPipe;
}

public async Task<TResult> SendAsync<TResult>(GetService getService, object request)
{
    if (_direct is null)
    {
        throw new ReturnFunctionIsNullException("The return function is null. SendAsync<TResult> method not executed.");
    }

    foreach (var handler in _methods.GetMethods())
    {
        var concreteHandler = getService(handler.HandlerType);
        await handler.Action(concreteHandler, request);
    }
    // NOTE: should we return object to avoid InvalidCastException?
    // NOTE: because TResult in our case - result type of the nested pipe
    var result = await _direct.SendAsync<TResult /* replace with non generic SendAsync */>(getService, request!) !;
    if (_nestedPipe is IPipelineAsync)
    {
        return await _nestedPipe.SendAsync<TResult>(getService, result!);
    }

    return result;
}

Am i on the right direction? Should i continue to work on it, not sure i'm competent enough..

ivanpaulovich commented 4 years ago

Hey @qpippop!

You were narrow on what I'd like to see. To avoid the invalid cast, what if:

if (_nestedPipe is IPipelineAsync)
{
    var result = await _direct.SendAsync<object>(getService, request!) !;
    return await _nestedPipe.SendAsync<TResult>(getService, result!);
}
else
{
    return await _nestedPipe.SendAsync<TResult>(getService, result!);
}

Would that work?

ivanpaulovich commented 4 years ago

I am looking forward the PR. Hope we could merge it soon ☕️