Since 2/3 of the queries varies (the where and select clauses), I think three queries is probably the right answer, unless you want to put a generic method that can be called to do the select part in any case. But since you're using PropertyInfo that isn't really possible. So the only thing I'd change is to not call GetProperties each time:
var props = typeof(Invoice).GetProperties();
var test = (from props
where prop.PropertyType == typeof (ItemInfo)
from z in (from item in typeof (ItemInfo).GetProperties()
select new Prop{ Name = prop.Name + item.Name,
PropType = item.PropertyType})
select z).ToList();
test.AddRange((from props
where prop.PropertyType == typeof(AmountInfo)
from z in (from amm in typeof(AmountInfo).GetProperties()
select new Prop{ Name = prop.Name + amm.Name,
PropType = amm.PropertyType })
select z).ToList());
test.AddRange((from props
where (prop.PropertyType != typeof(ItemInfo) &&
prop.PropertyType != typeof(AmountInfo))
select new Prop { Name = prop.Name,
PropType = prop.PropertyType }));
Actually, I probably wouldn't use LINQ here at all.
List<Prop> test = new List<Prop>();
var props = typeof(Invoice).GetProperties();
foreach(PropertyInfo prop in props){
if(prop.PropertyType == typeof(ItemInfo) || prop.PropertyType == typeof(AmountInfo))
test.AddRange(typeof(prop.PropertyType).Properties.ForEach(p => new Prop { Name = prop.Name + p.Name, PropType = p.PropertyType});
else
test.Add(new Prop { Name = prop.Name, PropType = prop.PropertyType });
}
The standard extension ForEach isn't defined on arrays, I think you can do it like
public static IEnumerable<U> ForEach<T, U>(this T[] values, Func<T,U> action){
foreach(T t in values) yield return action(t);
}