Sunday, April 19, 2020

Refactoring a Legacy .Net/C# - Replace Service Locator with Dependency Injection (Autofac) - Without breaking old code


I recently had the opportunity to add Dependency Injection into a 25yr old .Net Application. Now, I'm sure your saying to yourself, ".Net hasn't been around 25 yrs!"... well this application started life with C++/MFC (consisting of a few hundred CDialogs). Then, 15 years ago, it was halfway ported to C#/Winforms keeping most all Dialogs as CLI/MFC with some re-done in C#.

During this port, a home brew Inversion of Control (IoC) was created. This used the service locator (SL) pattern - basically, you ask it for an interface/type/service and it returns to you one that was registered to the system. Based off the framework's built-in IServiceProvider class. Now, this class doesn't do a whole lot, basically just provides a GetService(Type) method

There are many articles on pros and cons of DI vs SL supporting one or the other. But, I believe today's view is that SL is an anti-pattern (one example, Wikipedia page) because it hides you from knowing what dependencies are needed by the class, and hopefully they are registered with the system properly when calling the class. Although, pretty much all IoC's supporting injection also support manually resolving a reference - some times, you just have to do things not pretty 😀.

With the SL system in our app, I've found that Unit Testing is always extremely very tedious, and error prone. Partly because of the design of our system, also because you do not know what dependencies all the classes require - so mocking them is error prone. I can say passing all needed interfaces as part of a constructor certainly makes unit test setup easier. And during actual application runtime, have the IoC system inject all the services and manage lifetimes of shared resources is great.

So, my first goal was to find a modern, open source (having code is great when a tech becomes no longer popular/supported) IoC/DI platform for .Net. My second goal, was being able to have both the existing SL and the new DI system peacefully coexist. Needed to not break thousands of lines of code leading to runtime crashes trying to resolve dependencies.

After a bit of research, settled on Autofac. It had nice syntax, plenty of flexibility, and checked all the boxes.

Having old SL types resolve in new system (for both DI and SL)

Autofac supports numerous ways to register types into its registry. In this case, I already had a SL that could be use to find types, so the best way to get Autofac to see these types was to use RegisterSource builder method. Now, old resolver services can be Injected when Autofac creates services. Any types registered to Autofac are not found in older resolver (intentionally - moving away from it). And all existing code works without change. Take a look at the code below:

 

No comments:

Post a Comment

WinForms DataGridView with Appium/WinAppDriver (Python)

I recently spent some time trying to get the DataGridView of Winforms working (well enough) with Appium. The exposed properties (visible via...