Deleting SQLCommand throwing inadequate exception - c#

I have virtual dataGridView which displays all values in table by Just-In-Time method documentation here. My view has more computed columns, so I use it only to show data and then only delete by single SQLCommand.
The problem comes when I am executing the delete command. When I execute it, it throws error
Conversion failed when converting the nvarchar value 'TextValueFromThirdColumn' to data type int.
xxx is not ID, but computed data in third column.
using(SqlConnection c = new SqlConnection(Properties.Settings.Default.ConnectionString))
{
c.Open();
SqlCommand command = new SqlCommand("delete from entryTable where ID = #id", c);
command.Parameters.AddWithValue("#id", dataGridView1.SelectedRows[0].Cells["ID"].Value.ToString());
command.ExecuteNonQuery();
}
As you can see I am building query with parameter which adds just fine, but the error thrown seems inadequate for me, because third column has nothing to do with my ID column. Thanks in advance for any advice

Okay, It was my dumb fault. This is problem with trigger on SQL server. I did not realize I made changes to database layout and trigger stopped working because it inserted values into wrong column. The error code showed what happened with trigger, not with row.

Related

Delete a record from a database and DataGridView at the same time using C#

I have a DataGridView that extracts data from the database linked to SQL Server Manangement Studio and shows it on the DataGridView.
I have a Delete button that allows a user to delete a specific record using this code:
string SQLdelete = "DELETE FROM User WHERE Id = #RowID";
SqlParameter RowParameter = new SqlParameter();
SqlCommand deleteRecord = new SqlCommand();
deleteRecord.Connection = con;
deleteRecord.CommandType = CommandType.Text;
deleteRecord.CommandText = SQLdelete;
RowParameter.ParameterName = "#RowID";
RowParameter.SqlDbType = SqlDbType.Int;
RowParameter.IsNullable = false;
RowParameter.Value = rowIndex + 1;
deleteRecord.Parameters.Add(RowParameter);
deleteRecord.Connection.Open();
deleteRecord.ExecuteNonQuery();
deleteRecord.Connection.Close();
The problem is that after deleting multiple records, I think the RowID gets mixed up and for example if I have a Data ID from 6-8 (That's their IDs that are linked to the delete query), it doesn't get deleted and sometimes when I click on the 3rd record, the 1st record gets deleted.
I don't know if I'm clear enough but if you want me to add any information I'll let you know.
I think it has to do with the DataGridView's RowID and my table's ID; they get mixed up when I delete many records.
Can someone tell me what should I add in my delete code to avoid this problem?
Thank you so much!
Here you need to delete the records using the "ID" present in the table.
Means you need to bind the "ID" to datagridview, and "ID" present in the datagridview need to pass as parameter to query, and needs to delete the record.

Insert Records into SQL Server Database using C# Data Objects

I'm trying to properly bind a Data Adapter to my SQL database to insert records submitted by a person in a c# program. I feel like I am 80% of the way, but now i've hit a hitch.
So as in the code below, I can create and bind a Data Table just fine. I've tested the delete functions and they work just fine. I am now attempting to have a 'save' button insert a new row to my database. The problem I have now is that a user is supposed to put in their 'notes' and then hit save. I auto populate the rest of the columns, but I do not know how to grab the notes that the user entered.
Here is my code so far:
string userVerify = User.CurrentUser.UserName.ToString();
int participantID = this.mParticipant.ParticipantID;
DateTime date = DateTime.Now;
string properRow = dtNotes[1, dtNotes.NewRowIndex - 1].Value.ToString();
sqlDataAdapter.InsertCommand = new SqlCommand("INSERT INTO xxMyDatabasexx (ParticipantID,Verifier,Notes,Date) VALUES (#participantID,#notes, #userVerify,#date);");
sqlDataAdapter.InsertCommand.Parameters.AddWithValue("#participantID", participantID);
sqlDataAdapter.InsertCommand.Parameters.AddWithValue("#userVerify", userVerify);
sqlDataAdapter.InsertCommand.Parameters.AddWithValue("#date", date);
sqlDataAdapter.InsertCommand.Parameters.AddWithValue("#notes", properRow);
sqlDataAdapter.Fill(dataTable);
sqlDataAdapter.Update(dataTable);
I am aware that the properRow variable's logic is wrong. Of course if there are no rows then the program will crash, but also if no new note has been entered it will just reproduce the last note entered which of course is wrong as well. When i look into my dataTable at the time of sqlDataAdapter.Fill, I can see the note in the correct column but I don't know how to simply save it.
Any help is appreciated. Thanks.
EDIT:
What I was unaware of is that the InsertCommand (naturally) also needs the ExecuteNonQuery command with it. I was under the assumption that since both Delete and Update did not, that Insert wouldn't either. This seemed to fix the issue. Thanks all for the help.
You can insert the record into SQL Database without need for DataAdapter just by using Command object as shown in the following code snippet (just pass your Insert SQL statement string):
void SqlExecuteCommand(string InsertSQL)
{
try
{
using (SqlConnection _connSqlCe = new SqlConnection("Conn String to SQL DB"))
{
_connSql.Open();
using (SqlCommand _commandSqlCe = new SqlCommand(CommandSQL, _connSql))
{
_commandSql.CommandType = CommandType.Text;
_commandSql.ExecuteNonQuery();
}
}
}
catch { throw; }
}
The general format of SQL INSERT query string is shown below:
INSERT INTO YourTable (column1,column2,column3,...)
VALUES (value1,value2,value3,...);
You can further extend this solution by adding parameters to the SQL String/Command in order to protect against possibility of SQL injection (see the following example):
INSERT INTO YourTable (column1,column2,column3,...)
VALUES (#param1,#param2,#param3,...);
_commandSql.Parameters.Add("#param1","abc");
_commandSql.Parameters.Add("#param2","def");
_commandSql.Parameters.Add("#param3","ghijklm");
You can also use the alternative syntax for SQL Parameters, like for e.g.:
_commandSql.Parameters.Add("#param1", SqlDbType.NChar).Value = "abc";
_commandSql.Parameters.Add("#param2", SqlDbType.NChar).Value = "def";
_commandSql.Parameters.Add("#param3", SqlDbType.NVarChar).Value = "ghijklm";
Pertinent to your particular question, it should be like:
"INSERT INTO xxMyDatabasexx (ParticipantID, Verifier, Notes, [Date]) VALUES (#participantID, #userVerify, #notes, #dt)"
_commandSql.Parameters.Add("#ParticipantID",SqlDbType.NChar).Value= participantID;
_commandSql.Parameters.Add("#userVerify",SqlDbType.NChar).Value= userVerify ;
_commandSql.Parameters.Add("#notes",SqlDbType.NVChar).Value= properRow ;
_commandSql.Parameters.Add("#dt",SqlDbType.DateTime).Value= DateTime.Now;
Note: in case ParticipantID is the IDENTITY (i.e. Autonumber) Column, then do not include it in INSERT statement.
Hope this may help.
It seems to me that You are a bit lost. The way adapters are meant to work is
fill table from database via adapter (or take empty table)
bind table to GUI or manually transfer the information to GUI
change/add/delete data in table via binding or manually
update changes (inserts/updates/deletes) into database via adapter
The changes in table are automatically traced, so the adapter knows, what should be updated/inserted/deleted and use appropriate commands.
If You use adapter just as a holder for command You can ExecuteNonQuery with arbitrary parameters, You pass the whole concept and do not need adapter at all (see #AlexBells answer).
Apart from this, are You really going to write all that plumbing code by hand? Life is too short. If I were You, I would look for some ORM. You get simple CRUDs or concurrency checking with no effort.

C# Error: Update Requires a valid updatecommand when passed datarow collection with modified rows?

I know this error/question has been posted a lot. Iv been researching for a while now and still cannot figure how to properly update my database with changes made in my DataTable.
I have a DataTable filled with information from 2 different tables in my database. Changes are only being made to the columns that come from 1 table though. The second table I need to JOIN for calculations.
So when I try to update my database, changes will only be made to the 1 table. I am still need to ado.net, so im having a hard time applying the correct way to apply these changes based on information from MSDN and StackOverflow.
This is the code I have:
using (SqlCommand cmd = new SqlCommand("SELECT INCIDENT_NO, report_dt,MIN(theStart) as The_Start, Resolved_DT, Resolution_TIME, resolution_met,response_met,max_response_time,max_resolution_time,impact FROM TICKET_REPORT JOIN TimeTracker ON Incident_NO=theTask WHERE Resolved_DT != '' GROUP BY INCIDENT_NO,report_dt,Resolved_DT,Resolution_Time, resolution_met,response_met,max_response_time,max_resolution_time,impact", sqlConn))
{
var calc = new Calculation(holidays, new OpenHours("09:00;17:00"));
SqlDataAdapter da = new SqlDataAdapter(cmd);
dtResolution.Columns.Clear();
dtResolution.Clear();
da.Fill(dtResolution);
//-------Making calculation changes to columns from one table in datatable here---------
try
{
da.Update(dtResolution);
Console.WriteLine("Update successful");
}
catch(Exception ex)
{
Console.WriteLine("Updated failed");
Console.WriteLine(ex.Message);
}
da.Dispose();
}
I leaned that CommandBuilder will create the update command for me, but MSDN says I can only use CommandBuilder if my SELECT statement only includes 1 table, I have 2 two.
Other StackOverflow answers are saying that if im using a JOIN, im on my own.
How can I properly accomplish this update? Can I do this in C#? Should I pass the data table to a stored procedure in sqlserver instead?
I found it easier to just create a merge statement inside of a stored procedure, and just pass the datatable to the stored procedure.

How to get an email from SQL Server using C#

This is bugging me. Something so simple and it does not work. What could I be doing wrong?
Here is the code I am using ...
string strSQL = "SELECT ac.ContactEmail AS fldEmail FROM vwApprenticeshipContactDetails ac WHERE (ac.ApprenticeshipID = #ApprenticeshipID)";
SqlCommand cmd2 = new SqlCommand(strSQL, cnn);
cmd2.Parameters.Add("#ApprenticeshipID", SqlDbType.Int).Value = wsAppID;
Object fldEmail = cmd2.ExecuteScalar();
Believe it or not, the above returns what looks like an array when I debug and look at the value of 'fldEmail'. In otherwords it's empty, nothing or null (cannot tell as the output window shows an array). I have run this directly in SQL server and I get the email showing fine. Could the '#' character inside the database field be causing something here?
The email I expected back is 'g.somebody#domain.com.au', which to me looks ok.
UPDATE
I replaced the ContactEmail field above, with another column name from the database and it comes back fine. Not sure why emails are an issue. The datatype by the way here is VARCHAR(50).
UPDATE
In debug mode I noticed it returns system.DBNull data type (and digging deeper it returns empty), but why? I ran the exact same query with the correct parameter in SQL server and I get a email showing. Strange
Thanks
Solved it!
For some reason (and I am a noob when it comes to email objects in C#), but an ExecuteScalar is not properly populating as it was not "read" first. What I mean by this is that when I changed ExecuteScalar to an SqlReader, it worked because I am "forcing" the read of the SQL before testing it.
So my code looks now like this and it returns an email!
strSQL = #"SELECT TOP 1 ContactEmail FROM vwApprenticeshipContactDetails WHERE ApprenticeshipID = #ApprenticeshipID";
SqlCommand cmd2 = new SqlCommand(strSQL, cnn);
cmd2.Parameters.Add("#ApprenticeshipID", SqlDbType.Int).Value = wsAppID;
using (SqlDataReader rdr = cmd2.ExecuteReader())
{
string fldEmail = "support#domain.com.au"; //this is as a default in case the sql above does not return a value
while (rdr.Read())
{
fldEmail = rdr.GetSqlValue(0).ToString();
}
}
I was avoiding SqlReader initially because I thought they were specifically designed for when reading in more than one row of data. In my case I know the parameter given only returns one row or nothing at all.
Thanks
At the first change select to top 1 for performance reason.
string strSQL = "SELECT Top 1 ac.ContactEmail AS fldEmail FROM ...
If you dont have any result in query, you will get null value as result.
You can check how many records returned by query in sql server profiler.
Also you can check your connection string to make sure are you connected to correct database.

Running SQL commands and getting message back

I'm working on a project where I wish to be able to send commands to SQL Server and also run Queries. I've succeeded in returning a query to a listbox using Joel's very good tutorial here:
creating a database query METHOD
I am now trying to adapt this to execute some commands and then run a query to check the commands worked. My query is failing because I think the commands did not work.
Currently I am sending this:
MySqlCommand("CREATE TABLE #CSVTest_Data" +
"(FirstTimeTaken DATETIME," +
"LatestTimeTaken DATETIME," +
"Market VARCHAR(50)," +
"Outcome VARCHAR(50),"+
"Odds DECIMAL(18,2)," +
"NumberOfBets INT," +
"VolumeMatched DECIMAL(18,2),"+
"InPlay TINYINT)");
Into this:
private void MySqlCommand(string sql)
{
int numberOfRecords;
//string result;
using (var connection = GetConnection())
using (var command = new SqlCommand(sql, connection))
{
connection.Open();
numberOfRecords = command.ExecuteNonQuery();
}
MessageBox.Show(numberOfRecords.ToString());
}
My understand is that ExecuteNonQuery returns an integer of the number of rows effected. My message box shows a value of -1. Running the same command in SQL Server returns 'Command(s) completed successfully.' I would appreciate if somebody could tell me whether my MySqlCommand method looks OK and how I might return the SQL Server message that is output by running the function.
In order to obtain messages that are output to the Messages tab in SQL Server Management Studio, "the console" when executing SQL statements on SQL Server, it is necessary to hook into the InfoMessage event on the SqlConnection class:
using (var connection = GetConnection())
using (var command = new SqlCommand(sql, connection))
{
connection.InfoMessage += (s, e) =>
{
Debug.WriteLine(e.Message);
};
connection.Open();
numberOfRecords = command.ExecuteNonQuery();
}
Obviously you will need to handle the event differently from what I showed above, and there are other properties on the e parameter here as well, see SqlInfoMessageEventArgs for details.
NOW having said that, bear in mind that some of the messages output to the message tab in SQL Server Management Studio is generated by that program, and does not originate from the server itself, so whether that particular message you're asking about would show up through that event I cannot say for sure.
Additionally, in this particular type of SQL Statement, the correct return value from ExecuteNonQuery is in fact -1, as is documented:
For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. For all other types of statements, the return value is -1. If a rollback occurs, the return value is also -1.
(my emphasis)
Change
var numberOfRecords = command.ExecuteNonQuery();
to
var numberOfRecords = command.ExecuteScalar();
Also, please have a look at SqlCommand Methods
You Should use ExecuteScalar.
ExecuteScalar is typically used when your query returns one value.
ExecuteNonQuery is used for SQL statements like update,insert,create etc.
So change it to
numberOfRecords = (int)command.ExecuteNonQuery();
Here is a comment from MSDN on ExecuteNonQuery:
For UPDATE, INSERT, and DELETE statements, the return value is the
number of rows affected by the command. ... For all other types of
statements, the return value is -1.
Since you are executing neither UPDATE nor INSERT nor DELETE - you are receiving -1 even though operation is successful. Basically you can assume that if no SqlException was thrown - your CREATE statement worked.
numberOfRecords=(int) command.ExecuteScalar();

Categories

Resources