【问题标题】:Dynamic Lambda Select Index动态 Lambda 选择索引
【发布时间】:2014-01-16 19:02:11
【问题描述】:

我需要一些关于我要编写的 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 上而不创建扩展,因为它是一段足够小的代码。

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

干杯,

更新 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。

尽管它没有什么意义,它只是作为一个学习练习才真正有用。

谢谢。

【问题讨论】:

  • 为什么不直接接受谓词作为参数呢?如果您接受Expression&lt;Func&lt;T,bool&gt;&gt;,它不仅使该方法的实现变得非常简单,而且使所有内容都静态类型化;您不必担心在没有 Id 属性的东西上调用它,或者您可以使用它来根据 Id 以外的东西查找项目的索引。
  • @Servy 这听起来比我目前所做的更好。您介意在答案中充实一下吗?
  • 如果添加.ToList.AsEnumerable,则该查询中的所有数据都将加载到服务器端,并且您的 FindIndex 将对加载的数据起作用。它不会被翻译成 SQL
  • @SergeyLitvinov 是的。但我不知道有什么方法可以在不返回所有数据的情况下获取项目的索引,同时仍然使用 LINQ to Entities。

标签: c# linq reflection lambda expression-trees


【解决方案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;

【讨论】:

  • 感谢您的回复。不幸的是,这不起作用。当我使用 LINQ to Entities 时,我需要在 Select 之前包含 .AsEnumerable()。但是,这会给出错误“System.Collections.Generic.IEnumerable”不包含“Select”的定义和最佳扩展方法重载“System.Linq.Queryable.Select(System.Linq .IQueryable, System.Linq.Expressions.Expression>)' 有一些无效参数 –
  • 澄清一下,如果我不包括 AsEnumerable,那么在执行 .SingleOrDefault 时会出现以下错误:LINQ to Entities 无法识别方法 'System.Linq.IQueryable1[Entities.GeneralHelpers.LinqExtensions+GetRowCountClass] Select[Book,GetRowCountClass](System.Linq.IQueryable1[Entities .BLL.Models.Book], System.Linq.Expressions.Expression1[System.Func3[Entities.BLL.Models.Book,System.Int32,Entities.GeneralHelpers.LinqExtensions+GetRowCountClass]])' 方法,而这个方法不能翻译成商店表达式。
猜你喜欢
  • 2019-10-11
  • 2019-11-13
  • 1970-01-01
  • 1970-01-01
  • 2012-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多