The following code is intended to reduce redundant code by allowing you to generically invoke the same selector on a set of “getters” (data retrieval clients) and have all of the results combined. The final result set is awaitable, allowing the system to not block while waiting for all of the selectors to complete.
public async Task<T> GetBulkAsync<T>(Func<IGetter, Task<T>> selector) where T : IEnumerable<T>
{
Task<T>[] tasks = new Task<T>[this.getters.Count];
for (int i = 0; i < this.getters.Count; i++)
{
tasks[i] = selector(this.getters[i]);
}
T[] results = await Task.WhenAll(tasks);
return results.SelectMany<T, T>(r => r);
}
However, a problem comes up in my design here. There is one case where I simply want to use a selector that retrieves a single IData and then there’s times where a selector will retrieve an IEnumerable<IData> or maybe a List<IData> or something entirely different.
I have thought about using reflection to handle special cases with generic collections, but that’s messy and requires additional work for every type of collection I want to handle.
The main reason I care about IEnumerable<IData> (or really, IEnumerable<T>) is because I want to use Linq’s SelectMany in order to flatten out all of the results in the big result set. If each selector is coming back with an IEnumerable<T>, it needs to be reduced into one IEnumerable<T>, not an IEnumerable<IEnumerable<T>>.
Is there a good approach to solving this issue other than writing several different generic methods with constraints on collection types and without using reflection?
Instead of relying on one type parameter, use two and provide a transformation/projection function at the end, like so:
The
aggregateSelectorwould basically transform eachIEnumerable<T>into a newTResultinstance and you’d return anIEnumerable<TResult>.However, I recommend against this, as you’re relying on composition to occur in the function, when it really doesn’t have to.
Instead, I think you should return a sequence of sequences, like so:
This way, you can compose the results any way you want without having to change this method.