For the maximum date value, an expression tree must be built.

.net c# entity-framework expression-trees linq

Question

To enable me to pass in a generic Entity, I'm attempting to construct an expression tree for this Linq:

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

I want to develop a class that analyzes the maximum value of a generic Entity's TimeStamp attribute.

I attempted the following, but it complains:

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

On the last line of code, during compilation, I see the following error:

Overload resolution failed because no accessible 'Select' can be called with these arguments:

Why am I misusing this?

1
1
8/10/2011 6:56:39 PM

Accepted Answer

(According to comments)

You're attempting to utilize a combination of LINQ to Objects, which usesIEnumerable<T> Queryable-based LINQ (which utilizes delegates) andIQueryable<T> Expression trees, etc.). You cannot succeedEnumerable<T> a tree of expressions.

Three options:

  • Transform the assortment into anIQueryable<T> first:

    DateTime maxdate = this.EntityCollection.AsQueryable().Select(lambda).Max();
    
  • First, make the expression tree a delegate:

    DateTime maxdate = this.EntityCollection.Select(lambda.Compile()).Max();
    
  • Modify your approach to accommodate anIQueryable<T> as opposed to anIEnumerable<T>

3
8/10/2011 7:22:20 PM

Popular Answer

Personally, I like excess ofExpression.Property which requires aPropertyInfo instance better.

By doing that, you could:

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

Simply said, it's cleaner.

The call to Type.GetProperty could not be returning anything, which would explain the issue. To include non-public properties, you must use the excess ofGetProperty which permits the specification of values from theBindingFlags enumeration, thus keep in mind that the property name you supply as a parameter must be public.

But I believe there is a superior option. You ought to provide an interface like follows:

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

This enables you to specify your extension method in the following way:

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

Note: DateTime? is substituted forDateTime in the case that the sequence is empty. Additionally, if you want execution to take place on a server, you may design an overload that accepts a IQueryable<T>.

The key advantage is that the validity of the calls is checked at build time. This is much superior to a runtime exception being thrown.

Additionally, because you are using Entity Framework, which generates incomplete class files, it wouldn't be difficult to implement. It is simple to add an additional partial class file for each type that includes this:

public partial class MyEntity : IHaveTimeStamp
{ }

Your original code says you own theTimeStamp You don't need to do anything to implement the interface since it has already been implicitly implemented for you because any entity you wish to utilize this extension method on already has the property.TimeStamp property be open to the public.

If it is public, you can simply alter your definition to be this:

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

In any case, it just requires a few adjustments to the class name during each copy and paste operation.



Related Questions





Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow