jbogard / MediatR

Simple, unambitious mediator implementation in .NET
Apache License 2.0
11k stars 1.16k forks source link

Possibility to use Send(object) and Publish(object) without the constraint of IRequest or INotification interfaces #911

Closed Laurianti closed 1 year ago

Laurianti commented 1 year ago

Currently, MediatR requires that messages sent through the Send(object) and Publish(object) methods implement the corresponding IRequest or INotification interfaces. However, there might be a need to send or publish objects without being required to implement these interfaces.

Despite IMediator providing these overloads, it is currently not possible to send or publish objects without them being required to implement these interfaces.

When Send(object) or Publish(object) is used with an object that does not implement IRequest or INotification, an exception is thrown. This constraint restricts the usage to only objects that implement these interfaces.

I would suggest exploring the possibility of allowing the usage of Send(object) and Publish(object) without the constraint of IRequest or INotification interfaces. This would provide greater flexibility in sending messages without the need for such constraints.

I understand that the use of IRequest and INotification interfaces offers a standardized structure for message handling, but the possibility to send and publish objects without interfaces could simplify development in scenarios where advanced message handling is not required. Additionally, there are cases where direct control over types is not available, and there is no need to decouple them or wrap them inside an object that implements the interfaces, making the usage without interfaces even more advantageous.

I kindly request the community members to discuss this proposal and evaluate its feasibility, considering the potential implications and advantages it could offer for the flexibility of MediatR usage. Furthermore, it would be interesting to understand why it should be a required constraint for messages to implement these interfaces, especially considering that other Mediators, such as MassTransit, do not have this constraint.

Thank you for your attention and commitment to the development of this library.

jbogard commented 1 year ago

I modeled this more off of NServiceBus, which uses these constraints to provide guardrails for developers so that they do not "publish" requests and "send" notifications. I don't have cases where I use MediatR and don't have control over the types. If types are defined by others, I'll wrap them up because MediatR is only meant to be in-process anyway, not for someone else's arbitrary contracts.

Laurianti commented 1 year ago

Hi Jimmy,

Thank you for your response, I really appreciate it.

I understand that you want to provide clear guidelines to developers, and that makes sense.

However, in our case, we have to deal with contracts from third parties.

We thought about wrapping these contracts in a generic class, but it would prevent us from using inheritance and publishing without knowing the exact type of the request or notification.

class MyRequestBase : IRequest {}

class MyRequestImplementation : MyRequestBase {}

// myRequestBase.GetType() == typeof(MyRequestImplementation)
MySend(MyRequestBase myRequestBase) => _mediator.Send(myRequestBase);

class MyRequestHandler : IRequestHandler<MyRequestImplementation> {} // works
class RequestWrapper<T> : IRequest { T Message; }

// requestWrapper.Message.GetType() == typeof(MyRequestImplementation)
MySend(RequestWrapper<MyRequestBase> requestWrapper) => _mediator.Send(requestWrapper);

class MyRequestHandler : IRequestHandler<RequestWrapper<MyRequestImplementation>> {} // doesn't work

Unfortunately, wrapping doesn't solve our problem in these situations.

Thank you for considering our perspective.

jbogard commented 1 year ago

The way you make your request wrapper work is to create a concrete closed type, something like public class MyRequestWrapper : RequestWrapper<MyRequestImplementation>. Don't do a bunch of open generic types.