Simplifying code with LINQ (Collections within collections)
Given an object graph that looks something like the following;
A --> B[] --> B1[]
--> B2[]
--> C[]
I need to check certain conditions on members/properties of B1 and B2 as well as ensuring that b2.Code appears somewhere within the C[]. If all conditions are satisfied, I then need to construct a new object using variables from elements of the C and B array. My current attempt is shown below, but I am wondering if this could be made more concise with LINQ?
A a = GetA();
List<MyTest> tsts = new List<MyTest>();
foreach (B b in a.B)
{
foreach (B1 b1 in b.B1)
{
if (b1.State == ("READY"))
{
foreach (B2 b2 in b.B2)
{
开发者_如何转开发 var tst = (from c in a.C
where c.Code == b2.Code && !c.IsRedundant
select new MyTest
{
Code = c.Code,
BVal = b.BVal,
C1Val = c.C1
}).FirstOrDefault();
if (tst != null)
tsts.Add(tst);
break;
}
}
}
}
Absolutely. Basically each new foreach
roughly equates to an extra from
clause:
var tests = from b in GetA().B
from b1 in b.B1
where b1.State == "READY"
from b2 in b.B2.Take(1)
from c in a.C
.Where(x => x.Code == b2.Code && !c.IsRedundant)
.Take(1)
select new MyTest
{
Code = c.Code,
BVal = b.BVal,
C1Val = c.C1
};
var testList = tests.ToList();
A few notes on this:
- It seems odd that you're never actually using
b1
, but you'll create an extra set of tests for eachb1
which is in the "ready" state. - The unconditional
break
within theforeach (B2 b2 ...)
loop basically means we only ever execute the loop body once - hence theTake(1)
- The way you only use the first result of the innermost query is why I've got the inner query (expressed through extension methods) with a
Take(1)
call to get at most one result
It's quite possible that some of these oddities can be removed - but it's not clear what you're really trying to achieve, so I've just tried to make the code reproduce your original query as faithfully as possible.
精彩评论