Introduction
ASP.NET Core dependency injection has support for mapping an interface to an implementation of the interface, configuring lifetime rules, and managing configuration settings. It works well for most application bootstrapping needs but comes with an implicit assumption - that there is always a one-to-one mapping from the interface to implementation.
Workflow and other operations often call for different business rules which are expressed through various implementations of the same interface. For example, you may have an IWorkflowAction interface that has implementations like,
- SendEmailAction : IWorkflowAction
- RequestCreditReportAction : IWorkflowAction
- AssignTaskAction: IWorkflowAction
The implementation of IWorkflowAction needed to process a workflow request is not known when bootstrapping the application. It needs to be resolved at the time the workflow request is received. This article shows you how to configure and manage dependency injection resolution on demand using an ASP.NET Core Web API application.
All code samples are available for download in the samples attached to this article.
Interfaces and Implementations
The dependency injection solution will resolve a single IMathOperationRepository interface to four different implementations to handle addition, subtraction, multiplication, and division. You can extrapolate from this and apply its own business requirements.
- public interface IMathOperationRepository
- {
- OperationResult PerformOperation(OperationRequest opRequest);
- }
- public class AddOperationRepository : IMathOperationRepository
- {
- public OperationResult PerformOperation(OperationRequest opRequest)
- {
- OperationResult opResult = new OperationResult();
- opResult.Value = opRequest.X + opRequest.Y;
- return opResult;
- }
- }
The interface accepts a simple OperationRequest that has an X and Y value and applies the appropriate math operation per interface implementation. The AddOperationRepository adds the two values, the SubtractOperationRepository, subtracts Y from X, etc.
- public enum MathOperationType
- {
- Add = 0,
- Subtract = 1,
- Multiply = 2,
- Divide = 3
- }
- public class OperationRequest
- {
- . . .
- [JsonConverter(typeof(StringEnumConverter))]
- [JsonProperty(PropertyName = "operation")]
- public MathOperationType OperationType { get; set; }
- }
Bootstrapping
Typical ASP.NET Core dependency injection bootstrapping looks something like this,
- services.AddTransient<IMathOperationRepository, AddOperationRepository>();
This would work if the IMathOperationRepository always resolved to the AddOperationRepository implementation; however, it needs to be dynamically resolved based on the type of operation requested at the time of the request, not upon startup. Instead, a delegate is added which accepts a key value that resolves to the appropriate interface implementation. The function uses a simple facade pattern to map the key value to the class.
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddTransient<AddOperationRepository>();
- services.AddTransient<SubtractOperationRepository>();
- services.AddTransient<MultiplyOperationRepository>();
- services.AddTransient<DivideOperationRepository>();
- services.AddTransient<Func<MathOperationType, IMathOperationRepository>>(serviceProvider => key =>
- {
- switch (key)
- {
- case MathOperationType.Add:
- return serviceProvider.GetService<AddOperationRepository>();
- case MathOperationType.Subtract:
- return serviceProvider.GetService<SubtractOperationRepository>();
- case MathOperationType.Multiply:
- return serviceProvider.GetService<MultiplyOperationRepository>();
- case MathOperationType.Divide:
- return serviceProvider.GetService<DivideOperationRepository>();
- default:
- throw new KeyNotFoundException();
- }
- });
- . . .
- }
- [Route("api/[controller]")]
- [ApiController]
- public class ValuesController : ControllerBase
- {
- private Func<MathOperationType, IMathOperationRepository> _mathRepositoryDelegate;
- public ValuesController(Func<MathOperationType, IMathOperationRepository> mathRepositoryDelegate)
- {
- _mathRepositoryDelegate = mathRepositoryDelegate;
- }
- [HttpPost]
- public ActionResult<OperationResult> Post([FromBody] OperationRequest opRequest)
- {
- IMathOperationRepository mathRepository = _mathRepositoryDelegate(opRequest.OperationType);
- OperationResult opResult = mathRepository.PerformOperation(opRequest);
- return new ObjectResult(opResult);
- }
- }
Testing

Hamid KhanPosted Jul 13, 2022, 8:31 AM
Nice article for real need........ Thanks for sharing
Tecfield TecfieldPosted May 18, 2019, 9:43 PM
This defeats the whole purpose of DI. This is a totally different pattern called factory. You could use GetServices signature instead of GetService in order to keep it closer to DI.
Aniket RaiPosted May 6, 2019, 2:24 AM
Well explained !
Faisal PathanPosted Feb 12, 2019, 10:23 PM
Excellent content and useful, Thanks for sharing.