Search code examples
c#dependency-injectionimageresizer

Best way to load the Autorotate plugin using ImageResizer.net without XML config


I am using the following to resize images:

public interface IImageService
{
    byte[] ResizeImage(byte[] imageBytes, ImageFormat format, int thumbnailWidth = 150, bool autoRotate = false);
}

public ImageService : IImageService
{

    public byte[] ResizeImage(byte[] imageBytes, ImageFormat format, int thumbnailWidth = 150, bool autoRotate = false)
    {
        byte[] outputBytes = null;
        using (var outStream = new MemoryStream())
        {
            new ImageResizer.Plugins.Basic.AutoRotate().Install(Config.Current);
            var r = new ResizeSettings { Width = thumbnailWidth, Quality = 90, Mode = FitMode.Max };
            r.Add("autorotate", "true");
            ImageBuilder.Current.Build(imageBytes, outStream, r);
            outputBytes = outStream.ToArray();
        }

        return outputBytes;
    }

}

The code works, but the API leaves my code looking unpleasant, namely, the lines:

new ImageResizer.Plugins.Basic.AutoRotate().Install(Config.Current);

and:

ImageBuilder.Current.Build(imageBytes, outStream, r);

As you can see, I've wrapped the entire method in a class and interface so I can unit test my other code without these dependencies, but still it looks nasty.

Is there a nicer way to do this?


Solution

  • From perspective of the SOLID principles, what you're doing looks fine, because:

    • This class has a single responsibility, keeping the class small and maintainable and thus adhering to the Single Responsibility Principle.
    • Because of the defined interface, you can add cross-cutting concerns such as caching, without having to change both this implementation and the consumers, thus adhering to the Open/Closed Principle.
    • You defined the interface to be narrow (one method) and its API is described in a way that is useful to the consumer (instead of copying the API of the external library), you are effectively able to hide the external library from the core part of your application, thus adhering to the Interface Segregation Principle.
    • Since you define an abstraction that doesn't specify any details about the given implementation, you allow the application to be easily tested and allows you to swap the implementation without having to make any changes to the client, even allowing you to deploy the implementation separately - if you wish, which means you are adhering to the Dependency Inversion Principle.

    So from a design and maintainability perspective, I would say what you're doing is very good.

    You have some concerns about the code inside this class. I don't see a problem here. Yes, I agree, the API that ImageResizer exposes is ugly, but there's not much you can do about this - nor should do. You have elegantly abstracted away this ugly API from your application. Your interface is the port and the ImageService is the adapter between your application and ImageResizer. So it has to deal with the ImageResizer API. You could try abstracting away this again, but that would be weird, and only lead to code that is more complex, because you would expect this class to be the only class in the system that needs a dependency on ImageResizer.

    Still though, what I find weird is the new AutoRotate().Install(Config.Current) call; especially doing this during every call! I know nothing about ImageResizer, so this might be needed, but I my first intuition would be to move this code to a static constructor, to allow it to be called just once for the app domain.