Code not synced throwing Index was out of bounds array? - c#

Before you mark this question as a duplicate, here is the tricky part I don't understand. This error is sporadic, I believe the code is correct and it's always working and I'm handling the possible mistakes with an if else condition inside the Reader part. Here is the code:
public static Tuple<int, string> GetIDAndString(string term)
{
try
{
using (SqlConnection con = GetConnection())
using (cmd = new SqlCommand())
using (myReader)
{
int ID = 0;
string status = string.Empty;
cmd.Connection = con;
con.Open();
cmd.CommandText = #"SELECT t.TableID, t.Status
FROM Table t WITH (NOLOCK) /* I know NOLOCK is not causing the mistake as far as I know */
WHERE t.Term = #term";
cmd.Parameters.AddWithValue("#term", term);
myReader = cmd.ExecuteReader();
while(myReader.Read())
{
ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0);
status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim();
}
myReader.Close();
return new Tuple<int, string>(ID, status);
}
}
catch (Exception)
{
throw;
}
}
I know I should be using a class instead of a Tuple, but I can't change that existing code and as you can see. So the main problem is that in the production server there was a Index out of bounds array exception in that method but I can't identify what's the problem.
Even if the term is not found in the query, the myReader will not enter and I'll return the ID = 0, status = string.Empty. Sometimes when I'm debugging code and working on the develpment server, my code starts to crash everywhere, showing me exceptions where is tested code and I have to reopen the solution to avoid that (I haven't found a solution to that, not even cleaning the solution).
So I hope someone have experience with something like that in a production server. I don't have specifications to the production server so I don't know anything about the server.

First you don't need the try/catch block, you don't do anything with it. After that don't share SqlDataReader in the class, this could bring problems and probably the problem comes from this. You are overwriting the value of ID and Status all the time in your while. Probably a good idea will be to call Top 1 on your query and order it by with correct field. Also there is no need to Dispose() the SqlCommand, the Constructor of SqlCommand is calling SupressFinalization().
Why this problem can happen: Imagine your query returns 1000 records with TableID and Status column and you are entering the while loop. In this moment some other user is going in your application and executing another method which overwrites the SqlDataReader and return 5 records with only one column. On the next iteration of you while loop you will receive your exception. Because of that you should never define your Readers as static for the whole class. Static variables are shared between all of application users.
public static Tuple<int, string> GetIDAndString(string term)
{
int ID = 0;
string status = string.Empty;
using (SqlConnection con = GetConnection())
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = con;
con.Open();
cmd.CommandText = #"SELECT t.TableID, t.Status
FROM Table t WITH (NOLOCK) /* I know NOLOCK is not causing the mistake as far as I know */
WHERE t.Term = #term";
cmd.Parameters.AddWithValue("#term", term);
using(SqlDataReader myReader = cmd.ExecuteReader())
{
while(myReader.Read())
{
ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0);
status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim();
}
}
}
return new Tuple<int, string>(ID, status);
}

this probably happens when you do ID = myReader.IsDBNull(0) ? 0 : myReader.GetInt32(0); or status = myReader.IsDBNull(1) ? string.Empty : myReader.GetString(1).Trim(); because the result set does not conform to your expectations. You should add logging of the reader's row before actually reading it, might help you pinpoint the issue

I guess the problem is caused by the myReader field which I suppose is static. If you look at the SqlDataReader (I suppose that's the field's type) documentation at https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader(v=vs.110).aspx, you'll find that instance methods are not thread safe, hence you must synchronize access to that field.

using (myReader) captures the value that the reader had at that time and disposes that later. It does not remember the variable. This has to be so as you can see from this example: using (Random() ? myReader : null). Clearly, the C# language will not reexecute that expression at dispose time. It runs it just once.
So you're disposing some old/other reader.
In case you are sharing objects between threads (maybe using static variables) this trivially is a race condition. Don't do that. Use locals. There is no need/advantage to use static variables here.

Related

Fatal error encountered during command execution while updating blob to mysql

String query = "";
string constr = ConfigurationSettings.AppSettings["MySQLConnectionStringForIMS"];
using (MySqlConnection con = new MySqlConnection(constr))
{
//string query = "INSERT INTO user(name, files,contentType) VALUES (#name,#files,#contentType)";
if (update == "mainSec")
{
query = "update main_section set contentType=#contentType,fileData=#fileData,fileNameAfterUploading=#fname,haveDir=#dir where id=#id";
}
else
{
query = "update sub_section set subContentType=#contentType,subFileData=#fileData,fileNameAfterUploading=#fname,haveDir=#dir where MainSecId=#id and id=#subId";
}
using (MySqlCommand cmd = new MySqlCommand(query))
{
cmd.Connection = con;
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#contentType", contentType);
cmd.Parameters.AddWithValue("#fileData", data);
cmd.Parameters.AddWithValue("#fname", filename);
cmd.Parameters.AddWithValue("#dir", 1);
cmd.Parameters.AddWithValue("#id", mainId);
if (update == "subSec")
{
cmd.Parameters.AddWithValue("#subId", subId);
}
con.Open();
int st = cmd.ExecuteNonQuery();
if (st == 1)
{
//Uri uri = new Uri(url, UriKind.Absolute);
//System.IO.File.Delete(uri.LocalPath);
}
con.Close();
}
}
We are using MySql.Data.dll version 6.9.5.0.
This fails with the error: mysql Fatal error encountered during command execution. Any ideas on why this would fail?
Tl;DR
Because of mismatched branch comparisons, you are executing a query with 6 unbound variables, but you are only binding 5 parameters.
Detail
There wasn't really sufficient information provided in the stack trace / exception to answer definitively, but it seems the guess about the bad practice in the branching above was the root cause, i.e. in these two branches:
if (update == "mainSec")
{
query = ... Query has 5 unbound variables
}
else
{
query = ... Query has 6 unbound variables
}
and
if (update == "subSec")
{
... bind the 6th parameter here
}
.. because the update type / mode string wasn't constrained to a range of mainSec or subSec, there is a branch which used the sub_section query with 6 parameter tokens, but which didn't bind the 6th token, causing the error.
In situations like this, I would recommend that instead of using weakly constrained strings, that you rigidly constrain the range of inputs of your update, e.g. with an enum:
enum UpdateMode
{
Invalid = 0, // This will be the default, and can be used to ensure assignment
MainSection,
SubSection
}
Since there's only two possible modes, you could avoid the first query assignment branch with a conditional assignment, i.e.
Contract.Assert(updateMode != UpdateMode.Invalid);
var query = updateMode == UpdateMode.MainSection
? "update main_section set contentType=#contentType ... "
: "update sub_section set subContentType=#contentType ... ";
This has the benefits that the declaration and assignment of query can be tied together (and provides additional compiler guarantees that query must be assigned).
(And if there were more than two queries (and more than two enum states) then a static IReadOnlyDictionary<enum, string> would allow this pattern to be extended.)
The binding would also change to
if (updateMode == UpdateMode.SubSection)
{
cmd.Parameters.AddWithValue("#subId", subId);
}
Some notes
con.Close(); isn't needed, since you already have a using around the new Connection - Dispose will call .Close if it's open
I know this is commented out, but I would strongly recommend against doing File IO at this point
if (st == 1)
{
// File.IO
}
Since
From a separation of concerns point of view, deleting files belongs elsewhere. If the deletion is dependent on exactly one row being updated, then this can be returned from this Blob update method.
The I/O would be in the scope of the using block, this will potentially hold up the release of a MySql Connection (to the connection pool)
The IO could fail, and depending on any transaction control, this could leave your system in a problematic state, where the record is deleted but the file is not.

ORA-01008 Not All Variables Bound on a simple query C#

I am having the hardest time trying to neutralize this oracle not all variables bound. I tried the usual suggestions I got from some Google searches, but nothing seemed to help.
Eventually, for testing purposes, I reduced my query and code to a simple
public override List<DiscountList> GetDiscountList(string name)
{
string cmdText = "select discount from users where name = :Name";
DbParameters prms = new DbParameters(Ado.AdoTemplate.DbProvider);
prms.AddWithValue("Name", name);
List<DiscountList> list = Ado.AdoTemplate.QueryWithRowMapperDelegate<DiscountList>(CommandType.Text, cmdText,
new RowMapperDelegate<DiscountList>((reader, rowNum) =>
{
DiscountList item = new DiscountList();
item.Discount = reader.GetString(0, string.Empty);
return item;
})).ToList();
return list;
}
But I still receive the Oracle error, I even hardcoded the second parameter in AddWithValue to make sure it is not passing a null issue, but still the same error.
EDIT: To include whole function
Not sure why you are using DbParameters instead of OracleParameter like
command.Parameters.Add(New OracleParameter("Name", name));
(OR)
command.Parameters.AddWithValue("Name", name);
Without a bigger code snippet, it's hard to say if what you have done should work or not. To cut to the chase, this should be a working example of what I believe you are trying to do.
Note that the AddWithValue is very convenient and will nearly always result in the proper datatype mapping. If you really want to be iron-clad explicit, you can use the overload that specifies the datatype. If you ever start passing weird datatypes like Blobs, this might become important.
OracleCommand cmd = new OracleCommand("select discount from users where name = :Name",
conn);
cmd.Parameters.Add("Name", OracleDbType.VarChar);
cmd.Parameters[0].Value = name;
OracleDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
object discount = reader.GetValue(0);
}
reader.Close();
Turns out it was a silly mistake by me and I forgot an important piece of code:
})).ToList();
should be
}),prms).ToList();
I think the query should be like this:
string cmdText = "select discount from users where name = #Name";

What is the recommended way to work with multiple (nested) SQL Server queries using C#?

I have a scenario where I query my SQL Server DB, obtain the results, and based on the results, make subsequent queries to the DB again. Following is how I've structured my code for the same:
What I'm interested in knowing is, that is this the correct way to deal with such scenarios?
Should I be doing something else alternatively? Like, make the first call to the DB, load all the results in a dictionary, then make the next calls and use the result stored in the dictionary to make these next calls
(If you feel you need context on what my code does - I want to add a uniqueness constraint and index over columns ColA, ColB, and ColC on MyTable, but I can't directly apply the uniqueness constraint. There are some existing violations over these columns. So I first resolve these violations by changing the value of ColC for the entries that cause the violation, and after fixing all violations, I add the constraint)
void Main() {
using(SqlConnection connection = new SqlConnection(#"Data Source=localhost; Initial Catalog=mydatabase; Integrated Security=True; MultipleActiveResultSets=true"))
{
connection.Open();
//Check if the index exists over Columns ColA_ColB_ColC without the uniqueness constraint
SqlCommand myCommand = new SqlCommand(#"SELECT 1 FROM sys.indexes
WHERE name = 'UQ_ColA_ColB_ColC'
AND object_id = OBJECT_ID('MyTable')
AND is_unique = 0");
SqlDataReader myReader = myCommand.ExecuteReader();
if(myReader.HasRows)
{
try {
//Get the unique values that exist (ColA,ColB,ColC) tuple
myCommand = new SqlCommand(#"select count(*) as count,
ColA,ColB,ColC
from [apimanagement.local].[dbo].[MyTable]
group by ColA,ColB,ColC ", connection);
SqlDataReader myReader = myCommand.ExecuteReader();
while (myReader.Read()) {
//For each of the unique values, get all the rows that have that value
SqlCommand myCommand2 = new SqlCommand(#"select Id,ColA,ColB,ColC from MyTable
where ColA=#ColA and ColB=#ColB and ColC=#ColC", connection);
myCommand2.Parameters.AddWithValue("#ColA", myReader["ColA"].ToString());
myCommand2.Parameters.AddWithValue("#ColB", myReader["ColB"].ToString());
myCommand2.Parameters.AddWithValue("#ColC", myReader["ColC"].ToString());
int index = 2;
SqlDataReader myReader2 = myCommand2.ExecuteReader();
myReader2.Read(); //Read the first row off the results
//If more rows exist, then we have violations for the uniqueness constraint over (ColA,ColB,ColC)
//fix these violations by appending indices to the ColC value
while (myReader2.Read()) {
SqlCommand myCommand3 = new SqlCommand(#"UPDATE MyTable
SET ColC=#NewColC
WHERE Id=#Id", connection);
myCommand3.Parameters.AddWithValue("#Id", myReader2["Id"].ToString());
myCommand3.Parameters.AddWithValue("#NewColC", myReader2["ColC"].ToString()+index);
bool changedSuccessfully = false;
while(!changedSuccessfully)
{
try
{
myCommand3.ExecuteNonQuery();
index++;
break;
}
catch(SqlException e)
{
if((uint)e.HResult == 0x80131904)
{
index++;
}
else
{
throw e;
}
}
}
}
}
//After all the violations are fixed, we create an index over (ColA,ColB,ColC) with the uniqueness constraint
myCommand = new SqlCommand(#"DROP INDEX UQ_ColA_ColB_ColC on [MyTable];
CREATE UNIQUE NONCLUSTERED INDEX [UQ_ColA_ColB_ColC] ON [MyTable]([ColA] ASC, [ColC] ASC, [ColB] ASC) WHERE [ColB] != 3");
myCommand.ExecuteNonQuery();
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
}
}
}
Well - I'd say your SqlDataReader handling is wrong.
Wrap them in a usingto avoid connection leaks:
using (SqlDataReader myReader = myCommand.ExecuteReader())
{
//do stuff with myReader here.
} //using clause will ensure dispose
//do remainder stuff outside here.
Also only have the connection open as short a time as possible. Connection pooling will limit most all of the overhead with opening/closing connections while leaving you free to not worry about. Keeping connections open too long can hinder performance.
Outside those tips - the "recommended" way to structure is extremely subjective and logic dependant.
Basically - it comes down to how much data needs to be moved around between the server and your application. Also letting the database do the work it is optimized for and letting your code do the work .NET is optimized for - meaning how much of it can/should be kept in the database and how much data should be moved to your code layer.
A lot of that comes from experience and basically trying it out and then performance tuning it to see what it does best.
Edit: Saw your comment that this is a one time thing you'll not run again. Then I wouldn't worry at all and just do it the easiest way for you now and then move on.
Because then it's rarely cost effective to fiddle about too much and time is better spend on actual problems :)

How to get multiple rows from stored procedure?

I have a method for adding values to the database for all operations.
If this is selected from the database and this select return more rows from the database,
how can I get the rows and store in an array?
This is the method code :
public void ExcuteProcedure(string procName, List<SqlParameter> procparams)
{
try
{
SqlConnection mycon = new SqlConnection(connectionString);
mycon.Open();
SqlCommand mycom = new SqlCommand();
mycom.Connection = mycon;
mycom.CommandText = procName;
mycom.CommandType = CommandType.StoredProcedure;
foreach (var item in procparams)
{
SqlParameter myparm = new SqlParameter();
myparm.ParameterName = item.ParameterName;
// myparm.SqlDbType = item.SqlDbType;
myparm.Value = item.Value;
mycom.Parameters.Add(myparm);
}
var n= mycom.ExecuteScalar();
mycon.Close();
}
catch (SqlException e)
{
Console.WriteLine("Error Number is : " + e.Number);
Console.WriteLine("Error Message is : " + e.Message);
}
}
You need to call mycom.ExecuteReader(), which will give you a SqlDataReader which can read through the results.
Call Read() to advance through the rows.
It never ceases to amaze me the number of times I see devs trying to abstract away simple database connectivity; and the myriad of ways they inevitably screw it up.
The following may sound mean, but it needs said:
Clean up your code, it leaks like a sieve. Using clauses around the connection and command objects are pretty much mandatory. As it stands if you forget a single parameter or put in a bad value you will leak connections. Once the connection pool is filled up your app will crash in all sorts of interesting, and usually hard to debug, ways.
Next, if you aren't sure how to properly get records back from a database then you probably shouldn't try to abstract the code calling your procedures. Either use a lightweight ORM like Dapper or learn how what you are doing will ultimately involve a lot of extraneous code that the next developer on your project will want to rip out.
/rant over.
Getting back to the question: ExecuteScalar returns a single value. You need to use ExecuteReader. I'd suggest that you simply take the results of the reader, stuff it into a datatable and pass that back to the calling code.
var n = mycom.ExecuteScalar();
Scalar: an atomic quantity that can hold only one value at a time
Return a DataReader instead, and iterate through its rows
Fill a DataSet by using a DataAdapter (this is more appropriate if you have multiple tables in the result set).

What am I doing wrong with this query?

I can't seem to find why this function doesn't insert records into the database. :(
I get no error messages or whatsoever, just nothing in the database.
EDIT: this is how my query looks now .. still nothing ..
connection.Open();
XmlNodeList nodeItem = rssDoc.SelectNodes("/edno23/posts/post");
foreach (XmlNode xn in nodeItem)
{
cmd.Parameters.Clear();
msgText = xn["message"].InnerText;
C = xn["user_from"].InnerText;
avatar = xn["user_from_avatar"].InnerText;
string endhash = GetMd5Sum(msgText.ToString());
cmd.Parameters.Add("#endhash",endhash);
cmd.CommandText = "Select * FROM posts Where hash=#endhash";
SqlCeDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
string msgs = reader["hash"].ToString();
if (msgs != endhash || msgs == null)
{
sql = "INSERT INTO posts([user],msg,avatar,[date],hash) VALUES(#username,#messige,#userpic,#thedate,#hash)";
cmd.CommandText = sql;
cmd.Parameters.Add("#username", C);
cmd.Parameters.Add("#messige", msgText.ToString());
cmd.Parameters.Add("#userpic", avatar.ToString());
cmd.Parameters.Add("#thedate", dt);
cmd.Parameters.Add("#hash", endhash);
cmd.ExecuteNonQuery();// executes query
adapter.Update(data);// saves the changes
}
}
reader.Close();
}
connection.Close();
Does nodeItem actually have any items in it? If not, the contents of the foreach loop aren't being executed.
What's the adapter and data being used for? The queries and updates seem be done via other commands and readers.
What does 'hash' actually contain? If it's a hash, why are you hashing the content of the hash inside the while loop? If not, why is it being compared against a hash in the query SELECT * FROM posts WHERE hash = #endhash?
Won't closing the connection before the end of the while loop invalidate the reader used to control the loop?
Lots of things going on here...
You are using the command 'cmd' to loop over records with a datareader, and then using the same 'cmd' command inside the while statement to execute an insert statement. You declared another command 'cmdAdd' before but don't seem to use it anywhere; is that what you intended to use for the insert statement?
You also close your data connection inside the while loop that iterates over your datareader. You are only going to read one record and then close the connection to your database that way; if your conditional for inserting is not met, you're not going to write anything to the database.
EDIT:
You really should open and close the connection to the database outside the foreach on the xmlnodes. If you have 10 nodes to loop over, the db connection is going to be opened and closed 10 times (well, connection pooling will probably prevent that, but still...)
You are also loading the entire 'posts' table into a dataset for seemingly no reason. You're not changing any of the values in the dataset yet you are calling an update on it repeatedly (at "save teh shanges"). If the 'posts' table is even remotely large, this is going to suck a lot of memory for no reason (on a handheld device, no less).
Is anything returned from "Select * FROM posts Where hash=#endhash"?
If not, nothing inside the while loop matters....
Why are you closing the Database Connection inside the while loop?
The code you posted should throw an exception when you try to call cmd.ExecuteNonQuery() with an unopen DB connection object.
SqlCeCommand.ExecuteNonQuery() method returns the number of rows affected.
Why don't you check whether it is returning 1 or not in the debugger as shown below?
int rowsAffectedCount = cmd.ExecuteNonQuery();
Hope it helps :-)
You've got some issues with not implementing "using" blocks. I've added some to your inner code below. The blocks for the connection and select command are more wishful thinking on my part. I hope you're doing the same with the data adapter.
using (var connection = new SqlCeConnection(connectionString))
{
connection.Open();
var nodeItem = rssDoc.SelectNodes("/edno23/posts/post");
foreach (XmlNode xn in nodeItem)
{
using (
var selectCommand =
new SqlCeCommand(
"Select * FROM posts Where hash=#endhash",
connection))
{
var msgText = xn["message"].InnerText;
var c = xn["user_from"].InnerText;
var avatar = xn["user_from_avatar"].InnerText;
var endhash = GetMd5Sum(msgText);
selectCommand.Parameters.Add("#endhash", endhash);
selectCommand.CommandText =
"Select * FROM posts Where hash=#endhash";
using (var reader = selectCommand.ExecuteReader())
{
while (reader.Read())
{
var msgs = reader["hash"].ToString();
if (msgs == endhash && msgs != null)
{
continue;
}
const string COMMAND_TEXT =
"INSERT INTO posts([user],msg,avatar,[date],hash) VALUES(#username,#messige,#userpic,#thedate,#hash)";
using (
var insertCommand =
new SqlCeCommand(
COMMAND_TEXT, connection))
{
insertCommand.Parameters.Add("#username", c);
insertCommand.Parameters.Add(
"#messige", msgText);
insertCommand.Parameters.Add(
"#userpic", avatar);
insertCommand.Parameters.Add("#thedate", dt);
insertCommand.Parameters.Add(
"#hash", endhash);
insertCommand.ExecuteNonQuery();
// executes query
}
adapter.Update(data); // saves teh changes
}
reader.Close();
}
}
}
connection.Close();
}
Of course with the additional nesting, parts should be broken out as separate methods.
I suspect your problem is that you're trying to reuse the same SqlCeCommand instances.
Try making a new SqlCeCommand within the while loop. Also, you can use the using statement to close your data objects.
Why are you calling adapter.Update(data) since you're not changing the DataSet at all? I suspect you want to call adapter.Fill(data). The Update method will save any changes in the DataSet to the database.
How to debug programs: http://www.drpaulcarter.com/cs/debug.php
Seriously, can you post some more information about where it's working? Does it work if you use SQL Server Express instead of SQL CE? If so, can you break out SQL Profiler and take a look at the SQL commands being executed?

Categories