Использование лямбда-выражения для выбора разных полей из имен полей

Мне нужно получить два поля из таблицы базы данных (полученной с помощью linq-to-sql), одно поле представляет собой дату и время (и является фиксированным полем), а другое всегда является десятичным, но поле может быть другим.

Таблица содержит данные о валюте, которые обрабатываются два раза в день и в разных валютах, поэтому могут иметь такие поля, как AM_USD, PM_USD, AM_EUR и т. д. И мне нужно получить такие данные, как список дат по отношению к PM_USD или дату по отношению к AM_EUR.

Я хотел бы иметь возможность вызывать данные, например, с помощью лямбда-выражения (это урезанный пример):

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

Я пытался написать функцию для этого, и я терплю неудачу.

Самое близкое, что мне удалось:

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();
}

Что не удается с «System.InvalidOperationException: параметр Lambda не входит в область действия».

Я уверен, что упускаю что-то очевидное или делаю это неправильно.

Есть идеи?

Спасибо Т


person Toby Bush    schedule 18.11.2010    source источник
comment
Справедливо ли предположить, что вы не можете изменить эту ужасную схему для данных?   -  person Lazarus    schedule 18.11.2010


Ответы (2)


x.Foo является членом x (свойства или поля), а не параметром:

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();
}
person Marc Gravell    schedule 18.11.2010
comment
Привет, Марк, это отличный ответ, и он возвращает правильное выражение, но теперь я буду звучать глупо... Как мне вызвать этот метод? var data = TableData.Select(CreateSelect(имя поля)); возвращается с System.ArgumentNullExpception и просит меня попробовать явно указать аргументы типа. Спасибо T - person Toby Bush; 18.11.2010
comment
@Marc - это не позволит мне скомпилировать код. Эта строка работает нормально: var tst = CreateSelect(AMUSD); Но если я попробую что-то вроде data.Select(CreateSelect(AMUSD)); это не позволит мне скомпилировать. - person Toby Bush; 18.11.2010
comment
@Marc полная ошибка: аргументы типа для метода «System.Linq.Enumerable.Select‹TSource,TResult›(System.Collections.Generic.IEnumerable‹TSource›, System.Func‹TSource,int,TResult›)» не могут быть вытекает из употребления. Попробуйте явно указать аргументы типа - person Toby Bush; 18.11.2010
comment
@ Тоби, можешь привести воспроизводимый пример? то есть что такое data ? Это отлично работает для меня: - person Marc Gravell; 18.11.2010
comment
IQueryable‹TableData› source = new[] { new TableData { DateTimeAdded = DateTime.Today, Foo = 123,45M}, new TableData { DateTimeAdded = DateTime.Now, Foo = 678,90M}, }.AsQueryable(); проекция var = source.Select (CreateSelect (Foo)); var Dictionary = Projection.ToDictionary(x => x.Key, x => x.Value); - person Marc Gravell; 18.11.2010
comment
@Marc - хммм, я только что протестировал ваш код с другим проектом, но с тем же уровнем доступа к данным linq-to-sql, и это работает точно так, как описано (и я делал подобное раньше с выражениями where), так что я думаю, я Я делаю что-то действительно неправильно, или этот проект, возможно, нуждается в небольшом обновлении. Большое спасибо за вашу помощь. Тоби - person Toby Bush; 18.11.2010
comment
@Marc... Ой, я ссылался на два класса с одинаковыми именами из разных пространств имен (я не понял, где в обоих пространствах имен второе пространство имен было добавлено только вчера), это приводило к сбою страницы, так как был проблема с конверсией! Ты живешь и учишься... (#MyBad) - person Toby Bush; 18.11.2010

Из вашего вопроса:

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

Указание имени поля в запросе LINQ в виде строки можно выполнить с помощью Библиотека динамических запросов LINQ.

Вы можете использовать библиотеку DynamicQuery для любого поставщика данных LINQ (включая LINQ to SQL, LINQ to Objects, LINQ to XML, LINQ to Entities, LINQ to SharePoint, LINQ to TerraServer и т. д.). Вместо использования языковых операторов или безопасных для типов методов расширения лямбда для создания запросов LINQ библиотека динамических запросов предоставляет вам методы расширения на основе строк, в которые вы можете передать любое строковое выражение.

И, кстати, даже если вопрос не не совсем идентичны, ответ примерно так же.

person Julian    schedule 18.11.2010
comment
ответ; здесь нет ответа. - person Marc Gravell; 18.11.2010
comment
@Marc Gravell: не уверен, что я просто пропустил грамматическую шутку или что-то в этом роде (английский не является моим родным языком), но я связался с конкретным ответом a и не вижу ничего плохого в ссылке на это как ответ (как в ответе, на который я ссылался)... - person Julian; 18.11.2010
comment
Я прочитал это как ответ на эту категорию вопросов, где (будучи определенным артиклем единственного числа) относится к единственно возможному, разумному, разумному ответу на эту категорию вопросов. Это не важно, но лингвистически, возможно, это было бы понятнее, чем the. - person Marc Gravell; 18.11.2010
comment
@Marc Gravell: ну, если бы я хотел дать единственно возможный, разумный, разумный ответ, это было бы 42, верно;) - person Julian; 18.11.2010