Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
In my C# project I have a method that ask if an object exists in the db and if not then creates it. Now, if two users asks the same question simultaneously they both get null so the flow will be to save to db which is impossible for two duplicates to do that, so will raise an sql exception.
How can I deal with this issue please?
here is my code:
var date = DateTime.UtcNow.Date;
var todayCelebPageView = _celebPageViewsRepo.GetAll().SingleOrDefault(d => d.iCelebId == celebId && d.dDate == date);
if (todayCelebPageView != null)
{
todayCelebPageView.iScore++;
_celebPageViewsRepo.Save();
}
else
{
todayCelebPageView = new MovliCelebPageView() {dDate = date, iCelebId = celebId, iScore = 1};
_celebPageViewsRepo.Add(todayCelebPageView);
_movliRepository.DbContext.Entry(todayCelebPageView).State = System.Data.EntityState.Added;
_celebPageViewsRepo.Save();
}
Theres no easy answer to this really, it's a common problem with a number of solutions.
Some options might be:
Catch the correct SQL exception, and re-try accordingly
Create a queue for those database calls, and handle them one at a time
Some implementation of locking, either in the database (perhaps by wrapping it in a transaction) or in the code itself.
Something else to consider is what should happen from a business point of view when two attempts are made to create a record at the same time.
Should the person who created the record last win? Should the first person win and the second receive an error? Or should you write the first record and update it again with the second?
The answer to this will depend entirely on the specifics of what you are trying to do in your application.
Move the logic of the check and create to the procedure level, then it will be handled with transaction isolation:
IF NOT EXISTS (SELECT 'non-empty' FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.TABLE_NAME') AND type in (N'U'))
CREATE TABLE dbo.TABLE_NAME
But you still have to wrap your method and handle exception according the Number property of SqlException:
using (SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = new SqlCommand(queryString, connection);
try
{
command.Connection.Open();
command.ExecuteNonQuery();
}
catch (SqlException ex)
{
for (int i = 0; i < ex.Errors.Count; i++)
{
errorMessages.Append("Index #" + i + "\n" +
"Message: " + ex.Errors[i].Message + "\n" +
"LineNumber: " + ex.Errors[i].LineNumber + "\n" +
"Source: " + ex.Errors[i].Source + "\n" +
"Procedure: " + ex.Errors[i].Procedure + "\n");
}
Console.WriteLine(errorMessages.ToString());
}
}
System Error Messages
Cause and Resolution of Database Engine Errors
You should wrap the test for existence and the insert in a transaction. In that way the second call to check for existence will block while the first is completing.
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I want to update the values of textboxes into my SQL Server database.
The code does not show any syntax error and redirects easily to the page I'm redirecting but still database is not updating the new data in it.
conn.Open();
string str_id = Session["userid"].ToString();
int id = Convert.ToInt32(str_id);
id = Int32.Parse(str_id);
string updatequery = "Update empdata set fname='" + updatename.Text + "',education='" + updateeducation.Text + "',position='" + updateposition.Text + "',email='" + updateemail.Text + "',address='" + updateaddress.Text + "',contact='" + updatecontact.Text + "',account='" + updateaccount.Text + "',postal='" + updatepostal.Text + "',password = '" + updatepwd.Text + "' Where id = '" +id.ToString()+ "'";
SqlCommand updateinfo = new SqlCommand(updatequery, conn);
updateinfo.ExecuteNonQuery();
updateinfo.Dispose();
updationmessage.Text="<p style='color:green;'>Information updated successfully</p>";
Firstly,switch to ParameterBinding, your code is prone to sql inection (and slower)
Secondly, check the return value of ExecuteNonQuery. If it is 0, then there was no change in the database, meaning no matching id has been found
Thirdly, check if you are within a transaction where you need to commit the transaction - otherwise you will not see anything in the database.
I'm trying to remove a row from the database that has the same ART as is selected in the combobox. I had it working before but when I changed the database it was supposed to delete it from it stopped working and gave me a error message. I did change the database connection etc acording to the database change.
The error message (Hoping image works)
I don't know why it says "conversion failed when converting the varchar value 'R06018' to data type int" since I don't have a value of R06018 anywhere in the code, nor is it the selected row.
the code I tried after the delete stopped working, it's just the delete without any thing extra (I know it doesn't dispose but the program crashes when it tries to read, and it's just for finding the issue)
try
{
SqlCommand inkoopartdelete = new SqlCommand("delete from ART where ART=" + artnr.SelectedItem + "", Connectie.connMEVO);
drMEVO = inkoopartdelete.ExecuteReader();
MessageBox.Show(this.artnr.SelectedItem + " verwijderd.");
}
catch (Exception e) { MessageBox.Show("" + e); }
The old code after I changed the db connection (set as comment since I tried a smaller bit of code for the delete)
//SqlCommand inkoopdelete = new SqlCommand("delete from ART where ART=#art", Connectie.connMEVO_ART);
//inkoopdelete.Parameters.Add("#art", SqlDbType.VarChar).Value = artnr.SelectedItem;
//drMEVO = inkoopdelete.ExecuteReader();
//try
//{
// while (drMEVO.Read())
// { }
// MessageBox.Show(this.artnr.SelectedItem + " verwijderd.");
//}
//catch (SqlException v)
//{
// MessageBox.Show("" + v);
//}
//inkoopdelete.Dispose();
I hope any of you could help me, since I can't find the issue.
Found the issue, see accepted awnser for error in test code, real error seems to be me reading over a part of the code -_- ...srry
If the ART field is of type nvarchar then, if you really want to use string concatenations, you should enclose your string value in single quotes and write
SqlCommand inkoopartdelete = new SqlCommand(#"delete from ART
where ART='" + artnr.SelectedItem + "'", Connectie.connMEVO);
That's a valid enough reason to revert as soon as possible to use a parameterized query as you have initially. Other reasons to avoid this is the fact that if your value has an embedded single quote the Whole text becomes syntactically wrong. And, finally, string concatenation is the open door for Sql Injection Attacks
A last note. If you want to execute a query like DELETE/INSERT or UPDATE do not use ExecuteReader. It works, but it is not necessary to build an SqlDataReader for that kind of queries. Just use
int affectedRows = inkoopartdelete.ExecuteNonQuery();
Change below statement :
You have to give single quote.
SqlCommand inkoopartdelete = new SqlCommand("delete from ART where ART='" + artnr.SelectedItem + "'", Connectie.connMEVO);
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 8 years ago.
Improve this question
HI here is code snippet of C#. I am trying to generate a summary of data and display in formview in asp.net. But having a issue with this code generating error that
'Incorrect syntax near 'K12'.'
please help me out.
try
{
SqlConnection conn = new SqlConnection("server=ARSLAN- LAPI\\SQLEXPRESS;" +
"Trusted_Connection=yes;" +
"database=OTTS; " +
"connection timeout=30");
String query = "Select * FROM dbo.";
query = query + " " + "[" + session.SelectedItem.Text + "_" + dept.SelectedItem.Text + "]";
query = query + " " + "WHERE rollNo=" + "2K12-BSCS-37";
//SqlCommand cmd = new SqlCommand(query, conn);
//SqlDataReader reader;
SqlDataAdapter dataAdapter = new SqlDataAdapter(query, conn);
SqlCommandBuilder commandBuilder = new SqlCommandBuilder(dataAdapter);
DataTable table = new DataTable();
table.Locale = System.Globalization.CultureInfo.InvariantCulture;
dataAdapter.Fill(table);
dataform.DataSource = table;
dataform.Visible = true;
}
catch (SqlException ex)
{
ErrorMessage.Text="Error ::"+ ex.Message;
}
The roll number string in your where clause needs to be delimited as a string. This line query = query + " " + "WHERE rollNo=" + "2K12-BSCS-37"; should be replaced with query += " " + "WHERE rollNo=" + "'2K12-BSCS-37'"; Note the single quotes.
Better still would be to use string format to build your query, something like this:
string.Format("SELECT * FROM dbo.[{0}_{1}] WHERE rollNo = '{2}'",
session.SelectedItem.Text,
dept.SelectedItem.Text,
"2K12-BSCS-37")
And even better still would be to avoid this dangerous query altogether, since it exposes your database to numerous possible attacks. I have honestly never let users build their own table name in this fashion, so I can't even say if the SQLClient parameters would work here, though I expect they will not. I agree with previous comments that much range checking, etc. will be required to make this viable.
In the end, hopefully this is an internal application that only a select few users will ever have access to.
I have an Excel document that has about 250000 rows which takes forever to import. I have done many variations of this import, however there are a few requirements:
- Need to validate the data in each cell
- Must check if a duplicate exists in the database
- If a duplicate exists, update the entry
- If no entry exists, insert a new one
I have used parallelization as much as possible however I am sure that there must be some way to get this import to run much faster. Any assistance or ideas would be greatly appreciated.
Note that the database is on a LAN, and yes I know I haven't used parameterized sql commands (yet).
public string BulkUserInsertAndUpdate()
{
DateTime startTime = DateTime.Now;
try
{
ProcessInParallel();
Debug.WriteLine("Time taken: " + (DateTime.Now - startTime));
}
catch (Exception ex)
{
return ex.Message;
}
return "";
}
private IEnumerable<Row> ReadDocument()
{
using (SpreadsheetDocument spreadSheetDocument = SpreadsheetDocument.Open(_fileName, false))
{
WorkbookPart workbookPart = spreadSheetDocument.WorkbookPart;
Sheet ss = workbookPart.Workbook.Descendants<Sheet>().SingleOrDefault(s => s.Name == "User");
if (ss == null)
throw new Exception("There was a problem trying to import the file. Please insure that the Sheet's name is: User");
WorksheetPart worksheetPart = (WorksheetPart)workbookPart.GetPartById(ss.Id);
OpenXmlReader reader = OpenXmlReader.Create(worksheetPart);
StringTablePart = workbookPart.SharedStringTablePart;
while (reader.Read())
{
if (reader.ElementType == typeof(Row))
{
do
{
if (reader.HasAttributes)
{
var rowNum = int.Parse(reader.Attributes.First(a => a.LocalName == "r").Value);
if (rowNum == 1)
continue;
var row = (Row)reader.LoadCurrentElement();
yield return row;
}
} while (reader.ReadNextSibling()); // Skip to the next row
break; // We just looped through all the rows so no need to continue reading the worksheet
}
}
}
}
private void ProcessInParallel()
{
// Use ConcurrentQueue to enable safe enqueueing from multiple threads.
var exceptions = new ConcurrentQueue<Exception>();
Parallel.ForEach(ReadDocument(), (row, loopState) =>
{
List<Cell> cells = row.Descendants<Cell>().ToList();
if (string.IsNullOrEmpty(GetCellValue(cells[0], StringTablePart)))
return;
// validation code goes here....
try
{
using (SqlConnection connection = new SqlConnection("user id=sa;password=D3vAdm!n#;server=196.30.181.143;database=TheUnlimitedUSSD;MultipleActiveResultSets=True"))
{
connection.Open();
SqlCommand command = new SqlCommand("SELECT count(*) FROM dbo.[User] WHERE MobileNumber = '" + mobileNumber + "'", connection);
var userCount = (int) command.ExecuteScalar();
if (userCount > 0)
{
// update
command = new SqlCommand("UPDATE [user] SET NewMenu = " + (newMenuIndicator ? "1" : "0") + ", PolicyNumber = '" + policyNumber + "', Status = '" + status + "' WHERE MobileNumber = '" + mobileNumber + "'", connection);
command.ExecuteScalar();
Debug.WriteLine("Update cmd");
}
else
{
// insert
command = new SqlCommand("INSERT INTO dbo.[User] ( MobileNumber , Status , PolicyNumber , NewMenu ) VALUES ( '" + mobileNumber + "' , '" + status + "' , '" + policyNumber + "' , " + (newMenuIndicator ? "1" : "0") + " )", connection);
command.ExecuteScalar();
Debug.WriteLine("Insert cmd");
}
}
}
catch (Exception ex)
{
exceptions.Enqueue(ex);
Debug.WriteLine(ex.Message);
loopState.Break();
}
});
// Throw the exceptions here after the loop completes.
if (exceptions.Count > 0)
throw new AggregateException(exceptions);
}
I would have suggested that you do a bulk import WITHOUT any validation to an intermediary table, and only then do all the validation via SQL. Your spreadsheet's data will now be in a similiar structure as a SQL table.
This is what I have done with industrial strenght imports of 3 million rows + from Excel and CSV with great success.
Mostly I'd suggest you check that your parallelism is optimal. Since your bottlenecks are likely to be disk IO on the Excel file and IO to the Sql server, I'd suggest that it may not be. You've parallelised across those two processes (so each of them is reduced to the speed of the slowest); your parallel threads will be fighting over the database and potentially slowing eachother down. There's no point having (say) eight threads if your hard disk can't keep up with one - it just creates overhead.
Two things I'd suggest. First: take out all the parallelism and see if it's actually helping. If you single-threadedly parse the whole file into a single Queue in memory, then run the whole thing into the database, you might find it's faster.
Then, I'd try splitting it to just two threads: one to process the incoming file to the Queue, and one to take the items from the Queue and push them into the database. This way you have one thread per slow resource that you're handling - so you minimise contention - and each thread is blocked by only one resource - so you're handling that resource as optimally as possible.
This is the real trick of multithreaded programming. Throwing extra threads at a problem doesn't necessarily improve performance. What you're trying to do is minimise the time that your program is waiting idly for something external (such as disk or network IO) to complete. If one thread only waits on the Excel file, and one thread only waits on the SQL server, and what they do in between is minimal (which, in your case, it is), you'll find your code will run as fast as those external resources will allow it to.
Also, you mention it yourself, but using parameterised Sql isn't just a cool thing to point out: it will increase your performance. At the moment, you're creating a new SqlCommand for every insert, which has overhead. If you switch to a parameterised command, you can keep the same command throughout and just change the parameter values, which will save you some time. I don't think this is possible in a parallel ForEach (I doubt you can reuse the SqlCommand across threads), but it'd work fine with either of the approaches above.
Some tips for enhanced processing (as I believe this is what you need, not really a code fix).
Have Excel check for duplicate rows beforehand. It's a really decent tool for weeding out the obsolete tools. If A and B were duplicate, you'd create A then update with B's data. This way, you can weed out A and only create B.
Don't process it as an .xls(x) file, convert it to a CSV. (if you haven't already).
Create some stored procedures on your database. I generally dislike stored procedures when used in projects for simple data retrieval, but it works wonders for automated scripts that need to run efficiently. Just add a Create function (I assume the update function will be unnecessary after you've weeded out the duplicates (in tip 1)).+
Some tips I'm not sure will help your specific situation:
Use LINQ instead of creating command strings. LINQ automatically fine-tunes your queries. However, suddenly switching to LINQ is not something you can do at the blink of an eye, so you'll need to outweigh effort against how much you need it.
I know you said there is not Excel on the database server, but you can have the database process .csv files instead, there is no need for installed software for csv files. You can look into the following: http://dev.mysql.com/doc/refman/5.1/en/load-data.html
I am trying to update a mysql table while inside a c# for loop and a if statement well a few if statements. While running with a break point it will run the executenonquery once but the next loop it does not hit that. Even when i does hit the nonquery it does not change the table information.
the ffi string is the name of the column in my table and string val is what i want to put in. I know this is not the safe way to do it but I will change it when i can get it working the way it should.
Updated code it now runs the NONQUERY every time it should but still not updating the table
Code:
for (a = 0; a <= z; a++)
{
if (ds3.Tables[0].Rows[a][1].ToString() == dataGridView1.Rows[i].Cells[0].Value.ToString())
{
if (ds3.Tables[0].Rows[a][2].ToString() == dataGridView1.Rows[i].Cells[1].Value.ToString())
{
if (ds3.Tables[0].Rows[a][3].ToString() == dataGridView1.Rows[i].Cells[2].Value.ToString())
{
MessageBox.Show("We have a match " + dataGridView1.Rows[i].Cells[0].Value.ToString() + " " + dataGridView1.Rows[i].Cells[1].Value.ToString() + " " + dataGridView1.Rows[i].Cells[t].Value.ToString());
try
{
string ffi = textBox1.Text;
decimal val = decimal.Parse(dataGridView1.Rows[i].Cells[t].Value.ToString());
MySqlCommand cmd = new MySqlCommand("Update spt_results SET " + ffi + " = " + val + " where project_Id =" + dataGridView1.Rows[i].Cells[0].Value.ToString() + "",connection2);
//cmd.Connection = connection2;'
// cmd.Connection.Open();
cmd.ExecuteNonQuery();
//cmd.Connection.Close();
}
catch
{
}
The message box does show every loop and the connection2.open will run everytime
Thank you for looking and your help
The update string looks like "update spt_results SET FFI 300 = '15' where project_Id =AAA007" when it runs
Brent
Look at your code:
MySqlCommand cmd = new MySqlCommand();
cmd.CommandText = // ... snip SQL injection invitation
connection2.Open();
cmd.ExecuteNonQuery();
connection2.Close();
The MySqlCommand has no connection. You're opening and closing a connection, but it's got nothing to do with the command. I'd actually expect cmd.ExecuteNonQuery() to throw an exception because it has no connection...
Note that you should use using statements for the command and connection, to ensure that all the resources get cleaned up even in the face of an exception.
use cmd.Connection = connection2; just after connection2.Open();.
When you trying to execute the cmd.ExecuteNonQuery(), it is raising the error for no Connection bounded with the Command and error is caught in catch block. You didn't came to know because you have not doing anything in catch block for the errors.
If uncomment your code: The connection is open correctly and your code should work. But I'd suggest you to open connection once, before the loop, and close it at the end.
Another point is that you catched ALL exceptions, it is not good. The problem can be with the query, try to run "update spt_results SET FFI 300 = '15' where project_Id =AAA007" in the console or another MySQL client. It will throw an error. The field name 'FFI 300' must be quoted because it contains a white space and the value 'AAA007' must be quoted as a string literal. Try this query -
UPDATE spt_results SET `FFI 300` = '15' WHERE project_Id = 'AAA007'