Этот должен сделать это. Я специально не тестировал его.
var nextProducts = from item1 in recommendations.Select((rec, idx) => new { Rec = rec, Index = idx })
join item2 in recommendations.Select((rec, idx) => new { Rec = rec, Index = idx })
on item1.Index equals item2.Index - 1
where item1.Rec.Products.Any(p => p.Code == "A")
&& item1.Rec.Products.Any(p => p.Code == "B")
select item2.Rec;
Если вам необходимо обе записи, то оператор выбора может быть
select new { MatchingItem = item1.Rec, NextItem = item2.Rec };
Но тогда вы должны сделать группировку, чтобы учесть для совпадающего элемента, являющегося последним пунктом в списке (там будет не будет следующим пунктом в этом случае).
var nextProducts = from item1 in recommendations.Select((rec, idx) => new { Rec = rec, Index = idx })
join item2 in recommendations.Select((rec, idx) => new { Rec = rec, Index = idx })
on item1.Index equals item2.Index - 1
into groupjoin
from i2 in groupjoin.DefaultIfEmpty()
where item1.Rec.Products.Any(p => p.Code == "A")
&& item1.Rec.Products.Any(p => p.Code == "B")
select new { MatchingItem = item1.Rec, NextItem = i2 == null ? null : i2.Rec };
Код я сделал испытание было что-то подобное со списком строк.
List<string> list = new List<string>() { "a", "b", "c", "a", "d", "a", "e" };
var query = from item1 in list.Select((s, idx) => new { Item = s, Index = idx })
join item2 in list.Select((s, idx) => new { Item = s, Index = idx })
on item1.Index equals item2.Index - 1
where item1.Item == "a"
select item2.Item;
Которые возвращают b, d и e.
+1 Для использования синтаксиса лямбда. –