Search code examples
c#design-patternsdependency-injectionextension-methodsfactory-pattern

Require Factory class in Extension Method


I am working on a large legacy C# application and a task assigned to me is to remove all usages of a static factory class ServiceLocator.GetObject<T>() and replace with constructor injected dependencies throughout.

For the most part, this is simple, however there are around 50 cases in the application codebase where this is a bit tricky. For instance, Servicelocator is used in a static class, or extension method, or even a WPF MarkupExtension!.

For instance, if you were faced with a code snippet like this, what would you do? (apart from cry)

public static class MyExtensions
{
    private static ISingletonServiceOne _ServiceOne = null;
    private static ISingletonServiceTwo _ServiceTwo = null; // etc ... 

    public static SummaryHeader GetBannerSummary(this IModel rfq, object requester)
    {
        Guard.ArgumentNotNull(rfq, "rfq");
        Guard.ArgumentNotNull(requester, "requester");

        if (_ServiceOne == null)
        {
            _ServiceOne = ServiceLocator.GetService<ISingletonServiceOne>(requester);
            Guard.ArgumentNotNull(_ServiceOne, "_ServiceOne");
        }

        return _ServiceOne.GetBannerSummary(rfq);
    }

In the above the ServiceLocator.GetObject() method has been used in an Extension Method on IModel to locate a singleton registered service and execute a method on that using IModel.

The question is:

  • Are there any patterns/practices to avoid this sort of thing - a DI container needed in a static class, value-converter or extension method
  • Are there any patterns/practices to deal with cyclic dependencies in DI?
  • What would you do in the above given there is a trade off between good code and time to delivery?

I am thinking to move the GetBannerSummary() method out of extensions and only IModel in this case, however (don't laugh) there are also cases of the same ServiceLocator being used in ValueConverters (WPF) and MarkupExtensions :0

Your comments/suggestions appreciated


Solution

  • The only time I would ever use ServiceLocator is in static methods, e.g. extension methods, and IValueConverters as, unfortunately, there really isn't any other good way of getting dependencies.

    The only (slightly) better solution is to move the ServiceLocator call out to a lazy loaded property, so that the dependency can be injected during unit testing.

    In your case, however, that won't fly as you have a requester property being passed to the GetService, in which case you ideally need to add an IServiceOneFactory dependency to which you can pass your requester object. So:

    public interface IServiceOneFactory
    {
        ISingletonServiceOne Create(object requester);
    }
    
    public static class MyExtensions
    {
        public static IServiceOneFactory ServiceOneFactory
        {
            get 
            {
                if( _ServiceOneFactory==null)
                    _ServiceOneFactory = ServiceLocator.GetService<IServiceOneFactory>();
                return _ServiceOneFactory;
            }
            set { _ServiceOneFactory = value; }
        }
    
        private static IServiceOneFactory _ServiceOneFactory = null;
        private static ISingletonServiceOne _ServiceOne = null;
        private static ISingletonServiceTwo _ServiceTwo = null; // etc ... 
    
        public static SummaryHeader GetBannerSummary(this IModel rfq, object requester)
        {
            Guard.ArgumentNotNull(rfq, "rfq");
            Guard.ArgumentNotNull(requester, "requester");
    
            if (_ServiceOne == null)
            {
                _ServiceOne = ServiceOneFactory.Create(requester);
                Guard.ArgumentNotNull(_ServiceOne, "_ServiceOne");
            }
    
            return _ServiceOne.GetBannerSummary(rfq);
        }
    }