design – How can I allow sub-services to use concrete types, without having an ever-growing type argument list on the primary service?

NOTE: I believe I have a bit of an X/Y problem here, so please, bare with me. Also, I have trouble explaining things sometimes, so feel free to ask me to elaborate, and I will improve anything I can about my question to make it clearer.


I have a system in which I have a central service that calls a processor to perform some work such as fetching and processing data.

The central service, in my design, should have no knowledge of the concrete types that the processor is working with, and instead should work with interfaces to ensure that the processor is at a minimum, working with data types that meet the requirements for whatever call it’s executing.


Example Use Case

Let’s say I need to fetch entity locations and process them, excluding the specific implementation details, I know that I need to define the following:

  • An interface describing the data model.
  • An interface defining methods to fetch and process the aforementioned data model.

So the code would look something like this:

public interface IEntityLocation {
    object Id { get; set; }
    double? Latitude { get; set; }
    double? Longitude { get; set; }
}
public interface IEntityLocationService {
    bool TryFetchEntityLocations(out IEnumerable<IEntityLocation> locations);
    void ProcessEntityLocations(IEnumerable<IEntityLocation> locations);
}

Explaining the Problem

While the sample code in the use case above would work just fine for implementers, they lose the ability to work with their concrete types. I’d much rather work with generics while enforcing constraints, thus giving the implementer, the ability to work with their concrete type instead:

public interface IEntityLocationService<T> where T : IEntityLocation {
    bool TryFetchEntityLocations(out IEnumerable<T> locations);
    void ProcessEntityLocations(IEnumerable<T> locations);
}

This would be perfect, except that I have a service provider definition that looks like this:

public interface ILocationServiceProvider {
    IEntityLocationService<T1> EntityLocationService { get; }
    ISomethingElseLocationService<T2> SomethingElseLocationService { get; }
    void RecordSomethingAboutLocations(...);
}

So the processor implements ILocationServiceProvider to define how it’s location services should function. Essentially, IEntityLocationService above is a sub-service of the location service which is why I want to define it in this manner. However, having a type argument for all of these sub-services, is NOT scalable due to the fact that implementers end up with something like this:

internal sealed class LocationServiceProvider : ILocationServiceProvider<EntityLocation, SomethingElse, ..., InfinitelyMany>

Which probably means there’s something wrong with my design, at some level.


How can I allow the implementers of the sub-services described above, use their concrete types, without having an ever-growing type argument list on my quote-unquote service provider?