I've been trying for a long time to find a "clean" pattern to handle a .SelectMany
with anonymous types when you don't always want to return a result. My most common use case looks like this:
- We have a list of customers that I want to do reporting on.
- Each customer's data resides in a separate database, so I do a parallel
.SelectMany
- In each lambda expression, I gather results for the customer toward the final report.
- If a particular customer should be skipped, I need to return a empty list.
- I whip these up often for quick reporting, so I'd prefer an anonymous type.
For example, the logic may looks something like this:
//c is a customer
var context = GetContextForCustomer(c);
// look up some data, myData using the context connection
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
return null;
This could be implemented as a foreach statement:
var results = new List<WhatType?>();
foreach (var c in customers) {
var context = GetContextForCustomer(c);
if (someCondition)
results.AddRange(myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 }));
}
Or it could be implemented with a .SelectMany
that is pre-filtered with a .Where
:
customers
.Where(c => someCondition)
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
})
.ToList();
There are problems with both of these approaches. The foreach
solution requires initializing a List
to store the results, and you have to define the type. The .SelectMany
with .Where
is often impractical because the logic for someCondition
is fairly complex and depends on some data lookups. So my ideal solution would look something like this:
customers
.AsParallel()
.SelectMany(c => {
var context = GetContextForCustomer(c);
if (someCondition)
return myData.Select(x => new { CustomerID = c, X1 = x.x1, X2 = x.x2 });
else
continue? return null? return empty list?
})
.ToList();
What do I put in the else
line to skip a return value? None of the solutions I can come up with work or are ideal:
continue
doesn't compile because it's not an activeforeach
loopreturn null
causes anNRE
return
empty list requires me to initialize a list of anonymous type again.
Is there a way to accomplish the above that is clean, simple, and neat, and satisfies all my (picky) requirements?