简体   繁体   English

在C#中动态构建可变长度IN SQL子句的最佳实践

[英]Best practice for dynamically building variable length IN SQL clause in C#

Recently I found myself in C#, doing some SQL. 最近,我发现自己在C#中进行SQL。 This is not exactly my area of expertise. 这不完全是我的专业领域。 I wrote some code that looks pretty ugly, and am failing to find a better solution anyplace. 我写了一些看起来很丑陋的代码,却无法在任何地方找到更好的解决方案。 Many of the answers here on SO pose SQL injection risks, and are essentially doing the same thing im doing in one way or another. 关于SO的许多答案都构成了SQL注入的风险,并且本质上是以一种或另一种方式做的事情。 The situation is, I have a form which a user provides a list of Store ID's in a form. 情况是,我有一个表单,用户在表单中提供了商店ID的列表。 When they click a button, a CSV export will be generated using the stores they provide as exclusion criteria for the query. 当他们单击按钮时,将使用他们提供的存储作为查询的排除条件来生成CSV导出。 The way I accomplished this is by setting up my SQL string as a constant, then using a string builder to dynamically append @X in an IN clause. 我完成此操作的方法是将SQL字符串设置为常量,然后使用字符串生成器将@X动态附加到IN子句中。 The code looks fairly bad but heres a quick snippet to better explain. 该代码看起来相当糟糕,但是这里有一个简短的片段,可以更好地进行说明。 For example, say the SQL query is this 例如,说SQL查询是这个

private readonly String _SELECT_UNEXPECTED_TOTES = "SELECT * FROM TABLE WHERE TABLE.Store IN ";

I then do the following (stores is an array of strings, sb is a string builder): 然后,我执行以下操作(stores是一个字符串数组,sb是一个字符串生成器):

                //get enough room for all the stores
                var sqlParams = new SqlParameter[stores.Length];
                conn.Open();
                //append our query
                sb.Append(_SELECT_UNEXPORTED_TOTES);
                //open the IN list
                sb.Append("(");
                //build the in list
                int I = 0;
                foreach (String s in stores)
                {
                    sb.Append("@");
                    sb.Append(I);
                    sb.Append(",");
                    I++;
                }
                //trim the trailing ,
                sb.Length -= 1;
                sb.Append(")");
                //make the actual parameters
                I = 0;
                foreach (String s in stores)
                {
                    sqlParams[I] = new SqlParameter("@" + I, SqlDbType.VarChar);
                    sqlParams[I].Value = s;
                    I++;
                }

Later on in code I then use these SQL params in a SqlStatement object. 稍后在代码中,我然后在SqlStatement对象中使用这些SQL参数。 Does .NET provide a better way to accomplish this? .NET是否提供更好的方法来实现这一目标? I dont know a lot about .NETs SQL objects, and for all I know this solution could be just as bad as a simple string replace... Any advice is welcome. 我对.NETs SQL对象不了解很多,而且我知道这种解决方案可能与简单的字符串替换一样糟糕...欢迎任何建议。

I don't believe there is an easier way of doing this through ADO.NET. 我不相信通过ADO.NET可以做到这一点。 But as some other users had mentioned Entity Framework would make this considerably cleaner and it really is not difficult to setup, especially on a small app. 但是,正如其他一些用户提到的那样,实体框架可以使它变得更加整洁,而且设置起来并不困难,尤其是在小型应用程序上。 It would turn your above code into: 它将上面的代码变成:

var data = context.TABLE.Where(t => stores.Contains(t.Store)).ToList();

Here's a much more LINQ-y way to accomplish this: 这是一种更LINQ-y的方式来完成此任务:

string[] storeIds = { "1Store", "2Store", "RedStore", "BlueStore" };
using (SqlCommand cmd = new SqlCommand())
{
    var paras = storeIds.Select((store, index) => new
        {
            name = String.Format("@p{0}", index),
            value = store
        });
    StringBuilder sb = new StringBuilder();
    sb.Append("select * from myTable");
    sb.Append(" where myTable.storeid in (");
    sb.Append(String.Join(",", paras.Select(p => p.name).ToList()));
    sb.Append(")");
    cmd.CommandText = sb.ToString();
    foreach (var para in paras)
    {
        cmd.Parameters.AddWithValue(para.name, para.value);
    }
    cmd.Connection = conn;
    // Now do whatever you want with the command ...

The "magic" being the use of the Enumerable.Select<TSource, TResult> Method (IEnumerable<TSource>, Func<Source, Int32, TResult>) overload, which gives you access to the index as well as the value of the elements in your array of IDs. “魔术”是使用Enumerable.Select <TSource,TResult>方法(IEnumerable <TSource>,Func <Source,Int32,TResult>)重载,它使您可以访问索引以及元素的值在您的ID数组中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM