Search code examples
c#inversion-of-controlsimple-injectormediator

Getting known registration by name in Simple Injector


I am trying to get hold of an instance of a Mediator Request, I thought that using IoC could be viable.

Given the following registrations:

container.Register(typeof(IRequest<>), assemblies);
container.Register(typeof(IRequestHandler<,>), assemblies);

And the following class

public class SubtractRequest: IRequest<int>
{
    public int Left { get; set; }
    public int Right{ get; set; }

    public SubtractRequest() { }

    internal class Handler : IRequestHandler<SubtractRequest, int>
    {
        public Handler() {}

        public string Handle(SubtractRequest request)
        {
            return request.Left - request.Right;
        }
    }
}

What is the best way to resolve an instance by name "SubtractRequest".

My intention is to take a JSON-RPC object, something like this: {"jsonrpc": "2.0", "method": "subtract", "params": [42, 23], "id": 1}, extract the method and then get an instance of that mediator request by that method name.

var request = JsonConvert.DeserializeObject<JsonRpcRequest>(jsonRpcRequest);
var mediatorRequest = GetRequestByName($"{request.Method}Request"); //Via IoC?
//SetParams(mediatorRequest) [todo]
var result = mediator.Send(mediatorRequest).Result;

Basically I am trying to work out how best to implement my GetRequestByName() method to get the correct mediator request.


Solution

  • That's a very broad question and the answer depends on the framework you are using and how this data is coming in, but in general, you will have to:

    • relate the incoming request to a specific IRequest<TResponse> message.
    • extract the TResponse type from that request message type.
    • deserialize the incoming request to the request type.
    • create a closed-generic IRequestHandler<TRequest, TResponse> version based on the request type and the TResponse type.
    • resolve that closed IRequestHandler<TRequest, TResponse> from the Container.
    • Call the handler's Handle method.

    There is a Reference Architecture application on Github called SOLID Services that contains examples on how to do this. Especially take a look at the QueryDelegatingHandler implementation in the Web API project, since it basically does what you need. You might want to download the complete project and play around with it, but do note that the interfaces are named a bit differently. Where you call things IRequest<TResponse> and IRequestHandler<TRequest, TResponse>, the sample project calls this IQuery<TResult> and IQueryHandler<TQuery, TResult>. The idea however is the same.

    One last note, please remove the following registration:

    container.Register(typeof(IRequest<>), assemblies);
    

    IRequest<TResponse> is the definition for a message. A message is a DTO and creating them is not a concern of your DI Container. Registering them into your container might prevent you from succesfully verifying the container's configuration. The only thing you should register is IRequestHandler<,>.

    UPDATE:

    This is basically how your code should look:

    Type requestType = GetRequestTypeByName($"{request.Method}Request");
    
    dynamic request = JsonConvert.DeserializeObject(requestType, jsonRpcRequest);
    
    object result = ((dynamic)mediator).Send(request).Result;
    

    You will have to implement GetRequestTypeByName. This is not something that your DI Container can help you with. Just go over the types in the assembly using reflection and find the type named {request.Method}Request.