Using Lambda Expression to Select different fields

2020-06-17 05:25发布

问题:

I need to get two fields from a database table (retrieved using linq-to-sql), one field is a datetime (and is a fixed field) and the other is always a decimal, but the field can be different.

The table holds currency data which is processed twice a day and in different currencies so could have fields such as AM_USD, PM_USD, AM_EUR etc. And I need to get data such as a list of the date against PM_USD or the date against AM_EUR.

I would like to be able to call the data using a lambda expression for example (this is a stripped out example):

data = TableData.Select(x=>new {x.DateTimeAdded, x.[**field name as string**]});

I have been trying to write a function to do this, and am failing dismally.

The closest I have managed is:

private Func<TableData, KeyValuePair<DateTime, decimal>> CreateSelect(string FieldName)
{
    var parameterExp = Expression.Parameter(typeof(TableData), "sel");
    var dateParameter = Expression.Parameter(typeof(DateTime), "DateTimeAdded");
    var fieldParameter = Expression.Parameter(typeof(decimal), FieldName);
    ConstructorInfo constructorInfo = typeof(KeyValuePair<DateTime, decimal>).GetConstructor(new[] { typeof(DateTime), typeof(decimal) });
    NewExpression constructExpression = Expression.New(constructorInfo, new ParameterExpression[] { dateParameter, fieldParameter});

    var lambda = Expression.Lambda<Func<TableData, KeyValuePair<DateTime, decimal>>>( constructExpression, parameterExp);

    return lambda.Compile();
}

Which fails with "System.InvalidOperationException: Lambda Parameter not in scope".

I'm sure I missing something obvious, or going about it wrong way.

Any ideas?

Thanks T

回答1:

x.Foo is a member of x (property or field), not a parameter:

private Func<TableData, KeyValuePair<DateTime, decimal>> CreateSelect(string FieldName)
{
    var parameterExp = Expression.Parameter(typeof(TableData), "sel");
    var dateProp = Expression.PropertyOrField(parameterExp, "DateTimeAdded"); 
    var fieldProp = Expression.PropertyOrField(parameterExp, FieldName);
    ConstructorInfo constructorInfo = typeof(KeyValuePair<DateTime, decimal>).GetConstructor(new[] { typeof(DateTime), typeof(decimal) });
    NewExpression constructExpression = Expression.New(constructorInfo, new [] { dateProp, fieldProp});

    var lambda = Expression.Lambda<Func<TableData, KeyValuePair<DateTime, decimal>>>( constructExpression, parameterExp);

    return lambda.Compile();
}


回答2:

From your question:

data = TableData.Select(x=>new {x.DateTimeAdded, x.[**field name as string**]});

Specifying the field name in a LINQ query as a string can be done by using the LINQ Dynamic Query Library.

You can use the DynamicQuery library against any LINQ data provider (including LINQ to SQL, LINQ to Objects, LINQ to XML, LINQ to Entities, LINQ to SharePoint, LINQ to TerraServer, etc). Instead of using language operators or type-safe lambda extension methods to construct your LINQ queries, the dynamic query library provides you with string based extension methods that you can pass any string expression into.

And by the way, even thought the question isn't exactly identical, the answer is pretty much the same.