Необходимо построить дерево выражений для максимального значения даты - PullRequest
1 голос
/ 10 августа 2011

Я пытаюсь построить дерево выражений для этого запроса linq: чтобы я мог передать универсальный объект:

this.EntityCollection.Select((ent) => ent.TimeStamp).Max()

Я хочу создать класс, который принимает универсальный объект и находитmax его свойства TimeStamp.

Я пытался что-то вроде ниже, но он жалуется:

ParameterExpression param = Expression.Parameter(typeof(TE), "ent");

MemberExpression prop = Expression.
    Property(param, typeof(TE).GetProperty("TimeStamp").GetGetMethod());

Expression<Func<TE, DateTime>> lambda = Expression.Lambda<Func<TE, DateTime>>(
    prop, new ParameterExpression[] { param });

DateTime maxdate = this.EntityCollection.Select(lambda).Max();

Когда я компилирую, я получаю следующую ошибку в последней строке кода:

Не удалось разрешить перегрузку, потому что нельзя вызвать доступный 'Select' с этими аргументами:

Что я делаю не так?

Ответы [ 3 ]

3 голосов
/ 10 августа 2011

(согласно комментариям ...)

Проблема в том, что вы пытаетесь использовать смесь LINQ to Objects (которая использует IEnumerable<T> и делегаты) и LINQ на основе запросов (которая использует IQueryable<T> и деревья выражений). Вы не можете передать Enumerable<T> дерево выражений.

Три варианта:

  • Преобразование коллекции в IQueryable<T> сначала:

    DateTime maxdate = this.EntityCollection.AsQueryable().Select(lambda).Max();
    
  • Сначала преобразуйте дерево выражений в делегат:

    DateTime maxdate = this.EntityCollection.Select(lambda.Compile()).Max();
    
  • Измените ваш метод так, чтобы он принимал IQueryable<T> вместо IEnumerable<T>

1 голос
/ 10 августа 2011

Лично я предпочитаю перегрузку Expression.Property, которая принимает PropertyInfo экземпляр .

. Для этого вы можете сделать следующее:

ParameterExpression param = Expression.Parameter(typeof(TE), "ent");
MemberExpression prop = Expression.
    Property(param, typeof(TE).GetProperty("TimeStamp"));
Expression<Func<TE, DateTime>> lambda = Expression.Lambda<Func<TE, DateTime>>(
    prop, new ParameterExpression[] { param });
DateTime maxdate = this.EntityCollection.Select(lambda).Max();

Это намного чище.

Возможно, что вызов Type.GetProperty ничего не возвращает, и это дает вам ошибку.Помните, что имя свойства, передаваемое в качестве параметра, должно быть общедоступным, в противном случае вам нужно использовать перегрузку GetProperty, которая позволяет указывать значения из перечисления BindingFlags , чтобы указать, что вы хотите не-общественные свойства должны быть включены.

Однако я думаю, что есть лучшая альтернатива.Вы должны определить интерфейс следующим образом:

public interface IHaveTimestamp
{
    DateTime TimeStamp { get; set; }
}

Это позволит вам затем определить метод расширения следующим образом:

public static DateTime? MaxTimeStamp(IEnumerable<T> entities) 
    where T : IHaveTimeStamp
{
    // Return the max.
    return entities.Select(e => (DateTime?) e.TimeStamp).Max();
}

Примечание: DateTime? используется вместо DateTimeв случае, если у вас есть пустая последовательность.Кроме того, вы можете создать перегрузку, которая принимает IQueryable<T>, если вы хотите, чтобы выполнение происходило на сервере.

Основное преимущество, которое вы получаете здесь, заключается в том, что вы получаете проверку во время компиляциигде звонки действительны.Это намного лучше, чем генерировать исключение во время выполнения.

Кроме того, это не составит труда реализовать;вы используете Entity Framework, которая создает частичные файлы классов ;из-за этого легко добавить еще один частичный файл класса для каждого типа, который имеет это:

public partial class MyEntity : IHaveTimeStamp
{ }

Ваш исходный код указывает, что у вас уже есть свойство TimeStamp для каждого из объектов, которые вы хотитеиспользуйте этот метод расширения, потому что вам не нужно ничего делать для реализации интерфейса, он уже неявно реализован для вас, потому что свойство TimeStamp должно быть открытым.

Если это не общедоступно, то вы можете легко изменить свое определение следующим образом:

public partial class MyEntity : IHaveTimeStamp
{ 
    IHaveTimeStamp.TimeStamp
    { 
        get { return this.TimeStamp; } 
        set { this.TimeStamp = value; } 
    }
}

В любом случае, это простое задание копирования и вставки с некоторыми изменениями классаимя каждый раз.

0 голосов
/ 10 августа 2011
ParameterExpression param = Expression.Parameter(typeof(TE), "ent");
MemberExpression prop = Expression.Property(param, 
    typeof(TE).GetProperty("TimeStamp"));
Expression<Func<TE, DateTime>> lambda = Expression.Lambda<Func<TE, DateTime>>(
    prop, new ParameterExpression[] { param });
DateTime maxdate = this.EntityCollection.Select(lambda).Max();

Вам не нужно звонить GetGetMethod на Property.

...