SQLCommand.ExecuteReader() 不限制只读语句

发布于 2024-09-11 15:24:33 字数 895 浏览 5 评论 0原文

显然,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();

So apparently, ExecuteReader is used for read only and ExecuteNonQuery is used for transactions. But for some reason even when I used ExecuteReader I am still able to run write (Insert, Update, Delete) commands (typed in textbox1). Is there something wrong with my code or am I misunderstanding the way ExecuteReader is supposed to work?

//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();

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

缘字诀 2024-09-18 15:24:33

ExecuteReader 只是返回一个读取器,该读取器可以读取从 SQL 过程返回的行 - 它不会阻止您在提供该结果集的过程中运行任意 SQL。

执行插入/更新/删除,然后立即返回结果集(因此从看起来像读取的代码来看)可以说有点奇怪(读取:代码气味),应该进行审查以查看是否可以将其拆分为不同的操作。

ExecuteReader simply returns a reader that can read rows returned from a SQL procedure - it doesn't stop you from running arbitrary SQL on the way to providing that result set.

Doing inserts / updates / deletes and then immediately returning a result set (so from code looking like a read) is arguably a little odd (read: code smell), and should be reviewed to see if it can be split into distinct actions.

情释 2024-09-18 15:24:33

尽管两者都执行 SQL,但 ExecuteReader 预计会返回记录,而 ExecuteNonQuery 受影响的记录数。因此,两者是不同的。但在内部,它们的不同程度取决于供应商的特定实现。您可以单独使用 ExecuteReader 来执行所有数据库操作因为它刚刚工作(到目前为止),但由于它没有记录它并不是真正正确的方法< /em>.通过ExecuteNonQuery,您可以更清楚地了解您的意图。

就性能而言,我认为根本没有区别。我尝试使用 SQLiteMySqlClientSqlClientSqlServerCeVistaDb 并没有看到明显的差异有利于任何一个。他们都应该以某种方式在内部使用 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 仅在某些条件下调用。 请注意,insertupdate 很少是瓶颈(通常是 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 并没有什么坏处,并且没有任何性能差异。

Though both executes the sql, ExecuteReader is expected to return records while ExecuteNonQuery number of records affected. Both are different hence. But internally how different they are would depend on vendor specific implementation. You could use ExecuteReader alone for all your db actions because it just worked (until now) but since its not documented its not really the right approach to go for. You could be more clear about your intent with ExecuteNonQuery.

As far as performance is considered I don't think there's a difference at all. I tried with SQLite, MySqlClient, SqlClient, SqlServerCe and VistaDb and saw no noticeable difference to favour either. And they all should use ExecuteReader internally one way or other.

The essentials:

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;
}

and

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;
  }
}

As you can see MySqlClient directly calls ExecuteReader while SqlClient does only for certain conditions. Mind you inserts and updates are rarely the bottleneck (its often the selects).

As I said you wouldn't get the number of rows affected with the help of ExecuteReader, so use ExecuteNonQuery better to execute queries. A more direct replacement by ExecuteReader would be of ExecuteScalar which returns the data in first column of first row read.

The essentials:

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);
    }
}

and

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;
}

So it doesn't hurt to use ExecuteReader for ExecuteScalar and no performance difference whatsoever..

原谅过去的我 2024-09-18 15:24:33

这些方法的底层实现只是执行给定的 SQL 语句,因此您可以使用这些方法中的任何一个来运行几乎任何语句。只是最终的结果是一个读者试图返回一个期望的结果集,而另一个方法并不期望返回一个结果集。使用 ExecuteReader 运行不生成结果集的语句可能效率稍低。提供者仍然会创建一个数据读取器对象,这会增加一点成本(在大多数情况下可能可以忽略不计,除非提供者最终对服务器进行另一个往返调用)。

The underlying implementation of those methods simply executes the given SQL statement, so you can run pretty much any statement with any of those methods. It's just that the end result is that a reader tries to return an expected result set, while the other method does not expect a result set. It is probably a little less efficient to use ExecuteReader to run a statement that does not produce a result set. The provider will still create a data reader object, which would add a little bit of cost (probably negligible in most cases unless the provider ends up making another round-trip call to the server).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文