C# & ADO.NET

SqlCommand.ExecuteNonQuery example in ASP.NET (C#)

C# & ADO.NET · · Published 2026-10-09

ExecuteNonQuery runs a statement that changes data, or changes the database itself, and returns the number of rows affected. Here it is used in the three situations you will meet most: a single INSERT/UPDATE/DELETE, a stored procedure with an output parameter, and several statements in one transaction.

Insert from a form

<asp:TextBox ID="txtTitle" runat="server" MaxLength="200" />
<asp:TextBox ID="txtDue" runat="server" TextMode="Date" />
<asp:Button ID="btnAdd" runat="server" Text="Add task" OnClick="btnAdd_Click" />
<asp:Label ID="lblResult" runat="server" />
protected void btnAdd_Click(object sender, EventArgs e)
{
    if (!DateTime.TryParseExact(txtDue.Text, "yyyy-MM-dd", CultureInfo.InvariantCulture,
                                DateTimeStyles.None, out DateTime due))
    {
        lblResult.Text = "Please enter a valid date.";
        return;
    }

    const string sql = "INSERT INTO dbo.Tasks (Title, DueDate, Done) VALUES (@title, @due, 0)";
    string cs = ConfigurationManager.ConnectionStrings["Db"].ConnectionString;

    using (var con = new SqlConnection(cs))
    using (var cmd = new SqlCommand(sql, con))
    {
        cmd.Parameters.Add("@title", SqlDbType.NVarChar, 200).Value = txtTitle.Text.Trim();
        cmd.Parameters.Add("@due", SqlDbType.Date).Value = due;
        con.Open();
        int rows = cmd.ExecuteNonQuery();
        lblResult.Text = rows == 1 ? "Task added." : "Nothing was inserted.";
    }
}

A TextMode="Date" box renders an HTML date input, which always posts the value as yyyy-MM-dd whatever the user's display format; parsing it with the invariant culture is therefore reliable.

Update and delete, checking the count

public static bool MarkDone(int taskId, string cs)
{
    using (var con = new SqlConnection(cs))
    using (var cmd = new SqlCommand("UPDATE dbo.Tasks SET Done = 1 WHERE TaskId = @id AND Done = 0", con))
    {
        cmd.Parameters.Add("@id", SqlDbType.Int).Value = taskId;
        con.Open();
        return cmd.ExecuteNonQuery() == 1; // false: unknown id, or already done
    }
}

public static int PurgeCompleted(DateTime before, string cs)
{
    using (var con = new SqlConnection(cs))
    using (var cmd = new SqlCommand("DELETE FROM dbo.Tasks WHERE Done = 1 AND DueDate < @before", con))
    {
        cmd.Parameters.Add("@before", SqlDbType.Date).Value = before;
        con.Open();
        return cmd.ExecuteNonQuery(); // number of tasks deleted
    }
}

The returned count turns a silent no-op into information. Remember it becomes -1 if the batch sets SET NOCOUNT ON, and that rows changed by triggers are included.

Stored procedure with an output parameter

CREATE PROCEDURE dbo.AddTask
    @Title nvarchar(200), @DueDate date, @TaskId int OUTPUT
AS
BEGIN
    SET NOCOUNT ON;
    INSERT INTO dbo.Tasks (Title, DueDate, Done) VALUES (@Title, @DueDate, 0);
    SET @TaskId = SCOPE_IDENTITY();
END
using (var con = new SqlConnection(cs))
using (var cmd = new SqlCommand("dbo.AddTask", con) { CommandType = CommandType.StoredProcedure })
{
    cmd.Parameters.Add("@Title", SqlDbType.NVarChar, 200).Value = title;
    cmd.Parameters.Add("@DueDate", SqlDbType.Date).Value = due;
    SqlParameter idParam = cmd.Parameters.Add("@TaskId", SqlDbType.Int);
    idParam.Direction = ParameterDirection.Output;

    con.Open();
    cmd.ExecuteNonQuery();          // returns -1 here because of SET NOCOUNT ON
    int newId = (int)idParam.Value; // output values are available after execution
}

Several commands in one transaction

using (var con = new SqlConnection(cs))
{
    con.Open();
    using (SqlTransaction tx = con.BeginTransaction())
    {
        try
        {
            using (var move = new SqlCommand(
                "UPDATE dbo.Tasks SET ProjectId = @to WHERE ProjectId = @from", con, tx))
            {
                move.Parameters.Add("@to", SqlDbType.Int).Value = targetProject;
                move.Parameters.Add("@from", SqlDbType.Int).Value = oldProject;
                move.ExecuteNonQuery();
            }
            using (var del = new SqlCommand("DELETE FROM dbo.Projects WHERE ProjectId = @id", con, tx))
            {
                del.Parameters.Add("@id", SqlDbType.Int).Value = oldProject;
                del.ExecuteNonQuery();
            }
            tx.Commit();
        }
        catch
        {
            tx.Rollback();
            throw;
        }
    }
}

Every command that runs on a connection with an open transaction must be given that transaction; otherwise ADO.NET throws "ExecuteNonQuery requires the command to have a transaction…". Either both changes are saved or neither is.

For reading rows or single values instead, see ExecuteReader vs ExecuteNonQuery vs ExecuteScalar.

Official documentation