No se puede convertir este linq al árbol de expresiones dinámicas

c# expression expression-trees linq

Pregunta

Estoy intentando crear una expresión que usa FirstOrDefault con predicado en una expresión de IEnumerable<TComparable> pero me está dando este error: el parámetro 'o' no estaba vinculado en la expresión de consulta LINQ a Entidades especificada

Tengo una expresión linq como esta:

IEnumerable<string> names = new List<string>() { "abc", "def", "ghi" };
string name = names.FirstOrDefault(o => o.Contains("abc"));

public static Expression FirstOrDefault(this Expression expression, Type collectionValuesType, MethodInfo comparerMethod, string keyword)
{
    MethodInfo firstOrDefaultMethod = typeof(Enumerable).GetMethods()
        .FirstOrDefault(o => o.Name == "FirstOrDefault" && o.GetParameters().Length == 2)
        .MakeGenericMethod(new Type[] { collectionValuesType });


  Type firstOrDefaultDelegateType = typeof(Func<,>).MakeGenericType(collectionValuesType, typeof(bool));
    ParameterExpression firstOrDefaultPredicateParameter = Expression.Parameter(collectionValuesType);


//THIS LINE binds the "o" in (o => o.Contains("abc")) , and it is where I'm stuck with since yesterday!
    MemberExpression firstOrDefaultParameterO = Expression.Property(expression, typeof(string).GetProperty(???)); 

//o => o.ComparerMethod(keyword)
MethodCallExpression firstOrDefaultCompareExpression = Expression.Call(
    firstOrDefaultParameterO,
    comparerMethod,
    Expression.Constant(keyword, typeof(string))
);

//expression.FirstOrDefault(firstOrDefaultCompareExpression);
return Expression.Call(
    firstOrDefaultMethod,
    expression,
    Expression.Lambda(firstOrDefaultDelegateType, firstOrDefaultCompareExpression, Expression.Parameter(collectionValuesType))
);
}

Si tuviera un tipo complejo estaría usando así:

public class Example { public string Name; }

//o => o.Name.Contains("abc"))
        MemberExpression firstOrDefaultParameterO = Expression.Property(expression, typeof(Example).GetProperty("Name")); 

El problema es que no sé cómo vincular el tipo de cadena, ya que no tiene una propiedad que le dará el valor de la propiedad.

BTW: collectionValuesType = typeof (cadena)

He editado la pregunta como se sugiere para aclarar las cosas.

Respuesta aceptada

No es necesario construir Expression.Property para tipos simples como cadenas.

Por ejemplo. Si tengo que construir árbol de expresión para el método OrdenarPor para el tipo de Person con el Name la propiedad, voy a construir el árbol de expresión como esta:

ParameterExpression pe = System.Linq.Expressions.Expression.Parameter(typeof(T), "p");
Expression<Func<T, TPropertyType>> expr = System.Linq.Expressions.Expression.Lambda<Func<T, TPropertyType>>(System.Linq.Expressions.Expression.Property(pe, propertyName), pe);

Pero para el tipo de cadena simplemente lo haré: (ya que su expresión simplemente será x => x para los tipos de cadena)

If( typeof(T)==typeof(string))
{
    ParameterExpression pe = System.Linq.Expressions.Expression.Parameter(typeof(T), "p");
    Expression<Func<T, TPropertyType>> expr = System.Linq.Expressions.Expression.Lambda<Func<T, TPropertyType>>(pe,pe);
}

Probablemente pueda usar el mismo concepto para solucionar su problema.


Respuesta popular

OMG, se perdió un día entero porque pasé el parámetro incorrecto en Expression.Call al final.

return Expression.Call(
    firstOrDefaultMethod,
    expression,
    Expression.Lambda(firstOrDefaultDelegateType, firstOrDefaultCompareExpression, 

//THIS IS THE PROBLEM, it should be "firstOrDefaultPredicateParameter"
Expression.Parameter(collectionValuesType))
        );

@AnewGuyInTown, gracias por su ayuda, después de analizar su respuesta, realicé un "análisis completo" en mi código y encontré este error.



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