SQLCommand.ExecuteReader () не ограничивается только для чтения операторов - PullRequest
3 голосов
/ 28 июля 2010

Таким образом, по-видимому, ExecuteReader используется только для чтения, а ExecuteNonQuery используется для транзакций. Но по какой-то причине, даже когда я использовал ExecuteReader, я все еще могу запускать команды записи (Вставить, Обновить, Удалить) (набранные в textbox1). Что-то не так с моим кодом или я неправильно понимаю, как должен работать ExecuteReader?

//MY CODE

string sqlStatement = textbox1.Text;

System.Data.SqlClient.SqlConnectionStringBuilder builder =
  new System.Data.SqlClient.SqlConnectionStringBuilder();
builder.DataSource = ActiveServer;
builder.IntegratedSecurity = true;

System.Data.SqlClient.SqlConnection Connection = new 
   System.Data.SqlClient.SqlConnection(builder.ConnectionString);
Connection.Open();

System.Data.SqlClient.SqlCommand command = new 
  System.Data.SqlClient.SqlCommand(sqlStatement, Connection);
System.Data.SqlClient.SqlDataReader reader = command.ExecuteReader();

dataGridView1.AutoGenerateColumns = true;

bindingSource1.DataSource = reader;
dataGridView1.DataSource = bindingSource1;

reader.Close();
Connection.Close();

Ответы [ 3 ]

6 голосов
/ 28 июля 2010

ExecuteReader просто возвращает читателя, который может прочитать строки, возвращенные из процедуры SQL, - это не мешает запускать произвольный SQL на пути к предоставлению этого набора результатов.

Выполнение операций вставки / обновления / удаления, а затем немедленного возврата набора результатов (например, из кода, похожего на чтение), возможно, немного странно (читай: запах кода), и его следует проверить, чтобы увидеть, можно ли его разбить на отдельные действия.

5 голосов
/ 19 февраля 2013

Хотя оба выполняют sql, ожидается, что ExecuteReader возвратит записи, в то время как ExecuteNonQuery затронет количество записей.Следовательно, оба они разные. Но внутренне то, насколько они различны, зависит от конкретной реализации поставщика.Вы можете использовать ExecuteReader в одиночку для всех ваших действий с БД , потому что это просто сработало (до сих пор), но, поскольку это не документировано , это не совсем правильный подход для .Вы могли бы быть более ясным о своем намерении с ExecuteNonQuery.

Что касается производительности, я не думаю, что есть разница вообще.Я попытался с SQLite, MySqlClient, SqlClient, SqlServerCe и VistaDb и не увидел заметной разницы в пользу.И все они должны использовать ExecuteReader внутренне так или иначе.

Основы:

SqlClient:

private int InternalExecuteNonQuery(DbAsyncResult result, string methodName, bool sendToPipe)
{
    if (!this._activeConnection.IsContextConnection)
    {
        if (this.BatchRPCMode || CommandType.Text != this.CommandType || this.GetParameterCount(this._parameters) != 0)
        {
            Bid.Trace("<sc.SqlCommand.ExecuteNonQuery|INFO> %d#, Command executed as RPC.\n", this.ObjectID);
            SqlDataReader sqlDataReader = this.RunExecuteReader(CommandBehavior.Default, RunBehavior.UntilDone, false, methodName, result);
            if (sqlDataReader == null)
            {
                goto IL_E5;
            }
            sqlDataReader.Close();
            goto IL_E5;
        }
    IL_B5:
        this.RunExecuteNonQueryTds(methodName, flag);
    }
    else
    {
        this.RunExecuteNonQuerySmi(sendToPipe);
    }
IL_E5:
    return this._rowsAffected;
}

и

MySqlClient:

public override int ExecuteNonQuery()
{
  int records = -1;

  #if !CF
  // give our interceptors a shot at it first
  if ( connection != null && 
       connection.commandInterceptor != null &&
       connection.commandInterceptor.ExecuteNonQuery(CommandText, ref records))
    return records;
  #endif

  // ok, none of our interceptors handled this so we default
  using (MySqlDataReader reader = ExecuteReader())
  {
    reader.Close();
    return reader.RecordsAffected;
  }
}

Как видите, MySqlClient напрямую звонит ExecuteReader, тогда как SqlClient делает только для определенных условий. Имейте в виду, insert с и update с редко являются узким местом (часто это select с).

Как я уже сказал, вы не получите номерстрок, затронутых с помощью ExecuteReader, поэтому лучше использовать ExecuteNonQuery для выполнения запросов . Более прямая замена на ExecuteReader будет иметь значение ExecuteScalar, которое возвращает данные в первом столбце чтения первой строки.

Основы:

SqlClient:

override public object ExecuteScalar()
{
    SqlConnection.ExecutePermission.Demand();

    // Reset _pendingCancel upon entry into any Execute - used to synchronize state
    // between entry into Execute* API and the thread obtaining the stateObject. 
    _pendingCancel = false;

    SqlStatistics statistics = null;
    IntPtr hscp;
    Bid.ScopeEnter(out hscp, "<sc.sqlcommand.executescalar|api> %d#", ObjectID);
    try
    {
        statistics = SqlStatistics.StartTimer(Statistics);
        SqlDataReader ds = RunExecuteReader(0, RunBehavior.ReturnImmediately, true, ADP.ExecuteScalar);

        object retResult = null;
        try
        {
            if (ds.Read())
            {
                if (ds.FieldCount > 0)
                {
                    retResult = ds.GetValue(0);
                }
            }
            return retResult;
        }
        finally
        {
            // clean off the wire 
            ds.Close();
        }
    }
    finally
    {
        SqlStatistics.StopTimer(statistics);
        Bid.ScopeLeave(ref hscp);
    }
}

и

MySqlClient:

public override object ExecuteScalar()
{
    lastInsertedId = -1;
    object val = null;

    #if !CF
    // give our interceptors a shot at it first
    if (connection != null &&
        connection.commandInterceptor.ExecuteScalar(CommandText, ref val))
        return val;
    #endif

    using (MySqlDataReader reader = ExecuteReader())
    {
        if (reader.Read())
            val = reader.GetValue(0);
    }

    return val;
}

Так что не мешает использовать ExecuteReader для ExecuteScalar и никакой разницы в производительности вообще.

3 голосов
/ 28 июля 2010

Базовая реализация этих методов просто выполняет заданный оператор SQL, поэтому вы можете выполнить практически любой оператор с любым из этих методов.Просто конечный результат состоит в том, что читатель пытается вернуть ожидаемый набор результатов, тогда как другой метод не ожидает набор результатов.Вероятно, немного менее эффективно использовать ExecuteReader для запуска оператора, который не создает результирующий набор.Поставщик по-прежнему будет создавать объект для чтения данных, что добавит небольшую стоимость (вероятно, незначительную в большинстве случаев, если только поставщик не завершит повторный вызов сервера).

...