Dynamic Lambda Select Index

c# expression-trees lambda linq reflection

Pregunta

Necesito ayuda con una extensión LINQ que estoy intentando escribir. Estoy intentando crear una extensión que calcula el índice de fila de un ID determinado dentro de un IQueryable, excepto que ese tipo puede ser cualquier tabla. Creo que he llegado casi todo el tiempo, pero parece que no puedo completarlo. Estoy recibiendo el siguiente mensaje de error en la línea

Select(lambda)

Los argumentos de tipo para el método 'System.Linq.Enumerable.Select (System.Collections.Generic.IEnumerable, System.Func)' no se pueden inferir del uso. Trate de especificar los argumentos de tipo explícitamente. 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);
}

Sé que después de la selección necesito la siguiente línea para obtener el índice para regresar, pero por ahora estoy perplejo. Cualquier ayuda sería apreciada. Gracias.

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

Actualizar

He investigado un poco más y descubrí que algunas declaraciones LINQ no funcionan para LINQ to Entities, que es lo que estoy usando:

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

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

En particular "La mayoría de las sobrecargas de los métodos de proyección y filtrado son compatibles con LINQ to Entities, con la excepción de aquellos que aceptan un argumento de posición".

Para solucionar esto, estaba usando una llamada a AsEnumerable () para convertir esto en un Enumerable genérico, luego la llamada a Select y SingleOrDefault como se describió anteriormente. Sin embargo, he encontrado que no hay diferencia en el SQL creado entre una llamada a AsEnumerable y ToList, por lo que he decidido simplemente llamar:

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

directamente en mi IQueryable sin crear una extensión, ya que es un pedazo de código lo suficientemente pequeño.

Gracias por toda tu ayuda. Si alguien todavía ve una mejor manera de hacerlo, hágamelo saber.

aclamaciones,

Actualización 2

Como un poco de ejercicio de aprendizaje, tomé la sugerencia de Servy y esta respuesta, Creación de predicados dinámicos, pasando la propiedad a una función como parámetro y se me ocurrió lo siguiente:

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

Esto se puede llamar como:

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

Donde la consulta es de tipo IQueryable.

Sin embargo, tiene muy poco sentido y solo es realmente útil como ejercicio de aprendizaje.

Gracias.

Respuesta popular

Su lambda siempre devuelve GetRowCountClass y toma T para que pueda usar la versión genérica del método Expression.Lambda :

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

var item = query.Select(lambda);

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


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow