I have code that adds a value inside a SQL table :
SqlCommand command;
SqlDataAdapter adapter = new SqlDataAdapter();
String strSQL = "";
strSQL = "INSERT INTO tblTest (value1) VALUES ('" + strPLCData + "')";
command = new SqlCommand(strSQL, cnn);
adapter.InsertCommand = new SqlCommand(strSQL, cnn);
try
{
int rows = adapter.InsertCommand.ExecuteNonQuery();
txtStatusLogging.Text += "Inserted " + rows + " row(s) in the database." + Environment.NewLine;
}
catch (Exception ex)
{
MessageBox.Show("Failed to write to database : " + ex.Message);
}
command.Dispose();
But I'm a bit stuck when I want to add an unknown count of values in the database (according to a list of unknown size).
e.g. sometimes add only value1, other times add value1, value2 and value3 .... (depending on whats in a certain list).
How would I go about doing this?
There's absolutely no need for that SqlDataAdapter. If you want to add an arbitrary number of values to a table - use a straight INSERT SqlCommand and just loop over the list of values to insert.
Also: you should always use parametrized queries - no exceptions - and you should put your SqlConnection and SqlCommand objects in using () { ... } blocks - something like this:
Something like this:
public void InsertValues(List<int> values)
{
// define the insert query
string qryInsert = "INSERT INTO dbo.tblTest (value1) VALUES (#singleValue);";
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmdInsert = new SqlCommand(qryInsert, conn))
{
// define parameter
cmdInsert.Parameter.Add("#singleValue", SqlDbType.Int);
conn.Open();
// loop over values
foreach (int aValue in values)
{
// set the parameter value, execute query
cmdInsert.Parameters["#singleValue"].Value = aValue;
cmdInsert.ExecuteNonQuery();
}
conn.Close;
}
}
Related
Column names in each table must be unique. Column name 'Delivery' in table 'Sales' is specified more than once.
How can I solve this? I get a System.Data.SqlClient.SqlException.
class DataAccess
{
string ConnectionString;
public DataAccess()
{
ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["CafeteriaDBConnectionString"].ConnectionString;
}
public bool RecordASale(ArrayList ProductsList, DateTime SaleTime, int SalesmanID, decimal CashGiven, decimal TotalBill, decimal CashReturn, string Delivery)
{
int SaleID = ReturnSaleID();
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand("alter table [Sales] add [Delivery] varchar default 5 NOT NULL"))
{
command.Connection = connection;
command.ExecuteNonQuery();
}
}
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
connection.Open();
/*Start a local transaction*/
SqlTransaction sqlTran = connection.BeginTransaction();
/*Enlist a command in the current transaction*/
SqlCommand command = connection.CreateCommand();
command.Transaction = sqlTran;
try
{
// Execute separate commands.
command.Parameters.AddWithValue("#SaleTime", SaleTime);
command.Parameters.AddWithValue("#SalesmanID", SalesmanID);
command.Parameters.AddWithValue("#CashGiven", CashGiven);
command.Parameters.AddWithValue("#TotalBill", TotalBill);
command.Parameters.AddWithValue("#CashReturn", CashReturn);
/*command.Parameters.AddWithValue("#Delivery", Delivery);*/
command.CommandText =
"Insert into Sales (SaleTime, SalesmanID, CashGiven, TotalBill, CashReturn, Delivery) values (#SaleTime, #SalesmanID, #CashGiven, #TotalBill, #CashReturn, #Delivery)";
command.ExecuteNonQuery();
foreach (Details ProductDetail in ProductsList)
{
//// Execute separate commands.
//command.Parameters.AddWithValue("#ProductName", ProductDetail.Name);
//command.Parameters.AddWithValue("#ProductPrice", ProductDetail.Price);
//command.Parameters.AddWithValue("#ProductQuantity", ProductDetail.Quantity);
//command.Parameters.AddWithValue("#ProductTotal", ProductDetail.Total);
//command.Parameters.AddWithValue("#SaleID", SaleID);
command.CommandText =
"Insert into SaleItems (ProductName, ProductPrice, ProductQuantity, ProductTotal, SaleID) values ('" + ProductDetail.Name + "', '" + ProductDetail.Price + "', '" + ProductDetail.Quantity + "', '" + ProductDetail.Total + "', '" + SaleID + "')";
command.ExecuteNonQuery();
}
// Commit the transaction.
sqlTran.Commit();
//connection.Close();
return true;
}
catch (Exception ee)
{
throw ee;
}
}
}
If you want to check if your column exists before adding it to the datatable you should change your code in this way
string sql = #"if NOT exists(SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS
WHERE Column_Name = 'Delivery' AND Table_Name = 'Sales')
ALTER TABLE Sales ADD Delivery VARCHAR DEFAULT 5 NOT NULL";
using (SqlCommand command = new SqlCommand(sql))
{
command.Connection = connection;
command.ExecuteNonQuery();
}
In this way the operation to add the column Delivery to he table Sales will be executed only if the query on INFORMATION_SCHEMA.COLUMNS doesn't return a value
I have also noticed now that you are building your INSERT command concatenating strings together. This is a well know security problem that you can learn about searching how one can use it for Sql Injection
Also, if the SalesID column is an Identity column, you should not try to pass a value for it in the sql query
If I put "if, foreach, and else statement under comment //", the program works and Reduces book count by 1 from SQL database. But I want to check IF there is at least 1 available book to give. This code keeps showing me the message in "else" statement if I leave it like this. Help is needed fast, it's my final project, that is needed to be done before 23.07. :(
int book_qty = 0;
SqlCommand cmd2 = connection.CreateCommand();
cmd2.CommandType = CommandType.Text;
cmd2.CommandText = "SELECT * FROM Book_list WHERE BookName = '" + TextBoxBookName + "'";
cmd2.ExecuteNonQuery();
DataTable dt2 = new DataTable();
SqlDataAdapter da2 = new SqlDataAdapter(cmd2);
da2.Fill(dt2);
foreach (DataRow dr2 in dt2.Rows)
{
book_qty = Convert.ToInt32(dr2["book_qty"].ToString());
}
if (book_qty > 0)
{
SqlCommand cmd = connection.CreateCommand();
cmd.CommandType = CommandType.Text;
cmd.CommandText = "INSERT INTO Issue_book VALUES(" + TextBoxSearchMembers.Text + ",'" + TextBoxMemberName.Text + "','" + TextBoxMemberContact.Text + "','" + TextBoxMemberEmail.Text + "','" + TextBoxBookName.Text + "', '" + DateTimePicker1.Text + "')";
cmd.ExecuteNonQuery();
SqlCommand cmd1 = connection.CreateCommand();
cmd1.CommandType = CommandType.Text;
cmd1.CommandText = "UPDATE Book_list SET BookAvailability = BookAvailability-1 WHERE BookName ='" + TextBoxBookName.Text + "'";
cmd1.ExecuteNonQuery();
MessageBox.Show("successful issue");
this.Close();
else
{
MessageBox.Show("Book not available");
}
You are only checking book_qty from the last row in your result set instead of BookAvailability for all rows. You probably want to do something like:
SqlCommand cmd2 = connection.CreateCommand();
cmd2.CommandType = CommandType.Text;
cmd2.CommandText = "SELECT BookAvailability FROM Book_list WHERE BookName = '" + TextBoxBookName + "'";
var result = cmd2.ExecuteScalar();
book_qty = Convert.ToInt32(result);
You need to make sure that there is only one book with the given bookname available.
In that case just correcting this one line in your code would help as well:
book_qty = Convert.ToInt32(dr2["book_qty"].ToString());
to
book_qty = Convert.ToInt32(dr2["BookAvailability"].ToString());
Otherwise you'd need to query SUM(BookAvailability), but the following code would decrease the amount of books for multiple books at once, that wouldn't be good.
Untested code. I don't have your database. Comments and explanation in line.
private void OPCode()
{
try
{
//keep your connections close to the vest (local)
using (SqlConnection connection = new SqlConnection())
//a using block ensures that your objects are closed and disposed
//even if there is an error
{
using (SqlCommand cmd2 = new SqlCommand("SELECT BookAvailability FROM Book_list WHERE BookName = #BookName", connection))
{
//Always use parameters to protect from sql injection
//Also it is easier than fooling with the single quotes etc.
//If you are referring to a TextBox you need to provide what property is
//being accessed. I am not in a WPF right now and not sure if .Text
//is correct; may be .Content
//You need to check your database for correct data type and field size
cmd2.Parameters.Add("#BookName", SqlDbType.VarChar, 100).Value = TextBoxBookName.Text;
//A select statement is not a non-query
//You don't appear to be using the data table or data adapter
//so dump them extra objects just slow things dowm
connection.Open();
//Comment out the next 2 lines and replaced with
//Edit Update
//var returnVal = cmd2.ExecuteScalar() ?? 0;
//if ((int)returnVal > 0)
//*************************************************************
//Edit Update
//*************************************************************
//in case the query returns a null, normally an integer cannot
//hold the value of null so we use nullable types
// the (int?) casts the result of the query to Nullable of int
Nullable<int> returnVal = (int?)cmd2.ExecuteScalar();
//now we can use the .GetValueOrDefault to return the value
//if it is not null of the default value of the int (Which is 0)
int bookCount = returnVal.GetValueOrDefault();
//at this point bookCount should be a real int - no cast necessary
if (bookCount > 0)
//**************************************************************
//End Edit Update
//**************************************************************
{
using (SqlCommand cmd = new SqlCommand("INSERT INTO issue_book VALUES(#SearchMembers etc", connection))
{
//set up the parameters for this command just like the sample above
cmd.Parameters.Add("#SearchMembers", SqlDbType.VarChar, 100).Value = TextBoxSearchMembers.Text;
cmd.ExecuteNonQuery();
}
using (SqlCommand cmd1 = new SqlCommand("UPDATE Book_list SET BookAvailability = BookAvailability-1 WHERE BookName = #BoxBookName;", connection))
{
cmd1.Parameters.Add("#BoxBookName", SqlDbType.VarChar, 100);
cmd1.ExecuteNonQuery();
}
MessageBox.Show("success");
this.Close();
}
else
{
MessageBox.Show("Book not available");
}
}
}
}
catch (Exception exc)
{
MessageBox.Show(exc.ToString());
}
}
i have a button that when clicked inserts data from textbox and combobox fields into database tables, but every time i insert it gives me "Invalid attempt to call read when reader is closed". How can i get rid of this error. And tips on optimising the code are welcome, because i know im a total noob. thanks
private void btnSave_Click(object sender, RoutedEventArgs e)
{
try
{
SqlConnection sqlCon = new SqlConnection(#"Data Source=(localdb)\mssqllocaldb; Initial Catalog=Storagedb;");
sqlCon.Open();
string Query1 = "insert into location(Storage, Shelf, columns, rows) values(" + txtWarehouse.Text + ", " + txtShelf.Text + ", " + txtColumn.Text + ", " + txtRow.Text + ")";
SqlCommand sqlCmd = new SqlCommand(Query1, sqlCon);
SqlDataAdapter dataAdp = new SqlDataAdapter(sqlCmd);
dataAdp.SelectCommand.ExecuteNonQuery();
sqlCon.Close();
}
catch (Exception er)
{
MessageBox.Show(er.Message);
}
try
{
SqlConnection sqlCon = new SqlConnection(#"Data Source=(localdb)\mssqllocaldb; Initial Catalog=Storagedb;");
sqlCon.Open();
string Query3 = "SELECT LOCATION_ID FROM LOCATION WHERE storage='" + txtWarehouse.Text + "' AND shelf='" + txtShelf.Text + "' AND columns='"
+ txtColumn.Text + "' AND rows='" + txtRow.Text + "'";
SqlCommand sqlCmd1 = new SqlCommand(Query3, sqlCon);
SqlDataReader dr = sqlCmd1.ExecuteReader(); ;
while (dr.Read())
{
string LocationId = dr[0].ToString();
dr.Close();
string Query2 = "insert into product(SKU, nimetus, minimum, maximum, quantity,location_ID,category_ID,OrderMail_ID) values ('" + txtSku.Text + "','" + txtNimetus.Text + "', '"
+ txtMin.Text + "', '" + txtMax.Text + "', '" + txtQuan.Text + "', '" + LocationId + "', '" + (cbCat.SelectedIndex+1) + "', '" + (cbMail.SelectedIndex+1) + "')";
SqlCommand sqlCmd = new SqlCommand(Query2, sqlCon);
SqlDataAdapter dataAdp = new SqlDataAdapter(sqlCmd);
dataAdp.SelectCommand.ExecuteNonQuery();
}
sqlCon.Close();
}
catch (Exception ed)
{
MessageBox.Show(ed.Message);
}
}
Let's try to make some adjustments to your code.
First thing to consider is to use a parameterized query and not a
string concatenation when you build an sql command. This is mandatory
to avoid parsing errors and Sql Injections
Second, you should encapsulate the disposable objects in a using statement
to be sure they receive the proper disposal when you have finished to
use them.
Third, you can get the LOCATION_ID from your table without running a
separate query simply adding SELECT SCOPE_IDENTITY() as second batch to your first command. (This works only if you have declared the LOCATION_ID field in the first table as an IDENTITY column)
Fourth, you put everything in a transaction to avoid problems in case
some of the code fails unexpectedly
So:
SqlTransaction tr = null;
try
{
string cmdText = #"insert into location(Storage, Shelf, columns, rows)
values(#storage,#shelf,#columns,#rows);
select scope_identity()";
using(SqlConnection sqlCon = new SqlConnection(.....))
using(SqlCommand cmd = new SqlCommand(cmdText, sqlCon))
{
sqlCon.Open();
using( tr = sqlCon.BeginTransaction())
{
// Prepare all the parameters required by the command
cmd.Parameters.Add("#storage", SqlDbType.Int).Value = Convert.ToInt32(txtWarehouse.Text);
cmd.Parameters.Add("#shelf", SqlDbType.Int).Value = Convert.ToInt32(txtShelf.Text);
cmd.Parameters.Add("#columns", SqlDbType.Int).Value = Convert.ToInt32(txtColumn.Text );
cmd.Parameters.Add("#rows", SqlDbType.Int).Value = Convert.ToInt32(txtRow.Text);
// Execute the command and get back the result of SCOPE_IDENTITY
int newLocation = Convert.ToInt32(cmd.ExecuteScalar());
// Set the second command text
cmdText = #"insert into product(SKU, nimetus, minimum, maximum, quantity,location_ID,category_ID,OrderMail_ID)
values (#sku, #nimetus,#min,#max,#qty,#locid,#catid,#ordid)";
// Build a new command with the second text
using(SqlCommand cmd1 = new SqlCommand(cmdText, sqlCon))
{
// Inform the new command we are inside a transaction
cmd1.Transaction = tr;
// Add all the required parameters for the second command
cmd1.Parameters.Add("#sku", SqlDbType.NVarChar).Value = txtSku.Text;
cmd1.Parameters.Add("#nimetus",SqlDbType.NVarChar).Value = txtNimetus.Text;
cmd1.Parameters.Add("#locid", SqlDbType.Int).Value = newLocation;
.... and so on for the other parameters required
cmd1.ExecuteNonQuery();
// If we reach this point the everything is allright and
// we can commit the two inserts together
tr.Commit();
}
}
}
}
catch (Exception er)
{
// In case of exceptions do not insert anything...
if(tr != null)
tr.Rollback();
MessageBox.Show(er.Message);
}
Notice that in the first command I use parameters of type SqlDbType.Int because you haven't used single quotes around your text. This should be verified against the real data type of your table columns and adjusted to match the type. This is true as well for the second command where you put everything as text albeit some of those fields seems to be integer (_location_id_ is probably an integer). Please verify against your table.
I have a program that has 11 variable that need to be inserted into a SQL 2008 Express DB. All works until the variables that can be NULL are NULL. Then the SQL does not get the data. Here is my code and appreciate all that can help:
private void PostDatatoServer()
{
String connectionString = #"Data Source=LUCKYTIGER\SQLEXPRESS;Initial Catalog=John;Integrated Security=True";
SqlConnection con = new SqlConnection(connectionString);
con.Open();
textBox1.Text = "Connection made";
SqlCommand cmd = con.CreateCommand();
string str = "";
str += "INSERT INTO Parsed(Date, Gal, Sys, Sl, ST, PN, PlayN, Sym, Rk, All, Rel)";
str += "VALUES(#Date, #Gal, #Sys, #Sl, #ST, #PN, #PlayN, #Sym, #Rk, #All, #Rel)";
SqlCommand cmd = new SqlCommand(str, con);
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add(new SqlParameter("#Date", uegParser.strTime));
cmd.Parameters.Add(new SqlParameter("#Gal", Convert.ToInt16(uegParser.strGalaxyNum)));
cmd.Parameters.Add(new SqlParameter("#Sys", Convert.ToInt16(uegParser.strSystemNum)));
cmd.Parameters.Add(new SqlParameter("#Sl", uegParser.intSlot));
cmd.Parameters.Add(new SqlParameter("#ST", uegParser.intSlotType));
if (uegParser.strPlanetName == "")
cmd.Parameters.Add(new SqlParameter("#PN", SqlDbType.NVarChar).Value = DBNull.Value);
else
cmd.Parameters.Add(new SqlParameter("#PN", uegParser.strPlanetName));
if (uegParser.strPlayerName == "")
{
cmd.Parameters.Add(new SqlParameter("#PlayN", DBNull.Value));
TextBox2.Text = "Null player name";
}
else
{
cmd.Parameters.Add(new SqlParameter("#PlayN", uegParser.strPlayerName));
}
if (uegParser.strSymbols == "")
cmd.Parameters.Add(new SqlParameter("#Sys", DBNull.Value));
else
cmd.Parameters.Add(new SqlParameter("#Sym", uegParser.strSymbols));
if (uegParser.strRank == "")
cmd.Parameters.Add(new SqlParameter("#Rk", DBNull.Value));
else
cmd.Parameters.Add(new SqlParameter("#Rk", uegParser.strRank));
if (uegParser.strAlliance == "")
cmd.Parameters.Add(new SqlParameter("#All", DBNull.Value));
else
cmd.Parameters.Add(new SqlParameter("#All", uegParser.strAlliance));
cmd.Parameters.Add(new SqlParameter("#Rel", uegParser.intRelationship));
cmd.ExecuteNonQuery();
con.Close();
TextBox2.Text = "Connection closed";
}
The following is not an answer to your question but an example of all the places your code is abusing Ado.Net. Try to restructure any ado.net code you have in this manner. I do agree with the comments, your general approach is probably wrong however these are general pointers that you could probably benefit from in the rest of your code. Pointers are.
Always wrap SqlConnections in using blocks
Always use parameterized queries
Always specify the parameter SqlDbType (when using SqlServer obviously)
Always use the correct parameter types instead of adding string values
Refactored ado.net code
protected void btn_insert_Click(object sender, EventArgs e)
{
DataSet ds = new DataSet();
// i would not use Session unless necessary but that is out of scope for the question
// also do not forget to dispose the datatabale when finished and remove it from the session
ds = (DataSet)Session["DTset"];
// always wrap your SqlConnection in a using block
// it ensures the connection is always released
// also there is no reason to have this inside the loop
// there is no reason to close/reopen it every time
using(SqlConnection con = new SqlConnection(connStr))
{
con.Open(); // open once
for (int i = 1; i < ds.Tables[0].Rows.Count; i++)
{
// do not convert everything to strings, pick the correct type as it is in the table or convert it to the correct type if the table contains only strings
string Id = ds.Tables[0].Rows[i][0].ToString();
string Name = ds.Tables[0].Rows[i][1].ToString();
cmd = new SqlCommand("insert into tbl1(ID,Name) values (#ID,#Name)";
cmd.Parameters.AddWithValue("#ID", Id).SqlDbType = SqlDbType.; // pick the correct dbtype
cmd.Parameters.AddWithValue("#Name", Name).SqlDbType = SqlDbType.; // pick the correct dbtype
int j= cmd.ExecuteNonQuery();
// do not convert everything to strings, pick the correct type as it is in the table or convert it to the correct type if the table contains only strings
string Id1 = ds.Tables[0].Rows[i][2].ToString();
string Name1 = ds.Tables[0].Rows[i][3].ToString();
string VehicleTypeId = ds.Tables[0].Rows[i][4].ToString();
string VehicleType = ds.Tables[0].Rows[i][5].ToString();
string Capacity = ds.Tables[0].Rows[i][6].ToString();
string InsQuery = "insert into tbl2(Id,Name,Subject,status,review) values (#Id,#Name,#Subject,#status,#review)";
cmd = new SqlCommand(InsQuery,con);
cmd.Parameters.AddWithValue("#id", Id1).SqlDbType = SqlDbType.; // pick the correct dbtype
cmd.Parameters.AddWithValue("#Name", name1).SqlDbType = SqlDbType.; // pick the correct dbtype
// add the rest of your parameters here
int k= cmd.ExecuteNonQuery();
}
}
}
You should reconsider how you read your data from spreadsheet. Apparently you put the whole sheet into one big DataTable and then iterate over this. You should split your datareading, such that you only read the first two columns into one DataTable and the remaining five columns into a second DataTable. Then iterate over the two DataTables separately and save the contained rows into database.
If you really just want to prohibit to create rows with null values, you could simply check your values for null before you do the insert.
if (!String.IsNullOrEmpty(Id) && !String.IsNullOrEmpty(Name)) {
cmd = new SqlCommand( ....);
cmd.ExecuteNonQuery();
}
Additionally some hints:
Take a look at parametrized and prepared queries, they make your code a lot more secure.
You do not need to open and close your sql connection for every single command. You can open it before your loop, create and execute some commands, and close it after the loop, when your are finished.
You are missing the first row of your data. The vast majority of collections in c# start at index 0.
EDIT
For your request, I added the null checks into your code. But I really don't think you should do it this way! Like I mentionioned above, you should split your datatable into two tables, such that each of them only contains the relevant rows. And you should have a look at Igor's answer on how to create parameterized queries! And take into account the other hints from above. And finally, I don't mean to be rude, but you really should grab a good book or some tutorials from the web and learn the basics, so you will be able to understand the anwswers to your question.
protected void btn_insert_Click(object sender, EventArgs e)
{
DataSet ds = new DataSet();
ds = (DataSet)Session["DTset"];
for (int i = 1; i < ds.Tables[0].Rows.Count; i++)
{
string Id = ds.Tables[0].Rows[i][0].ToString();
string Name = ds.Tables[0].Rows[i][1].ToString();
SqlConnection con = new SqlConnection(connStr);
SqlCommand cmd;
if (!string.IsNullOrEmpty(Id) && !string.IsNullOrEmpty(Name)) {
cmd = new SqlCommand("insert into tbl1(ID,Name) values ('" + Id + "','" + Name + "')", con);
con.Open();
int j= cmd.ExecuteNonQuery();
con.Close();
}
string Id1 = ds.Tables[0].Rows[i][2].ToString();
string Name1 = ds.Tables[0].Rows[i][3].ToString();
string VehicleTypeId = ds.Tables[0].Rows[i][4].ToString();
string VehicleType = ds.Tables[0].Rows[i][5].ToString();
string Capacity = ds.Tables[0].Rows[i][6].ToString();
if (!string.IsNullOrEmpty(Id1) && !string.IsNullOrEmpty(Name1) && !string.IsNullOrEmpty(VehicleTypeId) && !string.IsNullOrEmpty(VehicleType) && !string.IsNullOrEmpty(Capacity)) {
string InsQuery = "insert into tbl2(Id,Name,Subject,status,review) values ('" + Id1 + "','" + Name1 + "','" + Subject+ "','" + status+ "','" + review+ "')";
cmd = new SqlCommand(InsQuery,con);
con.Open();
int k= cmd.ExecuteNonQuery();
con.Close();
}
}
}
Fool-proof solution: use SQL Stored procedure.
--sql
create procedure dbo.Parsed_i
#Date datetime,
#Gal int,
--so on
#PN nvarchar(100) = null --default value
--so on
as
INSERT INTO Parsed(Date, Gal, Sys, Sl, ST, PN, PlayN, Sym, Rk, All, Rel)
VALUES(#Date, #Gal, #Sys, #Sl, #ST, #PN, #PlayN, #Sym, #Rk, #All, #Rel)
//C#
//...
SqlCommand cmd = new SqlCommand("dbo.Parsed_i", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ST", SqlDbType.Int).Value = uegParser.intSlotType;
if (!string.IsNullOrEmpty(uegParser.strPlanetName))
cmd.Parameters.Add("#PN", SqlDbType.NVarChar).Value = uegParser.strPlanetName;
//note: no **else** part
//so on
I am trying to insert a record and get its newly generated id by executing two queries one by one, but don't know why its giving me the following error.
Object cannot be cast from DBNull to other types
My code is as below: (I don't want to use sql stored procedures)
SqlParameter sqlParam;
int lastInsertedVideoId = 0;
using (SqlConnection Conn = new SqlConnection(ObjUtils._ConnString))
{
Conn.Open();
using (SqlCommand sqlCmd = Conn.CreateCommand())
{
string sqlInsertValues = "#Name,#Slug";
string sqlColumnNames = "[Name],[Slug]";
string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + ");";
sqlCmd.CommandText = sqlQuery;
sqlCmd.CommandType = CommandType.Text;
sqlParam = sqlCmd.Parameters.Add("#Name", SqlDbType.VarChar);
sqlParam.Value = txtName.Text.Trim();
sqlParam = sqlCmd.Parameters.Add("#Slug", SqlDbType.VarChar);
sqlParam.Value = txtSlug.Text.Trim();
sqlCmd.ExecuteNonQuery();
//getting last inserted video id
sqlCmd.CommandText = "SELECT SCOPE_IDENTITY() AS [lastInsertedVideoId]";
using (SqlDataReader sqlDr = sqlCmd.ExecuteReader())
{
sqlDr.Read();
lastInsertedVideoId = Convert.ToInt32(sqlDr["lastInsertedVideoId"]);
}
}
}
//tags insertion into tag table
if (txtTags.Text.Trim().Length > 0 && lastInsertedVideoId > 0)
{
string sqlBulkTagInsert = "";
string[] tags = txtTags.Text.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
foreach (string tag in tags)
{
sqlBulkTagInsert += "INSERT INTO tags(VideoId, Tag) VALUES(" + lastInsertedVideoId + ", " + tag.Trim().ToLowerInvariant()+ "); ";
}
using (SqlConnection Conn = new SqlConnection(ObjUtils._ConnString))
{
Conn.Open();
using (SqlCommand sqlCmd = Conn.CreateCommand())
{
string sqlQuery = sqlBulkTagInsert;
sqlCmd.CommandText = sqlQuery;
sqlCmd.CommandType = CommandType.Text;
sqlCmd.ExecuteNonQuery();
}
}
}
And also if possible, please check is the above code coded well or we can optimize it more for improve performance?
Thanks
The call to SCOPE_IDENTITY() is not being treated as being in the same "scope" as the INSERT command that you're executing.
Essentially, what you need to do is change the line:
string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + ");";
to:
string sqlQuery = "INSERT INTO videos(" + sqlColumnNames + ") VALUES(" + sqlInsertValues + "); SELECT SCOPE_IDENTITY() AS [lastInsertedVideoId]";
and then call
int lastVideoInsertedId = Convert.ToInt32(sqlCmd.ExecuteScalar());
instead of .ExecuteNonQuery and the code block following the "//getting last inserted video id" comment.
The SCOPE_IDENTITY() should be extracted from the first command (SELECT, RETURN or OUT) and passed into the next command. By that, I mean that the SELECT_IDENTITY() should be at the end of the first command. In SQL 2008 there is additional syntax for bring values back as part of the INSERT, which makes this simpler.
Or more efficiently: combine the commands into one to avoid round-trips.