如何编写用于在 SelectMany 中进行选择的表达式树?

How to write an expression tree for selecting inside of SelectMany?

考虑以下 Person class

    // Person.cs
    public class Person
    {
        public Guid Id { get; set; }
        public string Name { get; set; }
        public string FamilyName { get; set; }
        public float Age { get; set; }
        public DateTimeOffset BithDate { get; set; }
        public IEnumerable<Address> Addresses { get; set; }
        public Person()
        {
            Addresses = new List<Address>();
        }
    }

    // Address.cs
    public class Address
    {
        public string Country { get; set; }
        public string City { get; set; }
        public string MainStreet { get; set; }
        public string Info { get; set; }
        public string No { get; set; }
    }

现在,我有下面的代码片段

var list = PeopleDataGenerator.GetPeople()
    .SelectMany(x => x.Addresses)
    .Select(x => x.City)
    ;

如何使用表达式树完成这部分.SelectMany(x => x.Addresses) .Select(x => x.City)

不清楚你是否想要 IEnumerableIQueryable... IEnumerable:

鉴于:

/// <summary>
/// IEnumerable&lt;TResult&gt; Enumerable.SelectMany&lt;TSource, TResult&gt;(IEnumerable&lt;TSource&gt; source, Func&lt;TSource, IEnumerable&lt;TResult&gt;&gt; selector)
/// </summary>
public static readonly MethodInfo SelectMany1 = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                                    where x.Name == nameof(Enumerable.SelectMany)
                                                    let args = x.GetGenericArguments()
                                                    where args.Length == 2
                                                    let pars = x.GetParameters()
                                                    where pars.Length == 2 &&
                                                        pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
                                                        pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], typeof(IEnumerable<>).MakeGenericType(args[1]))
                                                    select x).Single();

/// <summary>
/// IEnumerable&lt;TResult&gt; Enumerable.Select&lt;TSource, TResult&gt;(IEnumerable&lt;TSource&gt; source, Func&lt;TSource, TResult&gt; selector)
/// </summary>
public static readonly MethodInfo Select1 = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                                where x.Name == nameof(Enumerable.Select)
                                                let args = x.GetGenericArguments()
                                                where args.Length == 2
                                                let pars = x.GetParameters()
                                                where pars.Length == 2 &&
                                                    pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
                                                    pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], args[1])
                                                select x).Single();

(我有一个 gist 充满了这些定义)

你可以

// SelectMany
var par1 = Expression.Parameter(typeof(Person));
var sub1 = Expression.Property(par1, nameof(Person.Addresses));
var lambda1 = Expression.Lambda<Func<Person, IEnumerable<Address>>>(sub1, par1);
var selectMany = Expression.Call(SelectMany1.MakeGenericMethod(typeof(Person), typeof(Address)), par0, lambda1);

// Select
var par2 = Expression.Parameter(typeof(Address));
var sub2 = Expression.Property(par2, nameof(Address.City));
var lambda2 = Expression.Lambda<Func<Address, string>>(sub2, par2);
var select = Expression.Call(Select1.MakeGenericMethod(typeof(Address), typeof(string)), selectMany, lambda2);

// persons => Select(SelectMany(persons))
var par0 = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(typeof(Person)));
var lambda0 = Expression.Lambda<Func<IEnumerable<Person>, IEnumerable<string>>>(select, par0);
var compiled = lambda0.Compile();

如果您想要 SelectMany+Select 调用,它位于 select 变量中。如果您想要一个使用 SelectMany+Select 的可编译表达式,它位于 lambda0compiled.