如何在运行时在IEnumerable <T>上创建动态多属性Select?

djd*_*d87 6 c# linq lambda duck-typing

昨天我问了一个非常相似的问题,但直到今天我才意识到我接受的答案并没有解决我所有的问题.我有以下代码:

public Expression<Func<TItem, object>> SelectExpression<TItem>(string fieldName)
{
    var param = Expression.Parameter(typeof(TItem), "item");
    var field = Expression.Property(param, fieldName);
    return Expression.Lambda<Func<TItem, object>>(field, 
        new ParameterExpression[] { param });
}
Run Code Online (Sandbox Code Playgroud)

使用如下:

string primaryKey = _map.GetPrimaryKeys(typeof(TOriginator)).Single();
var primaryKeyExpression = SelectExpression<TOriginator>(primaryKey);
var primaryKeyResults = query.Select(primaryKeyExpression).ToList();
Run Code Online (Sandbox Code Playgroud)

这允许我从一个中拔出主键IQueryable<TUnknown>.问题是这段代码只适用于一个主键,我需要添加对多个PK的支持.

那么,有什么方法可以调整SelectExpression上面的方法来获取IEnumerable<string>(这是我的主键属性名称列表)并让方法返回一个选择这些键的表达式?

即给出以下内容:

var knownRuntimePrimaryKeys = new string[] { "CustomerId", "OrderId" }`
Run Code Online (Sandbox Code Playgroud)

我的选择需要执行以下操作(在运行时):

var primaryKeys = query.Select(x=> new { x.CustomerId, x.OrderId });
Run Code Online (Sandbox Code Playgroud)

Dar*_*rov 2

您可以使用Tuple<>,因为匿名类型必须在编译时已知:

public Expression<Func<TItem, object>> SelectExpression<TItem>(params string[] fieldNames)
{
    var param = Expression.Parameter(typeof(TItem), "item");
    var fields = fieldNames.Select(x => Expression.Property(param, x)).ToArray();
    var types = fields.Select(x => x.Type).ToArray();
    var type = Type.GetType("System.Tuple`" + fields.Count() + ", mscorlib", true);
    var tuple = type.MakeGenericType(types);
    var ctor = tuple.GetConstructor(types);
    return Expression.Lambda<Func<TItem, object>>(
        Expression.New(ctor, fields), 
        param
    );
}
Run Code Online (Sandbox Code Playgroud)

进而:

var primaryKeyExpression = SelectExpression<TOriginator>("CustomerId", "OrderId");
Run Code Online (Sandbox Code Playgroud)

将生成以下表达式:

item => new Tuple<string, string>(item.CustomerId, item.OrderId)
Run Code Online (Sandbox Code Playgroud)