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).
Related
How would I delete a row from a sql database, either with stored procedures or without, right now I have tried without, using a button press.
This is what I have so far, _memberid has been sent over from a differnt form from the database(For context).
private void btnDelete_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand();
cmd.Connection = Lib.SqlConnection;
cmd.CommandType = CommandType.Text;
cmd.CommandText = "Delete * From Members where MemberId = " + _memberId;
SqlDataAdapter adapter = new SqlDataAdapter();
adapter.DeleteCommand = cmd;
adapter.Fill(MembersDataTable); // Im fairly sure this is incorrect but i used it from old code
DialogResult = DialogResult.OK;
}
If you're trying to do a simple ADO.Net-based delete, then it would be somehting like his:
private void DeleteById(int memberId)
{
// or pull the connString from config somewhere
const string connectionString = "[your connection string]";
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var command = new SqlCommand("DELETE FROM Members WHERE MemberId = #memberId", connection))
{
command.Parameters.AddWithValue("#memberId", memberId);
command.ExecuteNonQuery();
}
}
Use parameter to prevent SQL injection.
There are essentially three main things I'm seeing...
One
You don't need the * in the query. DELETE affects the whole row, so there's no need to specify columns. So just something like:
DELETE FROM SomeTable WHERE SomeColumn = 123
Two
There's no need for a SqlDataAdapter here, all you need to do is execute the query. For example:
cmd.ExecuteNonQuery();
The "non query" is basically a SQL command which doesn't query data for results. Inserts, updates, and deletes are generally "non queries" in this context. What it would return is simply the number of rows affected, which you can use to double-check that it matches what you expect if necessary.
Three
Don't do this:
cmd.CommandText = "Delete From Members where MemberId = " + _memberId;
This kind of string concatenation leads to SQL injection. While it looks intuitively like you're using _memberId as a query value, technically you're using it as executable code. It's less likely (though not impossible) to be a problem for numeric values, but it's a huge problem for string values because it means the user can send you any string and you'll execute it as code.
Instead, use query parameters. For example, you might do something like this:
cmd.CommandText = "Delete From Members where MemberId = #memberId";
cmd.Parameters.Add("#memberId", SqlDbType.Int);
cmd.Parameters["#memberId"].Value = _memberId;
This tells the database engine itself that the value is a value and not part of the executing query, and the database engine knows how to safely handle values.
You could use a DataAdapter, but since you aren't using a datatable, it's just easier to do it without like this:
var sql = "DELETE FROM Members WHERE MemberId=#MemberId";
using(var cmd = new SqlCommand(sql, Lib.SqlConnection))
{
cmd.Connection.Open();
cmd.Parameters.Add("#MemberId",SqlDbType.Int).Value = _memberId;
cmd.ExecuteNonQuery();
}
And if you are using Dapper, you can do this:
Lib.SqlConnection.Execute("DELETE FROM Members WHERE MemberId=#MemberId", new {MemberId=_memberId});
If you are still using DataTables, I would highly recommend you look into using this (or something like this) to simplify your database accesses. It'll make CRUD logic on a database a breeze, and your code will me a lot more maintainable because you can get rid of all the odd needs to do casting, boxing/unboxing, and reduce the chances of runtime bugs because of the use of magic strings that happens so often with DataTables (column names). Once you start working with POCO classes, you'll hate having to use DataTables. That said, there are a few places where DataTables are a better solution (unknown data structures, etc), but those are usually pretty rare.
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.
I'm trying to write a method that returns a list with every row that a select query returns. But anything I can find on this is based on a single table.
This is what I'm trying:
public List<string> Select(string querystring)
{
string query = "SELECT " + querystring;
List<string> results = new List<string>();
if (this.OpenConnection())
{
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (dataReader.Read())
{
// in here I don't want a to "hard code" every column
results.Add( "returned data" );
}
dataReader.Close();
this.CloseConnection();
return results;
}
else
{
return results;
}
}
Is this possible? Do I need to make a new method for every table?
Or should I maybe return a list of objects instead of strings?
I'm really having a brainfart here so any help is appreciated.
As I stated from my comment:
I'm not sure what you're trying to do exactly... it looks like you're hoping to put a rows and columns (your data set) into just rows (your list of string). How are you planning on using this data? It will not be very useable trying to use it in the manner it looks like you're attempting. You can reference your data readers columns by index, but again I'm not clear on what you're hoping to accomplish.
You can however do it by doing something like this:
while (reader.Read())
{
// for loop with a maximum iteration of the number of columns in the reader.
for (int i=0;i<reader.FieldCount;i++)
{
results.Add(reader[i].ToString());
}
}
but again, I don't think this will get you data in a useable manner.
I would recommend you to look into Dapper.NET or EF6 or nHibernate.
I have a list Called ListTypes that holds 10 types of products. Below the store procedure loops and gets every record with the product that is looping and it stores it in the list ListIds. This is killing my sql box since I have over 200 users executing this constantly all day.
I know is not a good architecture to loop a sql statement, but this the only way I made it work. Any ideas how I can make this without looping? Maybe a Linq statement, I never used Linq with this magnitude. Thank you.
protected void GetIds(string Type, string Sub)
{
LinkedIds.Clear();
using (SqlConnection cs = new SqlConnection(connstr))
{
for (int x = 0; x < ListTypes.Count; x++)
{
cs.Open();
SqlCommand select = new SqlCommand("spUI_LinkedIds", cs);
select.CommandType = System.Data.CommandType.StoredProcedure;
select.Parameters.AddWithValue("#Type", Type);
select.Parameters.AddWithValue("#Sub", Sub);
select.Parameters.AddWithValue("#TransId", ListTypes[x]);
SqlDataReader dr = select.ExecuteReader();
while (dr.Read())
{
ListIds.Add(Convert.ToInt32(dr["LinkedId"]));
}
cs.Close();
}
}
}
Not a full answer, but this wouldn't fit in a comment. You can at least update your existing code to be more efficient like this:
protected List<int> GetIds(string Type, string Sub, IEnumerable<int> types)
{
var result = new List<int>();
using (SqlConnection cs = new SqlConnection(connstr))
using (SqlCommand select = new SqlCommand("spUI_LinkedIds", cs))
{
select.CommandType = System.Data.CommandType.StoredProcedure;
//Don't use AddWithValue! Be explicit about your DB types
// I had to guess here. Replace with the actual types from your database
select.Parameters.Add("#Type", SqlDBType.VarChar, 10).Value = Type;
select.Parameters.Add("#Sub", SqlDbType.VarChar, 10).Value = Sub;
var TransID = select.Parameters.Add("#TransId", SqlDbType.Int);
cs.Open();
foreach(int type in types)
{
TransID.Value = type;
SqlDataReader dr = select.ExecuteReader();
while (dr.Read())
{
result.Add((int)dr["LinkedId"]);
}
}
}
return result;
}
Note that this way you only open and close the connection once. Normally in ADO.Net it's better to use a new connection and re-open it for each query. The exception is in a tight loop like this. Also, the only thing that changes inside the loop this way is the one parameter value. Finally, it's better to design methods that don't rely on other class state. This method no longer needs to know about the ListTypes and ListIds class variables, which makes it possible to (among other things) do better unit testing on the method.
Again, this isn't a full answer; it's just an incremental improvement. What you really need to do is write another stored procedure that accepts a table valued parameter, and build on the query from your existing stored procedure to JOIN with the table valued parameter, so that all of this will fit into a single SQL statement. But until you share your stored procedure code, this is about as much help as I can give you.
Besides the improvements others wrote.
You could insert your ID's into a temp table and then make one
SELECT * from WhatEverTable WHERE transid in (select transid from #tempTable)
On a MSSQL this works really fast.
When you're not using a MSSQL it could be possible that one great SQL-Select with joins is faster than a SELECT IN. You have to test these cases by your own on your DBMS.
According to your comment:
The idea is lets say I have a table and I have to get all records from the table that has this 10 types of products. How can I get all of this products? But this number is dynamic.
So... why use a stored procedure at all? Why not query the table?
//If [Type] and [Sub] arguments are external inputs - as in, they come from a user request or something - they should be sanitized. (remove or escape '\' and apostrophe signs)
//create connection
string queryTmpl = "SELECT LinkedId FROM [yourTable] WHERE [TYPE] = '{0}' AND [SUB] = '{1}' AND [TRANSID] IN ({2})";
string query = string.Format(queryTmpl, Type, Sub, string.Join(", ", ListTypes);
SqlCommand select = new SqlCommand(query, cs);
//and so forth
To use Linq-to-SQL you would need to map the table to a class. This would make the query simpler to perform.
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?