ASP.NET Core and the Strategy Pattern

Using ASP.NET Core Dependency Injection to wire up a strategy pattern implementation to harness SOLID design.

Published on 03 April 2018

Update 04/04/2018

After a review by Steve Gordan (thanks Steve!) he has pointed out that the ASP.NET Core IoC container can't handle IMathOperator[] as a depenency however it can handle IEnumerable<IMathOperator> and not require the additional factory pattern. This was a hangover from how unity did it due to it's limitations and how I had implemented the pattern previously. Sorry ASP.NET Core!

The implementation can now be simplified by registering the IMathOperator implementations as below.

services.AddScoped<IMathOperator, AddOperator>();

The MathStrategy gets an update to take an IEnumerable<IMathOperator> instead of an array in it's constructor.

public class MathStrategy : IMathStrategy
{
	private readonly IEnumerable<IMathOperator> _operators;

	public MathStrategy(IEnumerable<IMathOperator> operators)
	{
		_operators = operators;
	}

	public int Calculate(int a, int b, Operator op)
	{
		return _operators.FirstOrDefault(x => x.Operator == op)?.Calculate(a, b) ?? throw new ArgumentNullException(nameof(op));
	}
}

The ConfigureServices method is much cleaner now. This is what I was expecting in the first place so I am glad it is possible.

public void ConfigureServices(IServiceCollection services)
{
	** snip not related registrations **

	services.AddScoped<IMathStrategy, MathStrategy>();
	services.AddScoped<IMathOperator, AddOperator>();
	services.AddScoped<IMathOperator, SubtractOperator>();
	services.AddScoped<IMathOperator, MultipleOperator>();
	services.AddScoped<IMathOperator, DivideOperator>();
}

The Github repository has been updated with the revised implementation.

The original post below has been left for reference.


Using design patterns in software development is very important. They help with code readability, maintainability and allow for applying SOLID principles to your code.

I have used the strategy pattern a number of times over the years for various tasks and the main part that I enjoy, once setup correctly, is that the main class does not need to be changed when adding more functionality into the processing.

To implement the strategy pattern cleanly you need to harness the power of using Dependency Injection and not rely on the concrete implementations.

After reading Strategy Pattern Implementations there are many ways of implementing the pattern but none of the methods James discuses harnesses the power of DI.

What is the strategy pattern?

The basic premise is the calling code has a single point of entry determined by user input or system specification. Based on the selection specified, in my example this will be an enum value, it will locate a single processor which corresponds to that processor which will execute.

How do I implement this?

Working with the basic arithmatic operators, which James uses, as an example you will need the following:

  1. IMathOperator interface which defines the operator type and the calculation each implementation will execute.
  2. IMathStrategy interface which the calling code will consume. This way the consuming code does not care about the implementation of the concrete classes and has a level of abstraction to allow for single responsibility.
  3. Operator enum to define the action.
public enum Operator
{
	Add,
	Substract
}

public interface IMathOperator
{
	Operator Operator { get; }

	int Calculate(int a, int b);
}

public interface IMathStrategy
{
	int Calculate(int a, int b, Operator op);
}

The implementation of the strategy requires to have the implementations of IMathOperator injected into it's constructor through DI.

public class MathStrategy : IMathStrategy
{
	private readonly IMathOperator[] _operators;

	public MathStrategy(IMathOperator[] operators)
	{
		_operators = operators;
	}

	public int Calculate(int a, int b, Operator op)
	{
		return _operators.FirstOrDefault(x => x.Operator == op)?.Calculate(a, b) ?? throw new ArgumentNullException(nameof(op));
	}
}

Once they have been injected in when the Calculate method is called the first processor which corresponds to the operator specified is located and executed.

With IoC containers, such as Unity, you can register all the concrete implementations of IMathOperator as named registrations and then when an array of IMathOperator are requested it injects all of the implementations. However this is not possible in the basic IoC in ASP.NET Core.

ASP.NET Core Restrictions

The IoC container which ships with ASP.NET Core is relatively basic, it allows for registration of implementatons with dependency trees however does not allow for the registration of the same interface with different implementations as Unity would and use them with a requirement of IMathOperator[].

Implementing in ASP.NET Core

It is possible to implement the above in ASP.NET Core with some juggling but you will require the use of a factory pattern at the point the dependencies are registered. As the strategy requires all of the IMathOperator implementations the factory pattern has to get them all together ready for consumption.

public interface IMathStrategyFactory
{
	IMathOperator[] Create();
}

The implementation of this factory relies on another function of the IoC container which is that you do not have to map interface to concrete implementation when you register the type with the IServiceCollection. This factory implementation relies on the concrete implementations.

public class MathStrategyFactory : IMathStrategyFactory
{
	private readonly AddOperator _addOperator;
	private readonly SubtractOperator _subtractOperator;

	public MathStrategyFactory(
		AddOperator addOperator,
		SubtractOperator subtractOperator)
	{
		_addOperator = addOperator;
		_subtractOperator = subtractOperator;
	}

	public IMathOperator[] Create() => new IMathOperator[] { _addOperator, _subtractOperator };
}

Once the concrete implementations have been registered eg.

services.AddScoped<AddOperator>();

The factory has to be registered and then the use of it to allow for resolving the IMathOperator[] for the strategy to be successful.

services.AddScoped<IMathStrategyFactory, MathStrategyFactory>();
services.AddScoped<IMathOperator[]>(provider =>
{
	var factory = (IMathStrategyFactory)provider.GetService(typeof(IMathStrategyFactory));
	return factory.Create();
});

How to consume the strategy?

Once all the registrations are in place the strategy needs to be consumed.

public class HomeController : ControllerBase
{
	private readonly IMathStrategy _mathStrategy;

	public HomeController(IMathStrategy mathStrategy)
	{
		_mathStrategy = mathStrategy;
	}

	public IActionResult Index()
	{
		int a = 10;
		int b = 5;
		int result = _mathStrategy.Calculate(a, b, Operator.Add);
		return Content(result.ToString());
	}
}

As you can see from the above, the HomeController does not care about how the strategy pattern is implemented nor how the calculations are found and executed, it only cares about calling it with the values and specifying the action to run on them. This allows for easy unit testing as you can test each implementation separately and it means the HomeController has only one interface to mock.

Open for extension

I mentioned at the beginning this method allows for applying the SOLID principles. The Single Responsibility Principle is applied to all concrete implementations as they have a single function to handle, the strategy is dealing with finding the appropriate implementation and calling it and the Controller calls the method and returns the result not caring about the processing.

But what about the Open/Closed principle? Well if we were to include a new operator, multiplication for example, we whould have to extend the enum, create a new concrete implementation and register it and that would be it.

Unfortunately as we are not using an IoC container such as Unity then we need to modify the factory which is a slight violation but I think one we can live with for the power of the functionality added.

Conclusion

We've looked at implementing the Strategy pattern harnessing the power of DI in ASP.NET Core to keep the consuming processing code SOLID.

A full working example can be found on Github.

Any questions/comments then please contact me on Twitter @WestDiscGolf