代码之家  ›  专栏  ›  技术社区  ›  Chuck D user2117118

用于多级查询的动态Linq表达式

  •  2
  • Chuck D user2117118  · 技术社区  · 8 年前

    我有下面的linq表达式,我想动态构建它。

    我遇到的问题是,我无法构建表达式树来容纳复杂的select/any语句。我已经读到,为了使用方法,你必须像下面那样调用方法:

     Invoke("Any")
    

    我需要动态构建的表达式:

    Expression<Func<TXLifeRequest, bool>> filter = (txreq) => 
    txreq.IsDeleted == false &&
    txreq.OLifE.Holding.Select(h => h.Policy)
        .Any(p => p.RequirementInfo
            .Any(r => r.ReqStatus.tc == OLI_LU_REQSTAT.OLI_REQSTAT_OUTSTANDING.tc));
    
    var results = db.GetQueryable(filter)
                    .Include(r => r.OLifE.Holding.Select(h => h.Policy)
                       .Select(p => p.RequirementInfo)).ToList();
    

    以下是我的模型课程:

    奥利·鲁·雷吉斯塔特

    public partial class OLI_LU_REQSTAT : BaseType {
    
        public string tc { get; set; }
    
        public string Value { get; set; }
    }
    

    TxLifeRequest(TxLifeRequest)

    public partial class TXLifeRequest : BaseEntity
    {
        public virtual OLifE OLifE { get; set; }
    
        ...
    }
    

    奥利夫

    public partial class OLifE : BaseEntity
    {
        public virtual List<Holding> Holding { get; set; }
            ...
    }
    

    霍尔丁

    public class Holding : BaseEntity
    {
        public virtual Policy Policy { get; set; }
        ...
    }
    

    政策

    public class Policy : BaseEntity
    {
        public virtual List<RequirementInfo> RequirementInfo { get; set; }
    
        ...
    }
    

    要求信息

    public partial class RequirementInfo : BaseEntity
    {
         public virtual OLI_LU_REQSTAT ReqStatus { get; set; }
    
        ...
    }   
    

    目前我正在针对getproperty运行一个foreach反射,但无法理解文档以便在对象模型中降低3-4个级别:

    ParameterExpression parameter = Expression.Parameter(typeof(T), "i");
    MemberExpression property = Expression.Property(parameter, propertyName);
    ConstantExpression constant = Expression.Constant(val, propertyType);
    
    
    var condition =
        Expression.Lambda<Func<T, bool>>(
            Expression.Equal(
                property,
                constant
            ),
            parameter
        );
    
    result = AppendExpression(result, condition, result);
    

    更新1。) 添加了RequirementInfo。添加所有类属性没有意义,因为存在所需的类结构。

    1 回复  |  直到 8 年前
        1
  •  4
  •   xanatos    8 年前

    相当长。我觉得“可维护”太复杂了,如果需要做一些更改,就变得相当困难。即使使其“动态”以便控制(启用或禁用部件)也是困难的。

    鉴于

    // Enumerable.Any()
    static readonly MethodInfo anyTSource = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                             where x.Name == nameof(Enumerable.Any)
                                             let args = x.GetGenericArguments()
                                             where args.Length == 1
                                             let pars = x.GetParameters()
                                             where pars.Length == 2 &&
                                                 pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
                                                 pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], typeof(bool))
                                             select x).Single();
    
    // Enumerable.Select()
    public static readonly MethodInfo selectTSourceTResult = (from x in typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public)
                                                              where x.Name == nameof(Enumerable.Select)
                                                              let args = x.GetGenericArguments()
                                                              where args.Length == 2
                                                              let pars = x.GetParameters()
                                                              where pars.Length == 2 &&
                                                                        pars[0].ParameterType == typeof(IEnumerable<>).MakeGenericType(args[0]) &&
                                                                        pars[1].ParameterType == typeof(Func<,>).MakeGenericType(args[0], args[1])
                                                              select x).Single();
    

    那是林肯 Enumerable.Any() 和 Enumerable.Select()

    把你的

    // txreq => ((txreq.IsDeleted == False) AndAlso txreq.OLifE.Holding.Select(h => h.Policy).Any(p => p.RequirementInfo.Any(r => (r.ReqStatus.tc == OLI_LU_REQSTAT.OLI_REQSTAT_OUTSTANDING.tc))))
    string str1 = filter.ToString();
    

    将表达式与生成的表达式进行比较

    使用一些快捷方式(而不是 txreq.IsDeleted == False 我用 !rxreq.IsDeleted 而不是包括 OLI_LU_REQSTAT.OLI_REQSTAT_OUTSTANDING.tc 我在构建表达式树时读取它的值,并将其放入 Expression.Constant() ,

    var par = Expression.Parameter(typeof(TXLifeRequest), "txreq");
    
    // txreq.IsDeleted == false (simplified to !txreq.IsDeleted)
    var notIsDeleted = Expression.Not(Expression.Property(par, "IsDeleted"));
    
    // r => 
    var par4 = Expression.Parameter(typeof(RequirementInfo), "r");
    
    // OLI_LU_REQSTAT.OLI_REQSTAT_OUTSTANDING.tc
    var oli_reqstat_outstanding_tc = Expression.Constant(OLI_LU_REQSTAT.OLI_REQSTAT_OUTSTANDING.tc);
    
    // r.ReqStatus.tc == OLI_LU_REQSTAT.OLI_REQSTAT_OUTSTANDING.tc
    Expression<Func<RequirementInfo, bool>> any2Lambda = Expression.Lambda<Func<RequirementInfo, bool>>(Expression.Equal(Expression.Property(Expression.Property(par4, "ReqStatus"), "tc"), oli_reqstat_outstanding_tc), par4);
    
    // To check if it is correct
    //any2Lambda.Compile();
    
    // p => 
    var par3 = Expression.Parameter(typeof(Policy), "p");
    
    // p.RequirementInfo.Any(...)
    Expression<Func<Policy, bool>> any1Lambda = Expression.Lambda<Func<Policy, bool>>(Expression.Call(anyTSource.MakeGenericMethod(typeof(RequirementInfo)), Expression.Property(par3, "RequirementInfo"), any2Lambda), par3);
    
    // To check if it is correct
    //any1Lambda.Compile();
    
    // h => 
    var par2 = Expression.Parameter(typeof(Holding), "h");
    
    // h.Policy
    Expression<Func<Holding, Policy>> selectLambda = Expression.Lambda<Func<Holding, Policy>>(Expression.Property(par2, "Policy"), par2);
    
    // To check if it is correct
    //selectLambda.Compile();
    
    //txreq.OLifE.Holding
    var holding = Expression.Property(Expression.Property(par, "OLifE"), "Holding");
    
    // txreq.OLifE.Holding.Select(...)
    var select = Expression.Call(selectTSourceTResult.MakeGenericMethod(typeof(Holding), typeof(Policy)), holding, selectLambda);
    var any1 = Expression.Call(anyTSource.MakeGenericMethod(typeof(Policy)), select, any1Lambda);
    
    var and = Expression.AndAlso(notIsDeleted, any1);
    
    Expression<Func<TXLifeRequest, bool>> lambda = Expression.Lambda<Func<TXLifeRequest, bool>>(and, par);
    
    // To check if it is correct and/or use it
    //var compiled = lambda.Compile();
    

    如果我们尝试 lambda.ToString() 我们得到:

    txreq => (Not(txreq.IsDeleted) AndAlso txreq.OLifE.Holding.Select(h => h.Policy).Any(p => p.RequirementInfo.Any(r => (r.ReqStatus.tc == "SOMEVALUE"))))
    

    这就足够了。