Cómo llamar al método de extensión .Any con Entity Framework usando árboles de expresión

c# entity-framework expression-trees

Pregunta

He visto algunos ejemplos aquí Llamando a un método desde una expresión y en MSDN, pero no he podido obtener el método correcto de llamada / tipo de objeto para Cualquiera () para la consulta a continuación. Parece que puedo recibir la llamada de propiedad pero no IEnumerable parte de la propiedad secundaria.
billing_map_set_lu es el padre de billmaps_lu y se define como una asociación en el Entity Framework.

La razón por la que estoy usando árboles de expresión es que necesito poder definir la consulta en tiempo de ejecución con 1-n .SelectMany (p => p.billmaps_lu) .Where (predicate) cláusulas. Así que pensé que si podía construir los árboles de expresiones podría manejar todas las diferentes combinaciones que tengo para este sistema, que son muchas.

var myResults = ctx.billing_map_set_lu
                   .Where(p => p.billmaps_lu.Any(b => b.billmap_columnname == "templatesittings_key" &&  b.billmap_columnvalue == 428264))
                                   SelectMany(p => p.billmaps_lu)
                   .Where (b =>b.billmap_columnname =="locations_key" && b.billmap_columnvalue == 12445)
                                   Select(z => z.billing_map_set_lu);

He intentado unos cuantos intentos usando las muestras de arriba ...

ParameterExpression bms = Expression.Parameter(typeof(billmaps_lu));
Expression left1 = Expression.Property(bms, typeof(billmaps_lu).GetProperty("billmap_columnname"));
Expression right1 = Expression.Constant("templatesittings_key", typeof(string));
Expression InsideAny1 = Expression.Equal(left1, right1);
Expression left2 = Expression.Property(bms, typeof(billmaps_lu).GetProperty("billmap_columnvalue"));
Expression right2 = Expression.Constant(428264, typeof(int));
Expression InsideAny2 = Expression.Equal(left2, right2);
Expression myWhereClause1 = Expression.AndAlso(InsideAny1, InsideAny2);

La parte anterior parece estar bien, pero cuando trato de hacer el .Any es como si no pudiera obtener la propiedad / el método correcto para sacar los objetos correctos. (Siento que estoy en un problema de física en el que estoy trabajando con las unidades incorrectas). Espero que me falte algo simple, soy bastante nuevo en Expression Trees ... He incluido un código que no funciona para intentar para mostrarte dónde está mi cabeza y cómo alguien puede guiarme en la dirección correcta.

MethodInfo method = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(billing_map_set_lu).GetProperty("billmaps_lu").PropertyType);
ParameterExpression billMapSetParameter = Expression.Parameter(typeof(billing_map_set_lu), "p");
ParameterExpression billMaps = Expression.Parameter(typeof(billmaps_lu), "p1");
var myFunction = Expression.Lambda<Func<billmaps_lu, bool>>(Expression.Call(method, Expression.Property(billMapSetParameter, typeof(billing_map_set_lu).GetProperty("billmaps_lu")), myWhereClause1), billMaps)

Respuesta aceptada

Descargo de responsabilidad, no tengo ningún código de trabajo compilado.

2 problemas.

El primer problema probablemente radica en:

ParameterExpression billMapSetParameter = Expression.Parameter(typeof(billing_map_set_lu), "p");

Eso no es un parámetro que necesitas en:

Expression.Lambda<Func<billmaps_lu, bool>>(Expression.Call(method, Expression.Property(**billMapSetParameter**, typeof(billing_map_set_lu).GetProperty("billmaps_lu")), myWhereClause1), billMaps)

Cambie billMapSetParameter a billMaps ParamterExpression, entonces debería estar listo. Está llamando a PropertyExpression para obtener su billMapSet para usted desde ParameterExpression.

2do problema: (No estoy seguro, pero mi instinto) Es posible que deba pasar la cláusula Where como una expresión constante con el tipo de expresión <.Func <>>. . Cualquier método toma dos parámetros, de los cuales, el segundo es una Expresión <.Func <>> (¿O simplemente un Func <>? No puedo recordar).

var whereExpression = Expression.Lambda<.Func<.billmaps_lu, bool>>(myWhereClause1, bms);
var ce = Expression.Constant(whereExpression)

Luego vuelva a ingresar al lugar donde originalmente se encuentra "myWhereClause1".

Dedo cruzado funciona

Edit- Scrap that, SHOW MI ZEH CODEZ

public class Foo
{
    public List<string> Strings { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Func<Foo, bool> func =
            a => a.Strings.Any(b => b == "asdf");

        // b => b == "asdf";
        var bParameter = Expression.Parameter(typeof (string));
        var asdfConstant = Expression.Constant("asdf");
        var compare = Expression.Equal(bParameter, asdfConstant);
        var compareExpression = Expression.Lambda<Func<string, bool>>(compare, bParameter);
        var ceCompareExpression = Expression.Constant(compareExpression.Compile());

        // a => a.Strings.Any(compareExpression)
        var parameter = Expression.Parameter(typeof (Foo));

        var foosProperty = Expression.Property(parameter, typeof (Foo).GetProperty("Strings"));
        MethodInfo method = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(string));

        var anyMethod = Expression.Call(method, foosProperty, ceCompareExpression);

        var lambdaExpression = Expression.Lambda<Func<Foo, bool>>(anyMethod, parameter);

        // Test.
        var foo = new Foo {Strings = new List<string> {"asdf", "fdsas"}};

        Console.WriteLine(string.Format("original func result: {0}", func(foo)));
        Console.Write(string.Format("constructed func result: {0}", lambdaExpression.Compile()(foo)));

        Console.ReadKey();
    }
}


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