Construyendo consultas genéricas de Linq

.net c# database expression-trees linq

Pregunta

Tengo una base de datos con dos tablas:

public class A {
    public string Name { get; set; }
    public int Id { get; set; }
}

public class B {
    public int Id { get; set; }
    public bool Prop1 { get; set; }
    public bool Prop2 { get; set; }
    public bool Prop3 { get; set; }
    public bool Prop4 { get; set; }
}

public class DataContext : DbContext {
    DbSet<A> Table1 { get; set: }
    DbSet<B> Table2 { get; set; }
}

Quiero escribir la función, que tomará como parámetro "Prop1", "Prop2", ..., "PropX" y devolverá las filas correspondientes de Table1. Algo como esto:

public List<A> GetByProp(string prop) {
    var result = new List<A>();

    using (var db = new DataContext()) {
        result = db.Table1.Join(db.Table2, t1=>t1.Id, t2=>t2.Id, (t1,t2)=>new {t1,t2}).
                           Where(????????). //t=>t.t2.prop == true
                           Select(t=>t.t2);

    }
    return result;
}

¿Cuál es la forma correcta de hacer esto?

Intenté usar Expression Tree, pero me quedé atascado en sus ...

  1. ¿Cómo construir la expresión con dos puntos? (t.t2.prop == verdadero)

  2. ¿Cómo puedo pasar el tipo anónimo (generado por Join ()) a genérico?

    var p = Expression.Parameter(typeof(???), t2); //??? - anonymous class
    var t = Expression.Constant(true, typeof(bool));
    var e = Expression.Equal(p, t);
    var l = Expression.Lambda<Func<???, bool>>(e, p);
    

Respuesta aceptada

¿Qué hay de colocar la condición como parte de la fuente del método Join ?

Con ese enfoque, su condición requiere Expression<Func<B, true>> y usted puede hacer fácilmente uno usando Expression Tree.

List<T> result;

var param = Expression.Parameter(typeof(B), "x");
var trueExp = Expression.Constant(true);
var condition = Expression.Equal(Expression.Property(param, prop), trueExp);
var whereLambda = Expression.Lambda<Func<B, bool>>(condition, param);

using (var db = new DataContext())
{
    result = db.Table1
               .Join(db.Table2.Where(whereLambda),
                     t1 => t1.Id,
                     t2 => t2.Id,
                     (t1, t2) => new { t1, t2 })
               .Select(t => t.t1)
               .ToList();
}
return result;

Actualizar

Si desea seguir su diseño inicial, debe hacer que el compilador infiera su tipo anónimo:

public static Expression<Func<T, bool>> GetPropertyCondition<T>(T source, string prop)
{
    var param = Expression.Parameter(typeof(T), "x");
    var trueExp = Expression.Constant(true);
    var condition = Expression.Equal(
                        Expression.Property(
                            Expression.Property(param, "t2"), prop),
                            trueExp);
    var whereLambda = Expression.Lambda<Func<T, bool>>(condition, param);
    return whereLambda;
}

y puedes llamarlo así:

var result = new List<A>();

var anonymous = new { t1 = (A)null, t2 = (B)null };
var condition = GetPropertyCondition(anonymous, prop);

using (var db = new DataContext())
{
    result = db.Table1.AsQueryable()
               .Join(db.Table2.AsQueryable(), t1 => t1.Id, t2 => t2.Id, (t1, t2) => new { t1, t2 })
               .Where(condition)
               .Select(t => t.t1)
               .ToList();
}
return result;

utiliza el hecho de que todos los objetos de tipo anónimo con el mismo conjunto de propiedades (tanto el nombre de la propiedad como el tipo de propiedad tienen que coincidir) dentro del conjunto comparten la misma clase anónima subyacente. Entonces, typeof(anonymous) aquí coincide con el tipo devuelto por el método de extensión Join .



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