I'm using the ExecuteNonQuery function and stored procedure to insert a new record in an MSSQL database.
During testing the insert of the new record is successful. But my second call to ExecuteScalar and get the newly inserted record's ID fails. The reason for the failure according to the debugger is:
ExecuteScalar requires an open and available Connection. The
connection's current state is closed.
Looking at this error it explains that the connection has been closed after the initial call to ExecuteNonQuery. Meaning that my code to get the ID won't have a valid connection to query with.
Question:
How can you retrieve ##Identity following an ExecuteNonQuery?
This is the piece of code that performs the insert in the DAL:
Database db = GetConnectionString();
string sqlCommand = "CREATE_RECORD";
string idQuery= "Select ##Identity";
int recID = 0;
using (DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand))
{
db.AddInParameter(dbCommand, "#Application", DbType.String, escalation.Application);
db.AddInParameter(dbCommand, "#UpdatedTime", DbType.DateTime, escalation.UpdatedTime);
db.ExecuteNonQuery(dbCommand);
dbCommand.CommandText = idQuery;
recID = (int)dbCommand.ExecuteScalar();
return recID ;
}
DISCLAIMER: This is a bad idea - the correct solution is server-side (server in this case is SQL Server).
You may be able to do this if you use SCOPE_IDENTITY() (which you should anyway - ##IDENTITY is not guaranteed to be your insert's identity) and execute your command as CommandType.Text instead of CommandType.StoredProcedure
WARNING: Serious security implications here, most notably SQL Injection Attack possibility:
Database db = GetConnectionString();
string sqlCommand = $"CREATE_RECORD '{escalation.Application}', '{escalation.UpdatedTime}'";
string idQuery= "Select SCOPE_IDENTITY()"
int recID = 0;
using (DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand))
{
dbCommand.CommandType = commandType.Text;
db.ExecuteNonQuery(dbCommand);
dbCommand.CommandText = idQuery;
recID = (int)dbCommand.ExecuteScalar();
return recID;
}
Of course, if you go this route, you might as well combine both commands into a single query:
Database db = GetConnectionString();
string sqlCommand = $"CREATE_RECORD '{escalation.Application}', '{escalation.UpdatedTime}'; Select SCOPE_IDENTITY()";
int recID = 0;
using (DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand))
{
dbCommand.CommandType = commandType.Text;
//TODO: Open connection using your db object
recID = (int)dbCommand.ExecuteScalar();
//TODO: Close connection using your db object
return recID;
}
Again, I stress that the correct solution is to fix this in SQL, not in C#. Use at your own risk!
You should create and open connection for each query and dispose it after query. Don't worry, there are connection pool in ADO and connection will not be physically established and closed each time. It's only a hint for ADO.NET.
int recID = 0;
string connStr = ProcThatGivesYouConnectionString();
using (SqlConnection con = new SqlConnection(connStr))
{
con.Open();
SqlCommand command = new SqlCommand("CREATE_RECORD", con);
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("#Application", escalation.Application);
command.Parameters.AddWithValue("#UpdatedTime", escalation.UpdatedTime);
command.ExecuteNonQuery();
}
using (SqlConnection con2 = new SqlConnection(connStr))
{
con2.Open();
SqlCommand command = new SqlCommand("Select ##Identity", con2);
recID = (int)command.ExecuteScalar();
}
Also you can execute both queries in one command if you want:
int recID = 0;
string connStr = ProcThatGivesYouConnectionString();
using (SqlConnection con = new SqlConnection(connStr))
{
con.Open();
SqlCommand command = new SqlCommand("
EXEC CREATE_RECORD #Application = #Application, #UpdatedTime = #UpdatedTime
SELECT ##Identity", con);
command.Parameters.AddWithValue("#Application", escalation.Application);
command.Parameters.AddWithValue("#UpdatedTime", escalation.UpdatedTime);
recID = (int)command.ExecuteScalar();
}
object r = command.ExecuteScalar();
Convert.ToInt32(r.ToString());
To prevent the ExecuteScalar gets Specified cast is not valid error , use above
Related
con = new SqlConnection (cs);
SqlCommand UpdateCommand = new SqlCommand("Update Stock Set ConfigID = #ConfigID , Quantity = #Quantity ,TotalPrice =#TotalPrice, StockDate =#StockDate ,Where StockID='" +txtStockID.Text+"'");
UpdateCommand.Parameters.Add("#ConfigID",SqlDbType.Int).Value= txtConfigID.Text;
UpdateCommand.Parameters.Add("#Quantity", SqlDbType.Int).Value = txtQty.Text;
UpdateCommand.Parameters.Add("#TotalPrice", SqlDbType.Int).Value = txtTotalPrice.Text;
UpdateCommand.Parameters.Add("#StockDate", SqlDbType.NVarChar, 50).Value = dtpStockDate.Value;
con.Open();
UpdateCommand.ExecuteNonQuery();
con.Close();
I find this error-message understandable, isn't it? However, you have to assign the connection to the SqlCommand, either by using the constructor or the property Connection:
string updateSQL = #"UPDATE Stock Set ConfigID = #ConfigID,
Quantity = #Quantity,
TotalPrice = #TotalPrice,
StockDate = #StockDate
WHERE StockID = #StockID";
SqlCommand UpdateCommand = new SqlCommand(updateSQL, con);
or
SqlCommand UpdateCommand = new SqlCommand(updateSQL);
UpdateCommand.Connection = con;
Note that i've added a parameter for the StockID in the Where and removed the last comma before the Where.
Note also that you should close connections when you're finished, therefore you can use the using-statement which ensures that it gets disposed/closed even on error:
using(var con = new SqlConnection (cs))
{
// ...
}
how to solve error of “ ExecuteNonQuery: Connection property has not been initialized?”
By initializing the Connection property
UpdateCommand.Connection = con;
UpdateCommand.ExecuteNonQuery();
Also you should wrap your connection object with a using statement to make sure it is properly disposed.
You can also use the connection to create the command:
var command = connection.CreateCommand();
This ensures you have a command initialised with the correct connection. You can set the query string using the property command.CommandText:
command.CommandText = #"SELECT foo FROM foo";
I want to display the data that is selected in the SQL query , I tried to use ExecuteScalar() but it only work with 1 value , here is my c# code :
SqlConnection conn = new SqlConnection("Data Source=MAX-PC\\SQLEXPRESS;Initial Catalog=newSchool;Integrated Security=True");
SqlCommand cmd = new SqlCommand("view_profile", conn);
cmd.CommandText = "exec view_profile #posted_id";
cmd.Parameters.AddWithValue("#posted_id", WebForm1.x);
conn.Open();
cmd.ExecuteNonQuery;
conn.Close();
and that's the proc :
CREATE PROC view_profile
#posted_in INTEGER
AS
BEGIN
SELECT P.poster , P.post_description
FROM Posts P
WHERE P.posted_in = #posted_in
END
you are a little confused:
ExecuteScalar(): Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
ExecuteNonQuery(): Executes a Transact-SQL statement against the connection and returns the number of rows affected, it is intended for UPDATE, INSERT and DELETE queries
What you need is ExecuteReader()
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = new SqlCommand(queryString, connection);
SqlDataReader reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(String.Format("{0}", reader[0]));
}
}
I have an ODBC connection to a database and I would like the user to be able to view data within any table. As this is an ASP.net application I cannot trust that the table name sent doesn't also contain nasties. I have tried using a parameterised query but I always get an error saying that I "Must declare the table variable" - this appears to be an issue because it is the table name
string sql = "SELECT TOP 10 * FROM ? ";
OdbcCommand command = new OdbcCommand(sql, dbConnection);
command.Parameters.Add(new OdbcParameter("#table", tableName));
OdbcDataAdapter adapter = new OdbcDataAdapter();
adapter.SelectCommand = command;
adapter.Fill(tableData);
What is the best method to achieve this in a secure way?
Use a stored procedure, it's the safest way.
Some hints:
You probably may also use the System.Data.SqlClient namespace objects
Enclose your connection, command and adapter objects initializations in using statements
Here's a simple example:
string sqlStoredProcedure = "SelectFromTable";
using (OdbcConnection dbConnection = new OdbcConnection(dbConnectionString))
{
dbConnection.Open();
using (OdbcCommand command = new OdbcCommand(sqlStoredProcedure, dbConnection))
{
command.CommandType = System.Data.CommandType.StoredProcedure;
command.Parameters.Add(new OdbcParameter("#table", tableName));
using (OdbcDataAdapter adapter = new OdbcDataAdapter(command))
{
adapter.SelectCommand = command;
adapter.Fill(tableData);
}
}
}
Another way to go would be to retrieve all table names and validate the tableName string variable as an entry in the list, maybe using:
DataTable tables = dbConnection.GetSchema(OdbcMetaDataCollectionNames.Tables);
Here's a simple implementation based on your scenario:
string sql = "SELECT TOP 10 * FROM {0}";
using (OdbcConnection dbConnection = new OdbcConnection(dbConnectionString))
{
dbConnection.Open();
DataTable tables = dbConnection.GetSchema(OdbcMetaDataCollectionNames.Tables);
var matches = tables.Select(String.Format("TABLE_NAME = '{0}'", tableName));
//check if table exists
if (matches.Count() > 0)
{
using (OdbcCommand command = new OdbcCommand(String.Format(sql, tableName), dbConnection))
{
using (OdbcDataAdapter adapter = new OdbcDataAdapter(command))
{
adapter.SelectCommand = command;
adapter.Fill(tableData);
}
}
}
else
{
//handle invalid value
}
}
In some programming contexts getting a scalar value from a sql query is easy:
RowCount = Connection.Execute("SELECT Count(*) FROM TableA").Fields(0).Value
In C#, given a SqlConnection variable conn that is already open, is there a simpler way to do this same thing without laboriously creating a SqlCommand, a DataReader, and all in all taking about 5 lines to do the job?
SqlCommand has an ExecuteScalar method that does what you want.
cmd.CommandText = "SELECT COUNT(*) FROM dbo.region";
Int32 count = (Int32) cmd.ExecuteScalar();
If you can use LINQ2SQL (or EntityFramework) you can simplify the actual query asking to
using (var context = new MyDbContext("connectionString"))
{
var rowCount = context.TableAs.Count();
}
If LINQ2SQL is an option that has lots of other benefits too compared to manually creating all SqlCommands, etc.
There is ExecuteScalar which saves you at least from the DataReader:
static public int AddProductCategory(string newName, string connString)
{
Int32 newProdID = 0;
string sql =
"INSERT INTO Production.ProductCategory (Name) VALUES (#Name); "
+ "SELECT CAST(scope_identity() AS int)";
using (SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add("#Name", SqlDbType.VarChar);
cmd.Parameters["#name"].Value = newName;
try
{
conn.Open();
newProdID = (Int32)cmd.ExecuteScalar();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return (int)newProdID;
}
(Example taken from this MSDN documentation article)
You do not need a DataReader. This example pulls back the scalar value:
Object result;
using (SqlConnection con = new SqlConnection(ConnectionString)) {
con.Open();
using (SqlCommand cmd = new SqlCommand(SQLStoredProcName, con)) {
result = cmd.ExecuteScalar();
}
}
Investigate Command.ExecuteScalar:
using(var connection = new SqlConnection(myConnectionString))
{
connection.Open();
using(var command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = mySql;
var result = (int)command.ExecuteScalar();
}
}
If you're feeling really lazy, encapsulate it all in an extension method, like we do.
EDIT: As requested, an extension method:
public static T ExecuteScalar<T> (this SqlConnection connection, string sql)
{
if (connection == null)
{
throw new ArgumentNullException("connection");
}
if (string.IsNullOrEmpty(sql))
{
throw new ArgumentNullException("sql");
}
using(var command = connection.CreateCommand())
{
command.CommandText = sql;
command.CommandType = CommandType.Text;
return (T)command.ExecuteScalar();
}
}
Note, this version assumes you've properly built the SQL beforehand. I'd probably create a separate overload of this extension method that took two parameters: the stored procedure name and a List. That way, you could protect yourself against unwanted SQL injection attacks.
I want to create a GUID and store it in the DB.
In C# a guid can be created using Guid.NewGuid(). This creates a 128 bit integer. SQL Server has a uniqueidentifier column which holds a huge hexidecimal number.
Is there a good/preferred way to make C# and SQL Server guids play well together? (i.e. create a guid using Guid.New() and then store it in the database using nvarchar or some other field ... or create some hexidecimal number of the form that SQL Server is expecting by some other means)
Here's a code snippet showing how to insert a GUID using a parameterised query:
using(SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using(SqlTransaction trans = conn.BeginTransaction())
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.Transaction = trans;
cmd.CommandText = #"INSERT INTO [MYTABLE] ([GuidValue]) VALUE #guidValue;";
cmd.Parameters.AddWithValue("#guidValue", Guid.NewGuid());
cmd.ExecuteNonQuery();
trans.Commit();
}
}
SQL is expecting the GUID as a string. The following in C# returns a string Sql is expecting.
"'" + Guid.NewGuid().ToString() + "'"
Something like
INSERT INTO TABLE (GuidID) VALUE ('4b5e95a7-745a-462f-ae53-709a8583700a')
is what it should look like in SQL.
You can pass a C# Guid value directly to a SQL Stored Procedure by specifying SqlDbType.UniqueIdentifier.
Your method may look like this (provided that your only parameter is the Guid):
public static void StoreGuid(Guid guid)
{
using (var cnx = new SqlConnection("YourDataBaseConnectionString"))
using (var cmd = new SqlCommand {
Connection = cnx,
CommandType = CommandType.StoredProcedure,
CommandText = "StoreGuid",
Parameters = {
new SqlParameter {
ParameterName = "#guid",
SqlDbType = SqlDbType.UniqueIdentifier, // right here
Value = guid
}
}
})
{
cnx.Open();
cmd.ExecuteNonQuery();
}
}
See also: SQL Server's uniqueidentifier
Store it in the database in a field with a data type of uniqueidentifier.
// Create Instance of Connection and Command Object
SqlConnection myConnection = new SqlConnection(GentEFONRFFConnection);
myConnection.Open();
SqlCommand myCommand = new SqlCommand("your Procedure Name", myConnection);
myCommand.CommandType = CommandType.StoredProcedure;
myCommand.Parameters.Add("#orgid", SqlDbType.UniqueIdentifier).Value = orgid;
myCommand.Parameters.Add("#statid", SqlDbType.UniqueIdentifier).Value = statid;
myCommand.Parameters.Add("#read", SqlDbType.Bit).Value = read;
myCommand.Parameters.Add("#write", SqlDbType.Bit).Value = write;
// Mark the Command as a SPROC
myCommand.ExecuteNonQuery();
myCommand.Dispose();
myConnection.Close();