【问题标题】:Creating where condition for LINQ within switch statement在 switch 语句中为 LINQ 创建 where 条件
【发布时间】:2016-09-25 08:06:13
【问题描述】:

所以基本上我必须为 4 个不同的角色获得 4 个不同的员工列表。 List of RolesList of Enum 如下:

public enum Roles {
       [Description("Level 1")]
       L1,

       [Description("Level 2")]
       L2,

       [Description("Level 3")]
       L3,

       [Description("Level 4")]
       L4
};
  • L1可以查看L1,L2,L3L4
  • L2可以查看L2,L3L4
  • L3 可以查看L3L4
  • L4 无权查看任何员工。

下面是我为获取员工编写的查询。

if (!Equals(Role, Enum.GetName(typeof(GlobalMethods.Roles), 3)))
{
      var _role = (GlobalMethods.Roles)Enum.Parse(typeof(GlobalMethods.Roles), Role,true);
      List<EmployeeViewModel> employees = new List<EmployeeViewModel>();
      switch (_role)
      {
            case GlobalMethods.Roles.L1:
                 employees = context.tblEmployees.Where(x => x.EmpID != homeViewModel.UserViewModel.EmpID).ToList().Select(x => new EmployeeViewModel
                 {
                      EmpActive = x.EmpActive,
                      EmpDOB = x.EmpDOB,
                      EmpName = x.EmpName
                 }).ToList();
                 break;
            case GlobalMethods.Roles.L2:
                 employees= context.tblEmployees.Where(x => x.EmpID != homeViewModel.UserViewModel.EmpID).ToList().Where(x=>x.EmpRole != Enum.GetName(typeof(GlobalMethods.Roles), 0)).Select(x => new EmployeeViewModel
                 {
                      EmpActive = x.EmpActive,
                      EmpDOB = x.EmpDOB,
                      EmpName = x.EmpName,
                 }).ToList();
                 break;
            case GlobalMethods.Roles.L3:
                 employees = context.tblEmployees.Where(x => x.EmpID != homeViewModel.UserViewModel.EmpID).ToList().Where(x.EmpRole != Enum.GetName(typeof(GlobalMethods.Roles), 0) && x.EmpRole != Enum.GetName(typeof(GlobalMethods.Roles), 1)).Select(x => new EmployeeViewModel
                 {
                      EmpActive = x.EmpActive,
                      EmpDOB = x.EmpDOB,
                      EmpName = x.EmpName,
                 }).ToList();
                 break;
            default: break;
      }
}

所以在上面的代码中,我有相同的 LINQ 查询,但只有 where 条件不同。有什么方法可以准备一个动态的where 条件并一次性获取列表?

【问题讨论】:

  • 通过创建Expression tree(二进制表达式)并使用And将它们组合以确保最终结果是可行的
  • 我认为,我过度理解了您的要求,建议基于表达式树的通用解决方案,但确实为整体解决方案提供了很好的灵活性

标签: c# wpf linq


【解决方案1】:

这应该可行。它假设每个 GlobalMethods.Roles 可以查看具有更大 int 值的所有角色。角色就是你所说的规则。

if (!Equals(Role, Enum.GetName(typeof(GlobalMethods.Roles), 3)))
{
     var _role = (GlobalMethods.Roles)Enum.Parse(typeof(GlobalMethods.Roles), Role, true);
     List<EmployeeViewModel> employees = new List<EmployeeViewModel>();
     string[] viewableRoles = GetViewableRoles(_role);
     employees = context.tblEmployees
                .Where(x => x.EmpID != homeViewModel.UserViewModel.EmpID && viewableRoles.Contains(x.EmpRole))
                .Select(x => new EmployeeViewModel
                {
                    EmpActive = x.EmpActive,
                    EmpDOB = x.EmpDOB,
                    EmpName = x.EmpName
                }).ToList();
}

private string[] GetViewableRoles(GlobalMethods.Roles userRole)
{
    //Uncomment if L4 can actually view no roles, including itself.
    // /if (userRole == GlobalMethods.Roles.L4)
    // {
    //  return new string[0];
    // }

    IEnumerable<GlobalMethods.Roles> allRoles = Enum.GetValues(typeof(GlobalMethods.Roles)).Cast<GlobalMethods.Roles>();
    return (from role in allRoles 
                where (int)role >= (int)userRole 
                select role.ToString()).ToArray();
}

【讨论】:

  • 感谢美丽的解决方案.. 快乐编码.. :)
  • 荣誉,正确理解问题并提供简单的解决方案 +1
【解决方案2】:

这是简化的,但你可以这样做:

class Program
{
    static void Main(string[] args)
    {
        var list = new List<Person>
        {
            new Person{ Name = "John" },
            new Person{ Name = "Amy" }
        };

        Func<Person, bool> pred = null;
        Roles role = Roles.RoleA;
        switch (role)
        {
            case Roles.RoleA:
                pred = p => p.Name.StartsWith("J");
                break;
            case Roles.RoleB:
                pred = p => p.Name.StartsWith("A") && p.Name.Length >= 3;
                break;
            default:
                break;
        }

        var result = list.Where(pred);
    }
}

class Person
{
    public string Name { get; set; }
}

enum Roles
{
    RoleA,
    RoleB
}

【讨论】:

  • 感谢您的建议,但我想接受已接受的答案,因为它似乎更容易,并且解决方案已解决问题。 +1 为您的努力.. 快乐编码.. :)
【解决方案3】:

以下是通用表达式树扩展方法,以照顾以下要求(您当然需要修改以适应您的具体要求)

  • TIEnumerable 的最终类型
  • T1是where子句中过滤的列类型

  • 在编译Expression Tree to Func时,为给定类型T声明ParameterExpression以供重用,尽管这也可以在Extension methodFunc生成期间单独完成

    ParameterExpression parameterType = Expression.Parameter(typeof(T), "object");

    public static class CustomExpression
    {
     // Create Initial Expression Tree
     public static BinaryExpression InitialExpression<T,T1>(
                                                            ParameterExpression parameterType
                                                            string columnName,
                                                            T1 value)
    {
        // Optional can be taken outside the Extension method to create a Func<T,bool>
    
        //ParameterExpression parameterType = Expression.Parameter(typeof(T), "object");
    
        MemberExpression typeColumn = Expression.Property(parameterType, columnName);
    
        ConstantExpression constant = Expression.Constant(value, typeof(T1));
    
        return Expression.NotEqual(typeColumn, constant);
    }
    
    // Create Combined Expression Tree
    public static BinaryExpression CombinedExpression<T,T1>(this BinaryExpression mainExpression,
                                                            ParameterExpression parameterType
                                                            string columnName,
                                                            T1 value)
    {
        // Optional can be taken outside the Extension method to create a Func<T,bool>
    
        //ParameterExpression parameterType = Expression.Parameter(typeof(T), "object");
    
        MemberExpression typeColumn = Expression.Property(parameterType, columnName);
    
        ConstantExpression constant = Expression.Constant(value, typeof(T1));
    
        return Expression.And(mainExpression,Expression.NotEqual(typeColumn, constant));
    }
    }
    

以下是您案例中的调用层次结构:

  • 声明Linq 查询的最终二进制表达式:

    BinaryExpression finalBinaryExpression = null;

     switch (_role)
      {
            case GlobalMethods.Roles.L1:
              finalBinaryExpression =          
              CustomExpression.InitialExpression<EmployeeViewModel,int>     
              (parameterType,"EmpID",homeViewModel.UserViewModel.EmpID);
             break;
            case GlobalMethods.Roles.L2:
              finalBinaryExpression =     
              CustomExpression.InitialExpression<EmployeeViewModel,int>               
              (parameterType,"EmpID",homeViewModel.UserViewModel.EmpID)
              .CombinedExpression<EmployeeViewModel,Roles>                    
          (parameterType,"EmpRole",Enum.GetName(typeof(GlobalMethods.Roles), 0));
             break;
            case GlobalMethods.Roles.L3:
                finalBinaryExpression =       
                CustomExpression.InitialExpression<EmployeeViewModel,int>
                (parameterType,"EmpID",homeViewModel.UserViewModel.EmpID)
                .CombinedExpression<EmployeeViewModel,Roles>
    
          (parameterType,"EmpRole",Enum.GetName(typeof(GlobalMethods.Roles), 0))
                .CombinedExpression<EmployeeViewModel,Roles>  
    
          (parameterType,"EmpRole",Enum.GetName(typeof(GlobalMethods.Roles), 1));
             break;
        }
    
  • 通过如下编译二进制表达式创建genericFunc

    Func<T, bool> filterFunc = Expression.Lambda<Func<T, bool>>
                               (finalBinaryExpression, parameterType).Compile();
    
  • 最终结果应用上面创建的Func

      var finalResult = context.tblEmployees.Where(o => filterFunc(o));
    

【讨论】:

  • 这里的想法是创建一个组合表达式树,它可以组合所有的过滤器要求并申请最终的结果集
  • 同意你@Mrinal,这非常有用,但我的要求很简单,上面接受的答案给出了要求。为您的时间和精力 +1.. 快乐编码.. :)
  • @GuruprasadRao,请检查修改后的版本,以备日后使用,因为之前的会导致Null reference exception,我们需要单独创建初始表达式。我使用类似的代码对网格进行大量动态过滤
  • 当然@Mrinal ..感谢您的努力。我将来肯定会使用它.. 再次感谢你.. :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-17
  • 1970-01-01
  • 2015-08-27
  • 1970-01-01
  • 2011-12-09
  • 2013-10-06
相关资源
最近更新 更多