动态构建IQueryable

本文关键字:IQueryable 构建 动态 | 更新日期: 2023-09-27 18:23:43

我有以下IQueryable:

        IQueryable<Class1> queryable =
            (from c1 in DbContext.Set<Class1>()
             from c2 in DbContext.Set<Class2>()
             from c3 in DbContext.Set<Class3>()
             where c1.Id == c2.Class1Id
             && c2.Id == c3.Class2Id
             && c3.ValueAsString == val
             select c1);

在上面的例子中,val是一个字符串。然而,Class3还有其他几个成员:

    public string ValueAsString { get;private set; }
    public int? ValueAsInteger { get; set; }
    public DateTime? ValueAsDate { get; set; }
    public decimal? ValueAsDecimal { get; set; }
    public bool? ValueAsBoolean { get; set; }

我需要根据"val"的类型修改IQueryable,它可能是上面5种类型之一。是否可以将IQueryable构建为:

        IQueryable<Class1> queryable =
            (from c1 in DbContext.Set<Class1>()
             from c2 in DbContext.Set<Class2>()
             from c3 in DbContext.Set<Class3>()
             where c1.Id == c2.Class1Id
             && c2.Id == c3.Class2Id                 
             select c1);

然后根据"val"的类型在执行之前添加最后一个在哪里?例如,如果val是十进制,则附加

c3.ValueAsDecimal == val

动态构建IQueryable

对于您的任务来说,使用表达式而不是像linq这样的sql会更方便。正如我所看到的,你的类通过主键相互连接,如果使用属性,这个查询可以大致如下所示:

Expression<Func<Class3, bool>> filterExpression = GetFilterExpression(val); //returns expression bases on val type
var queryable = DbContext.Set<Class1>()
                             .Include(cl1=>cl1.Class2.Class3) //or .Include(cl1=>cl1.Class2.Select(cl2=>cl2.Class3)) depending on your object relationships
                             .Where(filterExpression);

如果需要为Class1类型的根实体加载Class2和Class3实例,则使用此处的Includes。如果你不需要它们,你可以跳过.Include()构造。

GetFilterExpression示例:

public Expression<Func<Class1, bool>> GetFilterExpression(string value) 
{ 
    return cl1 => cl1.Class2.Class3.ValueAsString == value;
}

这实际上很容易,当条件通过时,您只需调用.Where(...)即可。您只需预先选择所有值c1c2c3,在添加了.Where(...)调用后,就可以从结果中选择c1值。像这样:

var q = (
    from c1 in dbContext.Set<Class1>()
    from c2 in dbContext.Set<Class2>()
    from c3 in dbContext.Set<Class3>()
    where c1.Id == c2.Class1Id
    && c2.Id == c3.Class2Id
    select new { c1, c2, c3 }
);
object var = ...; // Some value
if (var is decimal)
{
    q = q.Where(x => x.c3.ValueAsDecimal == (decimal)var);
}
else if (var is DateTime)
{
    q = q.Where(x => x.c3.ValueAsDate == (DateTime)var);
}
// TODO: Add other types of 'var' 
var queryable = q.Select(x => x.c1);