2016-10-28 2 views
1

Я довольно новичок в SQL и пытаюсь найти лучший способ добавить некоторые предопределенные данные. Я выяснил, что здесь я должен был использовать параметризованную команду, чтобы избежать атаки на sql-инъекцию, которая в этом случае не вызывает большого беспокойства, но я бы хотел избежать этой возможности и научиться делать это правильно ... Во всяком случае, здесь код я прямо сейчас:Добавьте несколько значений SQL с тем же параметризованным запросом?

 using (SqlTransaction trans = connection.BeginTransaction()) 
     { 
      foreach (IEnumerable<string> row in table.RowData) 
      { 
       using (SqlCommand sql = new SqlCommand("INSERT INTO " + table.Title 
        + " (" + string.Join(", ", table.Headers) 
        + ") VALUES (" + string.Join(", ", table.Headers.Select(x => "@" + x)) + ");", connection, trans)) 
       { 

        for (int i = 0; i < table.Headers.Count(); i++) 
        { 
         if (string.IsNullOrEmpty(row.ElementAt(i))) 
         { sql.Parameters.AddWithValue("@" + table.Headers.ElementAt(i), DBNull.Value); } 
         else 
         { sql.Parameters.AddWithValue("@" + table.Headers.ElementAt(i), row.ElementAt(i)); } 
        } 
        sql.ExecuteNonQuery(); 
       } 
      } 
      trans.Commit(); 
     } 

Это похоже на работу, и все данные попадают туда, но это чувствует "неэффективным для меня. Я обертываю его в транзакцию, поэтому есть только одно коммит, но каждый раз он создает параметры и просто устанавливает разные значения для каждой строки.

Есть ли способ сделать это с использованием одних и тех же параметров, но просто установить разные значения в строке? Или это лучший способ сделать это, и я не должен беспокоиться об этом?

Заранее благодарим за любую помощь, которую вы можете дать.

+2

объектов в C# дешевы. Может ли быть более эффективным? Возможно. Но вы не собираетесь получать много чего из этого, повторно используя параметр, и вы можете даже ввести некоторые тонкие ошибки, сделав это. Я советую против этого. – Amy

+0

С дополнительным контекстом мы могли бы помочь лучше, потому что мы не знаем, должна ли эта оболочка быть повторно используемой оболочкой вокруг API SQL, конкретный запрос с множеством операторов и значений вставки, или что - ответ зависит. – Yushatak

+3

Извините, что вам нужно сказать об этом, но это доступно для инъекционной атаки - вы объединяете переменные строки (table.Title) в свой запрос. – Hogan

ответ

2

Мы можем сделать то, что вы хотите, анализируя заголовки в параметры на этапе предварительной обработки. Я также удалил явную транзакцию, потому что каждая отдельная вставка уже получает неявную транзакцию по умолчанию (зачем платить штраф за эффективность двух транзакций?).

using (var command = new SqlCommand()) { 
    command.CommandText = 
     "INSERT INTO " + table.Title + " (" 
     + string.Join(", ", table.Headers) 
     + ") VALUES (" 
     + string.Join(", ", table.Headers.Select(x => "@" + x)) 
     + ");"; 
    command.Connection = connection; 

    foreach (var header in table.Headers) { 
     /* 
      Add all parameters as strings. One could choose to infer the 
      data types by inspecting the first N rows or by using some sort 
      of specification to map the types from A to B. 
     */ 
     command.Parameters.Add("@" + header, typeof(string)); 
    } 

    foreach (var row in table.RowData) { 
     for (var i = 0; i < table.Headers.Count(); i++) { 
      if (!string.IsNullOrEmpty(row.ElementAt(i))) { 
       command.Parameters["@" + table.Headers.ElementAt(i)].Value = row.ElementAt(i); 
      } 
      else { 
       command.Parameters["@" + table.Headers.ElementAt(i)].Value = DBNull.Value; 
      } 
     } 

     command.ExecuteNonQuery(); 
    } 
} 
+0

Ок, я получаю это, за исключением того, что command.Parameters.Add() требует второго параметра типа данных ... Не слишком сложно сделать, но приятно, что AddWithValue, похоже, автоматически распознает тип данных ... – sfaust

+1

@sfaust It на основе входящего значения. Поскольку мы поднимаем логику, вам нужно будет определить тип раньше времени. Один из способов - вывести тип данных, используя первые строки 'n'. – Kittoes0124

+0

Да, не слишком сложно сделать, просто хотел указать на это в основном для кого-то другого, который приходит вместе с этим вопросом. Я немного изменю, чтобы получить типы данных, но спасибо, вот что я искал! – sfaust

0

Да, вы можете быть намного эффективнее, повторно используя объекты SqlParameter. Вот некоторые псевдо-код:

 const string sql = "INSERT INTO table1 (column1) VALUES (@p0)"; 
     using (var sqlCommand = new SqlCommand(sql, connection, transaction)) 
     { 
      var param1 = sqlCommand.Parameters.Add("@p0", SqlDbType.Int); 
      foreach (var row in table) 
      { 
       param1.Value = row["value"]; 
       sqlCommand.ExecuteNonQuery(); 
      } 
     } 
+0

Это делает что-то другое. Это добавляет только один столбец в строку. Как именно это ответ? – Hogan

+1

Это показывает вам * HOW * для повторного использования SQL-параметров в foreach, что было суть вопроса. Используя небольшое критическое мышление, пользователь получит полный ответ. – jhilden

+1

Не ужасно вообще, @jhilden создает параметр вне цикла foreach, где я его строю внутри цикла. Первоначальный код создает новые каждый раз, этот код не является. Как это ужасно? – sfaust

0

это мой пример вставки, которая работает для меня

private void insertWordCount(string songId, string wordId, int wordCount) 
    { 
     string query = "insert into songs_words_conn values(@wordId,@songId,@wordCount)"; 
     SqlCommand cmd = new SqlCommand(query, conn); 

     cmd.Parameters.AddWithValue("@wordId", wordId); 
     cmd.Parameters.AddWithValue("@songId", songId); 
     cmd.Parameters.AddWithValue("@wordCount", wordCount); 

     cmd.ExecuteNonQuery(); 
    } 
+0

Это действительно работает, и это то, что я делаю. Однако, чтобы добавить несколько значений в таблицу, он должен был бы зацикливать, который создает параметры несколько раз, чего я пытался избежать ... – sfaust

Смежные вопросы