How to handle dynamic reference data in .NetCore API

How to handle dynamic reference data in .NetCore API

Sunny Sun Lv4

How to handle dynamic reference data in .NetCore API

Reference data can provide consistent and standardized information throughout a system. A straightforward approach to handling reference data is to use a switch case within a reference data service. However, as the number of reference data types increases, this approach can become increasingly cumbersome and difficult to maintain.

In my previous post , I walk through how to handle reference data in NestJS. In this article, we’ll explore how to implement dynamic reference data endpoints in ASP.NET Core APIs using C#. We’ll move away from a monolithic switch-case approach and embrace a generic pattern that adapts to various reference data types.

Using Switch Case

We can use the switch case to handle a small and relatively static set of reference data types. Below is an example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ReferenceDataService
{
public async Task<IEnumerable<RefData>> GetRefDataByTypeAsync(string type)
{
switch (type)
{
case "countries":
return await _countryRepository.GetAllAsync();
case "productCategories":
return await _productCategoryRepository.GetAllAsync();
// ... other cases
default:
throw new ArgumentException("Invalid reference data type");
}
}
}

However, using a switch case isn’t an ideal solution. The Challenges of the Switch-Case Approach are**:**

  • Code Rigidity: Adding new reference data types requires modifying the switch statement, making maintenance difficult.

  • Limited Reusability: Code becomes specific to the handled data types, hindering reusability.

Generic Strategy Pattern with keyedServiceProvider

A better approach is using the generic strategy pattern for retrieving reference data. First, let’s define our reference data service.

Interface and Reference data service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface IReferenceDataService
{
Task<IEnumerable<RefData>> GetRefDataByType(string refDataType);
}


public class ReferenceDataService : IReferenceDataService
{
private readonly IServiceProvider _keyedServiceProvider;
public ReferenceDataService(IServiceProvider keyedServiceProvider)
{
this._keyedServiceProvider = keyedServiceProvider;
}

public async Task<IEnumerable<RefData>> GetRefDataByType(string refDataType)
{
var service = _keyedServiceProvider.GetRequiredKeyedService<IRefdataStrategy>(refDataType);
return await service.GetRefData();
}
}

Here, we define an interface with a methodGetRefDataByType, which takes a string representing the reference data type and returns an IEnumerable asynchronously. Then theReferenceDataService class implement the interface. It acts as a facade, delegating the actual reference data retrieval to different strategies based on the type.

Please note that we use the keyedServiceProvider in the above example.

The keyedServiceProvider feature in .NET 8 provides a more flexible way to register and resolve services based on a key. It allows us to register multiple implementations of the same service interface with different keys and select the appropriate implementation based on the key at runtime.

In the example, we register two different implementations of the IRefdataStrategy. Then, in the ReferenceDataService class, we use _keyedServiceProvider.GetRequiredKeyedService(refDataType) to search the registered services for an implementation of IRefdataStrategy with the specified key.

1
2
builder.Services.AddKeyedScoped<IRefdataStrategy, IndustryTypeStrategy>("industry");
builder.Services.AddKeyedScoped<IRefdataStrategy, CountryTypeStrategy>("country");

Strategy Pattern

The strategy pattern is a behavioral design pattern that allows algorithms to be interchangeable. In the context of the getRefData method, it is used to implement different strategies for getting reference data based on the type.

In the example below, we define the IRefdataStrategy interface with a method GetRefData that returns an enumerable collection of RefData objects.

1
2
3
4
public interface IRefdataStrategy
{
Task<IEnumerable<RefData>> GetRefData();
}

Then, the CountryTypeStrategy class provides a specific implementation for retrieving country-related reference data. It depends on an IRepositoryBase to fetch the actual data.

1
2
3
4
5
6
7
8
9
10
11
12
13
public class CountryTypeStrategy : IRefdataStrategy
{
private readonly IRepositoryBase<CountryDataDto> repo;
public CountryTypeStrategy(IRepositoryBase<CountryDataDto> repo)
{
this.repo = repo;
}

public async Task<IEnumerable<RefData>> GetRefData()
{
return await this.repo.GetAll();
}
}

Dependency injection

We use dependency injection and the keyedServiceProvider, to select the appropriate strategy based on the reference data type.

1
2
3
4
5
6
builder.Services.AddScoped<IRepositoryBase<CountryDataDto>, CountryRepository>();
builder.Services.AddScoped<IRepositoryBase<IndustryDataDto>, IndustryRepository>();
builder.Services.AddScoped<IReferenceDataService, ReferenceDataService>();
builder.Services.AddKeyedScoped<IRefdataStrategy, IndustryTypeStrategy>("industry");
builder.Services.AddKeyedScoped<IRefdataStrategy, CountryTypeStrategy>("country");

API Endpoint to consume the ReferenceDataService

In our API controller, we can inject the ReferenceDataService.

1
2
3
4
5
6
7
8
9
10
11
12
private readonly IReferenceDataService refDataService;

public RefDataController(IReferenceDataService refDataService)
{
this.refDataService = refDataService;
}

[HttpGet(Name = "GetRefData")]
public async Task<IEnumerable<RefData>> GetRefData(string refDataType)
{
return await this.refDataService.GetRefDataByType(refDataType);
}

Summary

In this post, we leveraged the power of interfaces, dependency injection, KeyedServiceProdier, and the strategy pattern to create a flexible solution. Key takeaways include:

  • Utilizing interfaces like IRefdataStrategy decoupling data retrieval logic from specific data sources.

  • Keyed services in .NET 8 enable dynamic selection of appropriate strategies based on the reference data type.

I hope you find this post useful. You can find the source code example here .

  • Title: How to handle dynamic reference data in .NetCore API
  • Author: Sunny Sun
  • Created at : 2024-11-04 00:00:00
  • Updated at : 2024-11-30 13:33:32
  • Link: http://coffeethinkcode.com/2024/11/04/Dotnet-dynamic-reference-data-in-api/
  • License: This work is licensed under CC BY-NC-SA 4.0.