I need to rapidly write code to insert/update SQL data. In classic ASP/VBScript I could do that like below:
Set rs = Server.CreateObject("adodb.Recordset")
sql = "SELECT * FROM _table WHERE id=" & id
rs.Open sql, cn, 1, 3
If rs.recordcount = 0 Then
rs.AddNew
End If
rs("data") = data
rs.Update
rs.Close
What could be the nearest C# port of this code snippet? Thanks!
Assuming MS-SQL:
using (SqlConnection conn = new SqlConnection("Connection String"))
using (SqlCommand comm = new SqlCommand("INSERT INTO MyTable (Name, Age) VALUES (#name, #age)", conn))
{
conn.Open();
comm.Parameters.AddWithValue("#name", "Adam");
comm.Parameters.AddWithValue("#age", 25);
comm.ExecuteNonQuery();
}
Connection string obviously needs fleshing out, and the parameterised SQL needs confirming, but this is the start of what you need.
You don't need to worry about closing as the using statement does this for you.
Update: not knowing classic ASP very well, I should clarify that this code only does an insert and not an update, if you need to do an update, change the SQL to use the update syntax.
Hopefully this gets you started. Make a reference to the ADODB class:
ADODB.Connection cn = new ADODB.Connection();
cn.Open("Provider=etc...", null, null, 0);
ADODB.Recordset rs = new ADODB.Recordset();
String sql = "SELECT * FROM _table WHERE id=" & id;
rs.Open(sql, cn, ADODB.CursorTypeEnum.adOpenKeyset, ADODB.LockTypeEnum.adLockOptimistic, -1);
if (rs.RecordCount == 0)
rs.AddNew;
rs("data") = data;
rs.Update();
rs.Close();
cn.Close();
Related
I am inserting a data row into my SQL Server database and then I want to query the data to get the unique identifier from the inserted row but my SqlDataReader is returning an empty dataset. I am thinking it maybe that the transaction hasn't been committed or something like that but I am not sure. I do not get an error.
Here is my code:
try
{
strQuery = "INSERT INTO clientnames VALUES(NEWID(),'" + txtACLastName.Text + "','" + txtACFirstName.Text + "'," + 1 + ")";
using (SqlCommand sqlInsertCmd = new SqlCommand(strQuery, sqlConn))
{
intQueryResult = sqlInsertCmd.ExecuteNonQuery();
if (intQueryResult == 0)
{
blnSuccess = false;
goto InsertClientNamesError;
}
else
{
blnSuccess = true;
}
sqlInsertCmd.Dispose();
}
if (blnSuccess)
{
strQuery = "select clientID from clientnames where firstname = '" + txtACFirstName.Text + "' and lastname = '" + txtACLastName.Text + "'";
using (SqlCommand sqlSelectCmd = new SqlCommand(strQuery, sqlConn))
{
SqlDataReader sqlDataRead = sqlSelectCmd.ExecuteReader();
while (sqlDataRead.Read())
{
strClientID = sqlDataRead.ToString();
}
sqlDataRead.Close();
sqlSelectCmd.Dispose();
}
}
}
catch (Exception exQuery)
{
System.Windows.MessageBox.Show("InsertClientNames: Error, " + exQuery.Message + ", has occurred.");
}
You are not getting the desired result because perhaps the SqlConnection is not opened explicitly (just a guess hard to tell without having full code). But this link shows you how to read from reader --> https://msdn.microsoft.com/en-us/library/haa3afyz(v=vs.110).aspx
But I suggest that you Please do not do it this way. Reason is you are making Two round trips to the DB Server when only one would have done the job for you IF you were using stored procedures. Also you are exposing yourselves to SQL Injection attacks as you are not parameterizing your queries.
Stored procedure:
CREATE PROCEDURE dbo.INS_clientnames
(
#FirstName varchar(100),
#LastName varchar(100),
#NewID int out
)
AS
BEGIN
Declare #Err int
set #NewID = NewID() -- Get the New ID and store it in the variable ( #NewID ) that the SP will return back to the caller
INSERT INTO clientnames values (#NewID , #FirstName , #LastName)
SET #Err = ##ERROR
IF #Error <> 0 -- Check If there was an error
Begin
SET #NewID = -1 -- Indicates that there was an error. You could log this into a Log Table with further details like error id and name.
END
RETURN
END
C# code to execute the above stored procedure and get the NewID:
using(SqlConnection conn = new SqlConnection(connectionString ))
{
using(SqlCommand cmd = new SqlCommand("dbo.INS_clientnames", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
// set up the parameters that the Stored Procedure expects
cmd.Parameters.Add("#FirstName", SqlDbType.VarChar, 100);
cmd.Parameters.Add("#LastName" , SqlDbType.VarChar, 100);
cmd.Parameters.Add("#NewId" , SqlDbType.Int).Direction = ParameterDirection.Output;
// set parameter values that your code will send to the SP as parameter values
cmd.Parameters["#FirstName"].Value = txtACFirstName.Text ;
cmd.Parameters["#LastName"].Value = txtACLastName.Text ;
// open connection and execute stored procedure
conn.Open();
cmd.ExecuteNonQuery();
// read output value from #NewId
int NewID = Convert.ToInt32(cmd.Parameters["#NewId"].Value);
}
}
Add the following line to your stored procedure that inserts the record
SELECT SCOPE_IDENTITY()
This will return the last identity value inserted in that table.
And use cmd.ExecuteScalar() instead of ExecuteNonQuery()
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. [More info][1]
I see two approaches to do this:
either you generate the new GUID on the client side in your C# code and pass it into the query - then you already know what the new id is going to be, so you don't need to do a second query to get it:
you create your GUID on the server side and return it to the caller using the OUTPUT clause in your query
Approach #1:
// define connection string and query
string connStr = "--your connection string here--";
string query = "INSERT INTO dbo.Clients(ClientID, FirstName, LastName) VALUES(#ID, #First, #Last);";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(query, conn))
{
// create the GUID in C# - this is the ID - no need to go get it again - this *IS* the id
Guid id = Guid.NewGuid();
// set the parameters
cmd.Parameters.Add("#ID", SqlDbType.UniqueIdentifier).Value = id;
cmd.Parameters.Add("#First", SqlDbType.VarChar, 50).Value = "Peter";
cmd.Parameters.Add("#Last", SqlDbType.VarChar, 50).Value = "Miller";
// open connection, execute query, close connection
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
Approach #2:
// define connection string and query
string connStr = "--your connection string here--";
// query has an "OUTPUT" clause to return a newly inserted piece of data
// back to the caller, just as if a SELECT had been issued
string query = "INSERT INTO dbo.Clients(ClientID, FirstName, LastName) OUTPUT Inserted.ClientID VALUES(NEWID(), #First, #Last);";
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(query, conn))
{
// set the parameters - note: you do *NOT* send in a GUID value - the NEWID() will create one automatically, on the server
cmd.Parameters.Add("#First", SqlDbType.VarChar, 50).Value = "Frank";
cmd.Parameters.Add("#Last", SqlDbType.VarChar, 50).Value = "Brown";
// open connection
conn.Open();
// execute query and get back one row, one column - the value in the "OUTPUT" clause
object output = cmd.ExecuteScalar();
Guid newId;
if (Guid.TryParse(output.ToString(), out newId))
{
//
}
conn.Close();
}
I have written a CLR stored procedure in C# like this
[Microsoft.SqlServer.Server.SqlProcedure]
public static void IsUserNameExists(string strUserName, out SqlBoolean returnValue)
{
using (SqlConnection connection = new SqlConnection("context connection=true"))
{
connection.Open();
SqlCommand command = new SqlCommand("Select count(UserName) from [User] where UserName='" + strUserName + "'", connection);
int nHowMany = int.Parse(command.ExecuteScalar().ToString());
if (nHowMany > 0)
returnValue = true;
else
returnValue = false;
}
}
Is it vulnerable to SQL injection? I am using SqlParameter. Any best practises?
The only correct way to prevent sql injection should be using parameterized queries.
What you are doing is not safe, since you are concatenating strings.
Look into this here for reference How do parameterized queries help against SQL injection?
For clearification, why your code is vulnerable:
In terms of SQLParameter even something like '); DROP TABLE YourTable;-- will be a valid input (since it is a string). This will then be used by you to create the inner query and there's your SQL-Injection.
Is it vulnerable to SQL injection?
It is:
SomeType.IsUserNameExists("'; insert into [User](UserName) values ('Malefactor_Username'); select '1", out returnValue);
Any best practises?
Always use parametrized queries.
CLR Stored procedure doesn’t prevent this by default. You need to do this yourself since CLR doesn’t do this automatically (I guess this was the actual questions you wanted to know)
Just update your code like this and you should be all good.
[Microsoft.SqlServer.Server.SqlProcedure]
public static void IsUserNameExists(string strUserName, out SqlBoolean returnValue)
{
using (SqlConnection connection = new SqlConnection("context connection=true"))
{
connection.Open();
SqlCommand command = new SqlCommand("Select count(UserName) from [User] where UserName=#UserName", connection);
command.Parameters.Add(new SqlParameter("#UserName", strUserName));
int nHowMany = int.Parse(command.ExecuteScalar().ToString());
if (nHowMany > 0)
returnValue = true;
else
returnValue = false;
}
}
I am using SQl CLR for parsing some table column. I want to execute the queries also in C# user defined function. Can somebody give an example to execute select and insert queries in the function?
Thank you in advance.
SqlConnection objSqlConn;
string connString = string.Empty;
connString = "Data Source=(local);Initial Catalog=DB;User ID=uname;pwd=pass;Password=pass";
objSqlConn = new SqlConnection(connString);
objSqlConn.Open();
string query = "Select count(*) FROM [DB].[dbo].[TableName]";
SqlCommand cmdTotalCount = new SqlCommand(query, objSqlConn);
cmdTotalCount.CommandTimeout = 0;
string TotalCountValue = cmdTotalCount.ExecuteScalar().ToString();
return TotalCountValue;
In CLR, you can use existing connection to run queries.
Simple, returning data to client:
var cmd = new SqlCommand("select * from [table]");
SqlContext.Pipe.ExecuteAndSend(cmd);
Returning data via SqlDataReader:
var con = new SqlConnection("context connection=true"); // using existing CLR context connection
var cmd = new SqlCommand("select * from table", con);
con.Open();
var rdr = cmd.ExecuteReader();
SqlContext.Pipe.Send(rdr);
rdr.Close();
con.Close();
Running other commands:
var con = new SqlConnection("context connection=true"); // using existing CLR context connection
var cmd = new SqlCommand("insert into [table] values ('ahoj')", con);
con.Open();
var rsa = cmd.ExecuteNonQuery();
con.Close();
Once you switch to C# you execute queries like you'd normally do from your application (using ADO.NET's SqlConnection and SqlDataReader, using LINQ to SQL or using your custom build data layer).
To connect with the database you have to mention the database username and password in the connection string of your web.config file.
I want to use a select statement to find if there is a record that already exists. I've put the code below but it throws an error at the dReader = comm.ExecuteReader(); and i'm unsure why. Any help?
string connString = "Data Source=KIMMY-MSI\\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True";
SqlDataReader dReader;
SqlConnection conn = new SqlConnection(connString);
SqlCommand comm = new SqlCommand();
comm.Connection = conn;
comm.CommandText = "SELECT * FROM Customers WHERE CustomerID == " + txtID.Text;
comm.Connection.Open();
dReader = comm.ExecuteReader();
if (dReader.HasRows == true)
{
Response.Write("Exists");
}
The error:
Invalid Column Name (whatever I input)
It seems to be looking for a column named what I input rather than looking for the actual data.
Change your == to =. That is invalid SQL as it is.
Also if txtID.Text is non-numeric then it needs to be in single quotes. You should not be constructing your SQL like this, instead use a parameter:
comm.CommandText = "SELECT * FROM Customers WHERE CustomerID = #CustomerID";
comm.Parameters.AddWithValue("CustomerID", txtID.Text);
More Info
C# using statement
SQL reference
SQL injection (why you should parameterize your queries)
It looks like your command has an issue:
SELECT * FROM Customers WHERE CustomerID == 1
In SQL you don't need to use the == operator to ensure something is equal to another.
Try:
SELECT * FROM Customers WHERE CustomerID = 1
In addition, you might want to read up about SQL Injection, the way you are binding the value is directly from a textbox value. This has a huge security hole which could lead to arbitrary sql command execution.
Change this line:
comm.CommandText = "SELECT * FROM Customers WHERE CustomerID == " + txtID.Text;
To this line:
comm.CommandText = "SELECT * FROM Customers WHERE CustomerID = #id";
comm.Parameters.AddWithValue("id", int.Parse(txtID.Text));
Assuming that your customer id is int on the database.
The equals operator in SQL is just a single =.
Also, you really shouldn't be concatenating SQL queries like that, you are just opening yourself up to SQL Injection attack. So change it to be like this:
comm.CommandText = "SELECT * FROM Customers WHERE CustomerID = #CustomerId";
comm.Parameters.AddWithValue("#CustomerId", txtID.Text);
See Stop SQL Injection Attacks Before They Stop You on MSDN.
You are using invalid SQL. You name to change "==" to "=".
You should also consider wrapping your IDisposable objects in using statements so that unmanaged objects are properly disposed of and connections are properly closed.
Finally, think about using parameters in your SQL, instead of concatenating strings, to avoid SQL injection attacks:
string connString = #"Data Source=KIMMY-MSI\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True";
string sql = "SELECT * FROM Customers WHERE CustomerID = #CustomerID";
using (SqlConnection conn = new SqlConnection(connString))
using (SqlCommand comm = new SqlCommand(sql, conn))
{
comm.Connection.Open();
comm.Parameters.AddWithValue("#CustomerID", txtID.Text);
using (SqlDataReader dReader = comm.ExecuteReader())
{
if (dReader.HasRows == true)
{
Response.Write("Exists");
}
}
}
runtime error 'there is already an open datareader associated with this command which must be closed first'
objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn);
objDataReader = objCommand.ExecuteReader();
while (objDataReader.Read())
{
objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + objDataReader[0] + "')", objConn);
objInsertCommand.ExecuteNonQuery();//Here is the error
}
objDataReader.Close();
I cannot define any stored procedure here.
Any help would we appreciated.
No need to do all that, just turn on MARS and your problem will get solved. In your connection string just add MultipleActiveResultSets=True;
You can't perform an action on that connection while it's still working on reading the contents of a data reader - the error is pretty descriptive.
Your alternatives are:
1) Retrieve all your data first, either with a DataSet or use the reader to populate some other collection, then run them all at once after the initial select is done.
2) Use a different connection for your insert statements.
How about pulling the data into a DataSet via Fill and then iterate through that to perform your insert via NonQuery?
IDbDataAdapter da;
IDbCommand selectCommand = connection.CreateCommand();
selectCommand.CommandType = CommandType.Text;
selectCommand.CommandText = "SELECT field1, field2 FROM sourcetable";
connection.Open();
DataSet selectResults= new DataSet();
da.Fill(selectResults); // get dataset
selectCommand.Dispose();
IDbCommand insertCommand;
foreach(DataRow row in selectResults.Tables[0].Rows)
{
insertCommand = connection.CreateCommand();
insertCommand.CommandType = CommandType.Text;
insertCommand.CommandText = "INSERT INTO tablename (field1, field2) VALUES (3, '" + row["columnName"].ToString() + "'";
}
insertCommand.Dispose();
connection.Close();
Your best bet would be to read the information you need into a list and then iterating the list to perform your inserts like so:
List<String> values = new List<String>();
using(SqlCommand objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn)) {
using(SqlDataReader objDataReader = objCommand.ExecuteReader()) {
while(objDataReader.Read()) {
values.Add(objDataReader[0].ToString());
}
}
}
foreach(String value in values) {
using(SqlCommand objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + value + "')", objConn)) {
objInsertCommand.ExecuteNonQuery();
}
}
INSERT INTO tablename (field1, field2)
SELECT 3, field1 FROM sourcetable
A single SQL statement instead of one per insert. Not sure if this will work for your real-life problem, but for the example you provided, this is a much better query than doing them one at a time.
On a side note, make sure your code uses parameterized queries instead of accepting strings as-is inside the SQL statement - your sample is open to SQL injection.
Several suggestions have been given which work great, along with recommendations for improving the implementation. I hit the MARS limit due to existing code not cleaning up a reader so I wanted to put together a more respectable sample:
const string connectionString = #"server=.\sqlexpress;database=adventureworkslt;integrated security=true";
const bool useMARS = false;
using (var objConn = new System.Data.SqlClient.SqlConnection(connectionString + (useMARS ? ";MultipleActiveResultSets=True" : String.Empty)))
using (var objInsertConn = useMARS ? null : new System.Data.SqlClient.SqlConnection(connectionString))
{
objConn.Open();
if (objInsertConn != null)
{
objInsertConn.Open();
}
using (var testCmd = new System.Data.SqlClient.SqlCommand())
{
testCmd.Connection = objConn;
testCmd.CommandText = #"if not exists(select 1 from information_schema.tables where table_name = 'sourcetable')
begin
create table sourcetable (field1 int, field2 varchar(5))
insert into sourcetable values (1, 'one')
create table tablename (field1 int, field2 varchar(5))
end";
testCmd.ExecuteNonQuery();
}
using (var objCommand = new System.Data.SqlClient.SqlCommand("SELECT field1, field2 FROM sourcetable", objConn))
using (var objDataReader = objCommand.ExecuteReader())
using (var objInsertCommand = new System.Data.SqlClient.SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, #field2)", objInsertConn ?? objConn))
{
objInsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("field2", String.Empty));
while (objDataReader.Read())
{
objInsertCommand.Parameters[0].Value = objDataReader[0];
objInsertCommand.ExecuteNonQuery();
}
}
}
Option 1:
Must execute query and load data before running another query.
Option 2:
Add MultipleActiveResultSets=true to the provider part of your connection string. See the example below:
<add name="DbContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
What version of SQL Server are you using? The problem might be with this:
(from http://msdn.microsoft.com/en-us/library/9kcbe65k.aspx)
When you use versions of SQL Server before SQL Server 2005, while the SqlDataReader is being used, the associated SqlConnection is busy serving the SqlDataReader. While in this state, no other operations can be performed on the SqlConnection other than closing it. This is the case until the Close method of the SqlDataReader is called.
So, if this is what's causing your problem, you should first read all the data, then close the SqlDataReader, and only after that execute your inserts.
Something like:
objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn);
objDataReader = objCommand.ExecuteReader();
List<object> values = new List<object>();
while (objDataReader.Read())
{
values.Add(objDataReader[0]);
}
objDataReader.Close();
foreach (object value in values)
{
objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + value + "')", objConn);
objInsertCommand.ExecuteNonQuery();
}
Adding this to connection string should fix the problem.
MultipleActiveResultSets=true
Try something like this:
//Add a second connection based on the first one
SqlConnection objConn2= new SqlConnection(objConn.connectionString))
SqlCommand objInsertCommand= new SqlCommand();
objInsertCommand.CommandType = CommandType.Text;
objInsertCommand.Connection = objConn2;
while (objDataReader.Read())
{
objInsertCommand.CommandText = "INSERT INTO tablename (field1, field2) VALUES (3, '" + objDataReader[0] + "')";
objInsertCommand.ExecuteNonQuery();
}
Best Solution:
There is only problem with your "CommandText" value. Let it be SP or normal Sql Query.
Check 1: The parameter value which you are passing in your Sql Query
is not changing and going same again and again in your ExecuteReader.
Check 2: Sql Query string is wrongly formed.
Check 3: Please create simplest code as follows.
string ID = "C8CA7EE2";
string myQuery = "select * from ContactBase where contactid=" + "'" + ID + "'";
string connectionString = ConfigurationManager.ConnectionStrings["CRM_SQL_CONN_UAT"].ToString();
SqlConnection con = new SqlConnection(connectionString);
con.Open();
SqlCommand cmd = new SqlCommand(myQuery, con);
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
con.Close();
In order for it to be disposed easily i use the following coding-template :
`using (SqlConnection connection = new SqlConnection("your connection string"))
{
connection.Open();
using (SqlCommand cmd = connection.CreateCommand())
{
cmd.CommandText = "Select * from SomeTable";
using (SqlDataReader reader = cmd.ExecuteReader())
{
if(reader.HasRows)
{
while(reader.Read()){
// assuming that we've a 1-column(Id) table
int id = int.Parse(reader[0].ToString());
}
}
}
}
connection.Close()
}`