动态Lambda选择索引

时间:2014-01-16 19:02:11

标签: c# linq reflection lambda expression-trees

我需要一些LINQ扩展的帮助,我很想写。我正在尝试创建一个扩展,计算IQueryable中给定Id的行索引 - 除了该类型可以是任何表。我想我已经完成了大部分工作,但我似乎无法完成它。我在行

上收到以下错误消息
Select(lambda)
  

方法的类型参数   “System.Linq.Enumerable.Select(System.Collections.Generic.IEnumerable,   System.Func)'无法从用法中推断出来。   尝试指定类型参数   明确。 c:\ users \ shawn_000 \ documents \ visual studio   2013 \ projects \ dexconstruktaweb \ dexconstruktaweb \ generalhelper.cs 157 17 DexConstruktaWeb

private class GetRowCountClass
{
    public GetRowCountClass(int id, int index)
    {
        this.Id = id;
        this.Index = index;
    }

    public int Id { get; set; }
    public int Index { get; set; }
}

public static int GetRowCount<T>(this IQueryable<T> query, int id)
{
    Type sourceType = typeof(T);            
    ParameterExpression[] parameter = new ParameterExpression[2];
    parameter[0]  = Expression.Parameter(sourceType, "x");
    parameter[1] = Expression.Parameter(typeof(int), "index");
    Type getRowCountType = typeof(GetRowCountClass);
    ConstructorInfo constructor = getRowCountType.GetConstructor(new[] { typeof(int), typeof(int)} );

    PropertyInfo pi = sourceType.GetProperty("Id");
    Expression expr = Expression.Property(parameter[0], pi);
    NewExpression member = LambdaExpression.New(constructor,new Expression[] { expr, parameter[1]});

    LambdaExpression lambda = Expression.Lambda(member, parameter);

    var item = query.AsEnumerable()
        .Select(lambda);
}

我知道在选择之后我需要以下行来获取索引返回,但是现在我很难过。任何帮助,将不胜感激。感谢。

.SingleOrDefault(x => x.Id == id).index;

更新

我已经做了一些进一步的挖掘,发现一些LINQ语句不适用于LINQ to Entities,这就是我正在使用的:

http://msdn.microsoft.com/en-us/library/bb738550.aspx

http://msdn.microsoft.com/en-us/library/bb896317.aspx

特别是“LINQ to Entities支持大多数投影和过滤方法的重载,但接受位置参数的除外。”

为了解决这个问题,我使用对AsEnumerable()的调用将其转换为通用的Enumerable,然后调用Select和SingleOrDefault,如上所述。但是,我发现在调用AsEnumerable和ToList之间创建的SQL没有区别,所以我决定简单地调用:

.ToList().FindIndex(e => e.Id == id) 

直接在我的IQueryable上创建一个Extension,因为它是一个足够小的代码。

感谢您的帮助。如果有人仍然看到更好的方法,请告诉我。

欢呼声,

更新2

作为一个学习练习,我接受了Servy的建议和这个答案Creating Dynamic Predicates- passing in property to a function as parameter,并提出了以下建议:

public static int GetRowIndex<T>(this IQueryable<T> query, Expression<Func<T, int>> property, int id)
{
    var lambda = Expression.Lambda<Predicate<T>>(
            Expression.Equal(property.Body, Expression.Constant(id)), property.Parameters);

    return query.ToList().FindIndex(lambda.Compile());
}

这可以称为:

var result2 = query.GetRowIndex(x => x.Id, id);

查询的类型为IQueryable。

虽然它没有什么意义,它只是作为一种学习练习才有用。

感谢。

1 个答案:

答案 0 :(得分:1)

您的lambda始终返回GetRowCountClass并使用T,因此您可以使用Expression.Lambda方法的通用版本:

var lambda = Expression.Lambda<Func<T, GetRowCountClass>>(member, parameter);

var item = query.Select(lambda);

return item.SingleOrDefault(x => x.Id == id).Index;