0

I have a API that is responsible for processing some documents that are posted to it. To do this I have a controller with endpoints for different file types such as "word", "excel", "image" etc.

This controller is injected with a service responsible for handling the processing.

private readonly IDocProcessor documentProcessor;
public MyController(IDocProcessor docProcessor)
{
    documentProcessor = docProcessor;
}

This is all working perfectly, I now have the requirement to modify the processing, in just the "image" endpoint, due to a limitation in a third-party component.

So, what I want to do is write another service which implements IDocProcessor for images. It will not be used for the others, so will simply throw a NotImplementedException.

What I am struggling with is how I can inject both services into the controller? Or the best practice for doing this.

I have been reading through this question How to register multiple implementations of the same interface in Asp.Net Core? for some pointers, but some of the answers are few years old so wonder if there are new ways to achieve this?

Ryan Thomas
  • 1,724
  • 2
  • 14
  • 27
  • Simply inject as `IEnumerable`, and register all needed implementations like this `.AddSingleton()`. – Alexey Rumyantsev Mar 11 '21 at 14:21
  • 2
    Probably a separate point, but `It will not be used for the others, so will simply throw a NotImplementedException` makes it sound like `IDocProcessor` [violates the interface segregation principle](https://reflectoring.io/interface-segregation-principle/#methods-throwing-exceptions) and could be broken apart into separate interfaces – devNull Mar 11 '21 at 14:27
  • @devNull Yes, you are probably right, there is no reason why it could not implement all of the methods in the future. I personally don't think it's worth me ending up with a interfaces for every type e.g IWordProcessor, IExcelProcessor, IImageProcessor – Ryan Thomas Mar 11 '21 at 14:33
  • @AlexeyRumyantsev This works, but then what should I do? Have a second read only variable for each processor or...? – Ryan Thomas Mar 11 '21 at 14:34
  • Not only does `IDocProcessor` violate the ISP (as @devNull) noted, but likely also the Liskov Substitution Principle, because it contains methods that are not supported, making it impossible to swap implementations. – Steven Mar 11 '21 at 14:43
  • @Steven Okay... so if I refactored to have IOfficeDocumentProcessor, IImageProcessor, and IHtmlProcessor and injected all of those it would solve the problem, but that just doesn't feel right to me haha. – Ryan Thomas Mar 11 '21 at 14:48
  • If it's structurally meaningful to extract the processing parameters or logic from the service, you could inject `IDocProcessor` or something similar, directly just to the action method, with [FromServices] attribute. Or have the processing method on `IDocProcessor` accept an enum that specifies the type of processing. – Leaky Mar 11 '21 at 15:53
  • @RyanThomas If only having collection of objects implementing same interface is useless to achieve your goal, then your problem is definitely in another place. This technique is useful for implementing some chains of plugable abstract handlers or something like this. You should reconsider your architecture and focus firstly on S and I in SOLID. – Alexey Rumyantsev Mar 11 '21 at 16:14
  • Thanks everyone, you're right. I'm going to change and have 3 interfaces, one for Office documents, one for images and one for XML based mark-ups. Then inject these accordingly. – Ryan Thomas Mar 12 '21 at 09:10

0 Answers0