Building Linq generic queries

.net c# database expression-trees linq

Question

I have a database with two tables:

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

I want to write function, which will take as parameter "Prop1", "Prop2", ... , "PropX" and return appropriate rows from Table1. Something like this:

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

What is a proper way to do this?

I've tried to use Expression Tree, but I got stucked their...

  1. How to build expression with two dots? (t.t2.prop == true)

  2. How can I pass anonymous type (which Join() generates) to generic

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

Accepted Answer

How about placing the condition as a part of Join method source?

With that approach your condition requires Expression<Func<B, true>> and you can easily make one using 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;

Update

If you want to follow your initial design you should make the compiler infer your anonymous type:

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

and you can call it like that:

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;

it uses the fact, that every anonymous type object with the same set of properties (both property name and property type have to match) within the assembly share the same underlying anonymous class. So typeof(anonymous) here matches type returned by Join extension method.



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why