You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I believe it's possible to merge the two design together in a way which would be fast, configurable and while supporting .NET Standard. See #98
Configuration
Add a property to AddMediator that accepts an array of types. At compile time the array is examined and all relevant Mediator types are added from their respective projects. This is used to create a unqiue implementation of IMediator used for this AddMediator call. We can use interceptors or psuedo-interceptors (using caller attributes) to return the respective Mediator class. This way we can have multiple configurable instaces of IMediator in one project.
Using a pseudo-intercetor (interceptor could be used instead)
publicstaticvoidAddMediator(.... parametersgo here,[CallerLineNumber]line=0,[CallerFilePath]file=""){// an efficient condition can be built using the line number, file path length and varying chars in the file path// not sure how much the compiler will doreturn(line, path)switch{(0, _)=> Mediator1.AddMediator(services),(42,{ Length:15})=> Mediator2.AddMediator(services),(42,{ Length:21})=> Mediator3.AddMediator(services),
_ =>thrownew ArgumentOutOfRangeException()}}
There are a variety of different configuration approaches, I chose this one for its simplicity to demonstrate the concept.
Fast type switching
Iirc one of plans for Mediator 3.0 attempted to resolve #7, by analyzing all calls to ValueTask<TResponse> Send<TResponse> and intercepting it with the correct method. Unfortunately this wouldn't be compatible with multiple projects per class as different type could be registered making it impossible to be sure that the relevant method exists on the IMediator implementation and then calling it.
Instead we could use a pseudo-interceptor to generate switch statements based upon known concrete type calls for each Mediator implementation. As each Mediator implementation has different types registered it would be neccessary to emit an exception when an invalid IRequest object is passed into Send.
See #97
// Generated for the Mediator class with types from DogModel and DogHandler registeredpublicValueTask<TResponse>Send<TResponse>(IRequest<TResponse>request,CancellationTokencancellationToken=default,[CallerLineNumber]line=0,[CallerFilePath]file=""){switch(line, path){case(0, _):return Send((DogRequest)request, token);break;case(42,{ Length:15}):return Send((BigDogRequest)request, token);break;case(42,{ Length:21}):thrownew Exception($"This should not happen, unregistered request {request.GetType()}"),// Concrete type not registed to this classdefault:return Send(request, token);}returndefault;}
// Generated for the Mediator class with types from CatModel and CatHandler registeredpublicValueTask<TResponse>Send<TResponse>(IRequest<TResponse>request,CancellationTokencancellationToken=default,[CallerLineNumber]line=0,[CallerFilePath]file=""){switch(line, path){case(0, _):thrownew Exception($"This should not happen, unregistered request {request.GetType()}"),// Concrete type not registed to this classcase(42,{ Length:15}):thrownew Exception($"This should not happen, unregistered request {request.GetType()}"),// Concrete type not registed to this classcase(42,{ Length:21}):return Send((CatRequest)request, token);break;default:return Send(request, token);}returndefault;}
It's possible to achieve similar results using an interceptor by checking the type of IMediator to see if it supports the conrete IRequest object.
The text was updated successfully, but these errors were encountered:
TimothyMakkison
changed the title
Proposal: Design for Mediator 3.0 to support multiple configuration and fast switching.
Proposal: Mediator 3.0 design to support multiple configuration and fast switching.
Nov 22, 2023
Hi, sorry took a while to get to this. Thanks for looking into it! I have some questions and comments
The idea here is supporting multiple calls to AddMediator right? Is there still a root implementation that dispatches to the correct impl? Because the DI Container is supposed to give you only one IMediator right?
Unfortunately this wouldn't be compatible with multiple projects per class
What do you mean here, multiple handlers per request? I'm probably missing something since I'm stuck on how to even get the IMediator instance
Regarding the code samples here with switches, I'd come to the conclusion that there is no fast (enough) switch. What makes these branches OK where the current ones aren't in terms of perf? As in case (42, { Length: 15 }) vs case BigDogRequest.
The idea with .NET 8 interceptors was to eliminate the branches altogether. I think if that design were to collide with the scenario of having multiple impls/projects somehow then I would prefer to drop that to simplify.
That said I don't have a clear picture of the project topology we're targeting in general or with this design, maybe you can write out some example solution that this design would go well with?
I believe it's possible to merge the two design together in a way which would be fast, configurable and while supporting .NET Standard. See #98
Configuration
Add a property to
AddMediator
that accepts an array of types. At compile time the array is examined and all relevant Mediator types are added from their respective projects. This is used to create a unqiue implementation ofIMediator
used for thisAddMediator
call. We can use interceptors or psuedo-interceptors (using caller attributes) to return the respectiveMediator
class. This way we can have multiple configurable instaces ofIMediator
in one project.See #125
Using a pseudo-intercetor (interceptor could be used instead)
There are a variety of different configuration approaches, I chose this one for its simplicity to demonstrate the concept.
Fast type switching
Iirc one of plans for Mediator 3.0 attempted to resolve #7, by analyzing all calls to
ValueTask<TResponse> Send<TResponse>
and intercepting it with the correct method. Unfortunately this wouldn't be compatible with multiple projects per class as different type could be registered making it impossible to be sure that the relevant method exists on theIMediator
implementation and then calling it.Instead we could use a pseudo-interceptor to generate switch statements based upon known concrete type calls for each Mediator implementation. As each Mediator implementation has different types registered it would be neccessary to emit an exception when an invalid
IRequest
object is passed intoSend
.See #97
It's possible to achieve similar results using an interceptor by checking the type of
IMediator
to see if it supports the conreteIRequest
object.The text was updated successfully, but these errors were encountered: