【问题标题】:Construct Query with Linq rather than SQL strings使用 Linq 而不是 SQL 字符串构造查询
【发布时间】:2010-09-23 00:10:09
【问题描述】:

我有一个使用动态查询的 2.0 服务器控件,格式大致如下:

string sql = "Select " + columnvariable + " FROM " + tablenamevariable

因此,显然,您可以从数据库中的任何有效表中为其指定任何有效的列名,并且它将列中的值返回到 DataReader(在这种情况下)。

我正在尝试减少代码库中显式零碎 SQL 的数量,并且更愿意在 LINQ 中执行此操作。是否有捷径可寻?它甚至是可取的吗?我想在这种情况下生成的零碎 SQL 会非常通用,以至于在这种情况下不会真正造成安全问题。

即便如此,这似乎是一些相当基本的功能,所以我很好奇。我已经在我的项目中包含 System.Linq.Dynamic ,但这似乎不再允许程序员动态选择他们想要动态列的表。

我并不是要讨论这个问题。我想要一个像“是的,这是可能的而且微不足道的,这就是如何……”或“是的,但前提是您构建了这套精心设计的处理程序类并基本上重写了 LINQ 的部分,这就是如何……”这样的答案。

然而,我很想知道人们是否认为在 LINQ 中做这种事情最好被描述为 a) 一个愉快的好主意或 b) 疯狂的谈话。

【问题讨论】:

  • 不完全是我的想法......也许可以举个例子......
  • 在可能的情况下摆脱它可能是个好主意。您是否考虑过如果(例如通过恶意用户输入)tablenamevariable 变为" myTable; DROP TABLE ReallyImportantTable;" 会发生什么情况。阅读 SQL 注入。
  • @Winston:这是一个很好的观点。这种注入的风险很小,因为数据将通过定制服务器控件的配置属性输入,只有我们组织的技术人员才能访问。当有权访问控件的人想要做类似的事情时,他们会用更简单、更具破坏性的方式来做这件事。即便如此……如果可以的话,我宁愿安全也不愿后悔。因此,原来的问题。到目前为止,我不得不问“我还有什么其他选择?”

标签: c# .net sql linq


【解决方案1】:

因为您正在寻找一个简单的答案......不,这在 LINQ 中是不可能的。

LINQ 本质上是强类型的,而您正在寻找完全动态的东西。如果您的表和列是运行时变量(即字符串),那么您的 SQL 将需要是一个字符串……或者您需要使用 ORM(NHibernate、L2S、EF 等)来访问数据。

【讨论】:

  • 嗨,谢谢,是的,我得出了这个结论。但幸运的是,我正在学习很多东西。
【解决方案2】:

这里有一个来自好朋友 Scott Gu(thrie) 的例子:

Dynamic LINQ, Part 1

【讨论】:

  • 那是我看到的第一篇文章。不幸的是,我需要能够从表中动态选择表以及我想要的列。在示例文章中,查询仅限于 products 表。
  • 赞成粘贴 Google 的“动态 LINQ”的最高结果,它甚至没有为原始问题中的一个基本问题提供解决方案?如果你能得到它,那就太好了......
  • 认真的吗?你寻求帮助,然后斥责那些试图帮助你的人的回答?将来可能对您的事业没有帮助...
  • 严格来说,现在也对我的事业没有帮助。我选择退出我的命名帐户正是因为这个地方投票系统的奇异和任意性质。我并不是批评您发布一个懒惰的答案本身,我批评的是无论哪个 twonk 认为它比我自己的认真考虑问题的各个方面的答案更有帮助。我承认,你确实应该得到比发布一些简单但不起作用的东西的人更多的荣誉。
  • 好吧,Dynamic LINQ 文章是一个开始......让它适用于您的场景需要更多的努力和信息,但是给它投反对票并不酷,特别是因为很多人甚至不了解动态 LINQ,因此可以合理地假设您对此一无所知。
【解决方案3】:

Rick Strahl 博客中的这篇文章似乎在谈论这个话题的某种意义:

http://www.west-wind.com/Weblog/posts/314663.aspx

在没有任何更清楚的情况下,我想我会根据这一点做出选择。

任何其他潜在的回答者都应该考虑他们的潜在答案是否比上述链接更清楚。如果没有,那么参与其中可能会浪费您的时间。

编辑:虽然这在上面提到的文章中链接到这个:

http://www.west-wind.com/WebLog/ShowPost.aspx?id=134706

完美地阐明了我的问题,实际上是另一篇文章的前传。令人着迷的东西。

【讨论】:

    【解决方案4】:

    你从错误的角度来处理这个问题。你认为你想要这个:

    void DoStuff(tablenamevariable, columnvariable)
    {
        string sql = "Select " + columnvariable + " FROM " + tablenamevariable 
        // read data 
    }
    
    
    DoStuff("MyTable", "MyColumn");
    

    为什么是那个界面?这有什么问题:

    void DoStuff<T>(IQueryable<T> query)
    {
          // read data
    }
    
    
    DoStuff(from t in MyTable select t.MyColumn);
    

    这仍然定义了调用函数中的表和列,但具有更好的类型安全性和灵活性。例如,如果需要,您现在可以包含 Where 子句。

    DoStuff(from t in MyTable where t.OtherColumn == 5 select t.MyColumn);
    

    【讨论】:

    • 这是个好东西。我希望我能投票赞成。一个主要问题。在您的第二个示例中,DoStuff 的参数是字符串吗?还是光头声明?我打算现在继续看看这个 IQueryable 接口,所以答案可能就在那里。但无论如何,这更有帮助。如果我能让它在我的应用程序中按预期工作,那就更是如此了!
    • 这不是一个字符串,它是一个秃头语句(实际上是表达式)。对于字符串,如果您编写了“MyTabel”,您将在运行时才发现错误。我的方式,你会在编译时看到错误。
    • 这无疑会在其他时间为我省去一些麻烦,但现在很明显 L2S 不是这项工作的工具。尽管如此,IQueryable 接口对我来说还是新的,看起来它会派上用场,所以谢谢!
    【解决方案5】:

    很遗憾,您的问题并未说明 columnvariable 的确切来源,但如果允许我做出以下假设,那么我想我可以为您提供一个近乎微不足道的解决方案:

    • 假设 1:columnvariable 是一个通过方法调用传递的值,但最终它是一个常量。 IE。它不是用户提供的字符串,不是从文件中读取的,等等。

    • 假设 2:您可以访问为 columnvariable 传入这些常量的所有代码。

    如果这些假设成立,那么我的建议是更改包含以下内容的方法:

    string sql = "Select " + columnvariable + " FROM " + tablenamevariable
    

    变成更像这样的东西:

    public IQueryable<Customer> GetCustomers<T>(Expression<Func<Customer, T>> column)
    {
        return db.Customers.Select(column);
    }
    

    然后代替

    // Assuming "CustomerName" is the name of a column in the Customers table
    var myCustomers = GetCustomers("CustomerName");
    

    你会说

    // Properly type-safe and compile-time checked
    var myCustomers = GetCustomers(c => c.CustomerName);
    

    【讨论】:

    • 我认为这可能会受益于一些澄清。本质上,我正在制作一个服务器控件,它假定使用它的人熟悉底层数据库的数据库架构。所以你有一个服务器控制标签,它把表和列作为属性。然后这些应该访问相关数据并将其返回。 LINQ 似乎不是这项工作的工具。
    【解决方案6】:

    如果您需要它是完全动态的,即您可以为表名和列名设置任何字符串,那么我怀疑您唯一的选择是手动构建表达式树。比如……

    public IQueryable<TResult> GetColumn<TResult>(string tableName, string columnName)
    {
        // Get the table, e.g. for "Customers" get db.Customers
        var table = db.GetType().GetField(tableName).GetValue(db);
    
        // Find the specific type parameter (the T in IQueryable<T>)
        var iqueryableT = table.GetType().FindInterfaces((ty, obj) => ty.IsGenericType && ty.GetGenericTypeDefinition() == typeof(IQueryable<>), null).FirstOrDefault();
        var type = iqueryableT.GetGenericArguments()[0];
    
        // Construct a query that retrieves the relevant column
        var param = Expression.Parameter(type);
        var prop = Expression.Property(param, columnName);
        var expression = Expression.Lambda(prop, param);
    
        // Call Queryable.Select() with the appropriate parameters
        // Notice there are two Select methods, need to get the right one...
        return (IQueryable<TResult>) typeof(Queryable).GetMethods(BindingFlags.Static | BindingFlags.Public)
            .Where(meth => meth.Name == "Select")
            .Select(meth => meth.MakeGenericMethod(type, typeof(TResult)))
            .Where(meth => meth.GetParameters()[1].ParameterType == typeof(Expression<>).MakeGenericType(typeof(Func<,>).MakeGenericType(type, typeof(TResult))))
            .First()
            .Invoke(null, new object[] { table, expression });
    }
    

    然后你可以这样称呼它:

    var customerNames = GetColumn<string>("Customers", "CustomerName");
    

    当然,您仍然需要知道列的类型,因为您希望它返回正确的强类型可查询。或者,如果您希望它自动将 all 数据转换为字符串,可以添加 Expression.Convert;那么你不需要每次都指定string,但是你的列数据当然会被转换成字符串,可能会有损失。

    【讨论】:

    • 神圣的咒骂!那是一段非常有用的代码。我想我需要几天的时间才能解开那里发生的所有事情。我肯定会在某个时候尝试一下。
    【解决方案7】:

    您的查询:

    string sql = "Select " + columnvariable + " FROM " + tablenamevariable

    可以像这样使用 LINQ 进行“翻译”:

    var sql = (from tablenamevariable select columnvariable).ToList();

    现在“sql”变量包含存储在“tablenamevariable”中的“columnvariable”列表。

    您可以使用 LINQ 使其变得简单。

    【讨论】:

    • 如果它有效,我会喜欢它,但它没有。至少不是直接剪切和粘贴工作。
    猜你喜欢
    • 2012-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多