需要为最大日期值构建表达式树

时间:2011-08-10 17:46:48

标签: c# .net linq entity-framework expression-trees

我正在尝试为此linq查询构建表达式树:所以我可以传入一个通用实体:

this.EntityCollection.Select((ent) => ent.TimeStamp).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 个答案:

答案 0 :(得分:3)

(根据评论......)

问题在于您尝试使用LINQ to Objects(使用IEnumerable<T>和委托)和基于Queryable的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 :(得分:1)

就个人而言,我更喜欢overload of Expression.Property which takes a PropertyInfo instance

这样做,你可以这样做:

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的调用可能没有返回任何内容,这就是错误。请记住,作为参数传递的属性名称必须是公共的,否则,您需要使用overload of GetProperty which allows you to specify values from the BindingFlags enumeration来表示您希望包含非公共属性。

但是,我认为还有更好的选择。你应该定义一个这样的界面:

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>的重载。

您在这里获得的主要好处是您可以获得编译时检查呼叫有效的位置。这比更多比在运行时抛出异常更好。

此外,实施并不困难;您正在使用创建partial class files的实体框架;因此,为每个具有此类型的类型添加另一个部分类文件很容易:

public partial class MyEntity : IHaveTimeStamp
{ }

您的原始代码表明您已经在要使用此扩展方法的每个实体上已经拥有TimeStamp属性,因此,您无需执行任何操作来实现该接口,它已经为您隐式实现,因为TimeStamp属性应该是公开的。

如果公开,那么您可以轻松更改您的定义:

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

无论哪种方式,这都是一个简单的复制粘贴工作,每次都会对类名进行一些调整。

答案 2 :(得分:0)

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