martinothamar / Mediator

A high performance implementation of Mediator pattern in .NET using source generators.
MIT License
2.16k stars 71 forks source link

Support for internal objects that implement IRequest. #118

Open Zodt opened 1 year ago

Zodt commented 1 year ago

Over the years, I have come across many projects in which:

All the listed objects have an internal access modifier, and the .csproj files specify the InternalsVisibleTo tag up to the overlying layers.

At the moment, in my current company, migration from Jimmy Bogart's MediatR to the current implementation is being discussed due to its advantages; however, the problem described above forces us, for the time being, to refrain from that.

Description of the problem

In cases where internal Handler, internal request, and internal response are used, Inconsistent accessibility occurs. The problem is that the generated Mediator.Send method is public, when it's input parameter and return value are internal.

Handler

namespace ServiceDesk.Appeals.Application.Handlers;
internal sealed class GetHelloCustomerQueryHandler : IRequestHandler<GetHelloCustomerQuery, GetHelloCustomerQueryResponse>
{
    public ValueTask<GetHelloCustomerQueryResponse> Handle(GetHelloCustomerQuery request, CancellationToken cancellationToken)
    {
        var message = $"Hello, {request.Name}";
        return ValueTask.FromResult(
            new GetHelloCustomerQueryResponse(message)
        );
    }
}

Request

namespace ServiceDesk.Appeals.Domain.Queries;
internal sealed record GetHelloCustomerQuery(string Name) : IRequest<GetHelloCustomerQueryResponse>;

Response

namespace ServiceDesk.Appeals.Application.Queries;
internal sealed record GetHelloCustomerQueryResponse(string UserMessage);

Mediator.Send

namespace ServiceDesk.Appeals.Intermediate; /* indicated in the mediator's registration 
    builder.Services.AddMediator(options => 
        options.Namespace = "ServiceDesk.Appeals.Intermediate"
    );
*/
public sealed partial class Mediator : global::Mediator.IMediator, global::Mediator.ISender, global::Mediator.IPublisher
{
    //...

    /// <summary>
    /// Send a message of type global::MinimalApi.WebApi.Intermediate.GetHelloCustomerQuery.
    /// Throws <see cref="global::System.ArgumentNullException"/> if message is null.
    /// </summary>
    /// <param name="message">Incoming message</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>Awaitable task</returns>
    public global::System.Threading.Tasks.ValueTask<global::MinimalApi.WebApi.Intermediate.GetHelloCustomerQueryResponse> Send(
        global::MinimalApi.WebApi.Intermediate.GetHelloCustomerQuery message,
        global::System.Threading.CancellationToken cancellationToken = default
    )
    {
        ThrowIfNull(message, nameof(message));

        return _diCacheLazy.Value.Wrapper_For_MinimalApi_WebApi_Intermediate_GetHelloCustomerQuery.Handle(message, cancellationToken);
    }

    //...
}

Intended solution

Based on the access modifier of the object implementing the IRequest, set the necessary overload of the IMediator.Send method with the corresponding access modifier.

martinothamar commented 11 months ago

Hey, sorry about the late reply, I've started work on improving this as suggested in #131

martinothamar commented 11 months ago

This is available in 3.0.0-preview.15