为什么我不能在IEnumerables上构建动态查询?

时间:2015-10-22 18:45:13

标签: c# linq

我想在用户在运行时提供的列上订购IEnumerable。要动态构建LINQ,我知道我可以:

  1. 编写switch case语句以执行各种情况;
  2. 构建表达式树并将其作为xyz.OrderBy(myExpression.Compile());
  3. 传递
  4. 通过调用IEnumerableIQueryable作为xyz.AsQueryable()进行管道,然后执行.OrderBy(userInputString);
  5. 关于上述第三种方法的简单问题 - 为什么不直接在IEnumerable上提供动态查询构建功能?

2 个答案:

答案 0 :(得分:1)

这样做的好处很少,但有许多缺点。在任何情况下,你需要(或者一个库)能够将一个字符串转换为一组关于该做什么的指令,你需要能够解析字符串以确保它们不会要求你不想要的东西。大多数程序员(尤其是新程序员)都不了解他们所做的事情的安全后果,所以他们在不知不觉中编写了不安全的代码。没有这样的库迫使程序员实际解析他们应该一直在做的输入。

典型的讨论可能会出现:为什么我们至少无法以对象属性的名义传递命令?好。那么为什么我们不能通过传递一个属性的名称来选择?好。那么为什么我们不能通过传递像prop ==&#34这样的字符串表达式来过滤这个"?然后突然之间我们遇到了安全问题因为程序员不愿意检查用户是否提供类似这样的东西"或" 1" =" 1作为输入。

程序员开始编写代码如下:

var usercheckstring="Username='"+txtUser.Text+"' and Password='"+txtPassword.Text+"'";
var user=db.Users.Where(usercheckstring).FirstOrDefault();
if (user!=null)
{
   ... Log in the user here ...
}

然后当某些黑客在' or Admin='1中输入密码时会哭,然后将其作为管理员登录。然后人们说当C#或LINQ始终是程序员的错误时,它是不安全的。

虽然Scott Gu是一个好人,几乎无可争议地是一个优秀的程序员,但即使他陷入了同样的陷阱,并在他的DynamicLinq博客条目中给出了一些令人难以置信的错误建议:http://weblogs.asp.net/scottgu/dynamic-linq-part-1-using-the-linq-dynamic-query-library他说你在哪里可以做到这一点:

.Where(String.Format("CategoryID={0}" & Request.QueryString["id"]) 

WHOA!当然,你可以做到这一点,但你确实应该不应该(除了它有语法错误的事实)。坏斯科特,不好!他向世界展示了如何进行DynamicLinq注射。虽然不像典型的SQL注入攻击那么糟糕,但它也会以同样的方式受到影响。

盲目地使用用户提供的数据总是一个坏主意,这是人们想用动态LINQ做的99%(我要么在互联网上找到统计数据,要么就是我做了它)。他们无法弄清楚如何使用正常的LINQ来做,想要采取一个不好的捷径,然后被烧毁。一个开关/盒不是那么难,需要几行代码,不安全,并且比在对象上进行反射以尝试将字符串转换为lambda要快得多。

答案 1 :(得分:0)

我已经从ScottGu博客上提供的Dynamic Query Library向我的项目导入了一个Dynamic Query类。这为我提供了扩展方法来构建动态查询,方法是在运行时提供字段名称来对集合进行排序。使用这个库我可以做如下事情:

var employees = new List<Employee>
{
    new Employee
    {
        DeptId = 1,
        Id = 1,
        Name = "JKL"
    },
    new Employee
    {
        DeptId = 1,
        Id = 2,
        Name = "ABC"
    },
    new Employee
    {
        DeptId =  5,
        Id = 3,
        Name = "LMN"
    }
};

var query1 = employees.OrderBy(e => e.Name);
foreach (var employee in query)
{
    Console.WriteLine(employee.Name); //Prints names
}

var query2 = employees
    .AsQueryable()
    .OrderBy("Name");
foreach (var employee in query2)
{
    Console.WriteLine(employee.Name); //Prints exact output as foreach loop above.
}

请注意,在query1中,我必须在.OrderBy()中写一个lambda,我只是直接在query2中提供字段名称,但我需要做{集合上的{1}}。

员工定义为:

.AsQueryable()

所以,问题的答案是,如果我想知道为什么我不能直接在public class Employee { public int Id; public string Name; public int DeptId; } OrderBy("Name"),我应该发邮件并询问ScottGu或阅读有关相同提示的源代码。