I have a Table in my local database Ships(HistID,ShipName,ShipLength). I am polling the database for all ships with HistID == theme, but while(reader.Read()){} is never entered. Also, my Ships table has more than one row (saw this problem in another SO question) so I'm not sure why I cant store the results into List of Tuples. Executing the query alone in Visual Studio 2015 yields the correct results.
public List<Tuple<String, int>> getHistoricalShipList(int theme)
{
List<Tuple<String, int>> list = new List<Tuple<string, int>>();
using (db)
{
cmd = new SqlCommand(#"Select ShipName, ShipLength from Ships Where HistID=#theme", db);
cmd.Parameters.AddWithValue("#theme", theme);
db.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.HasRows) // always returning false
{
//Loop through results
while (reader.Read())
{
String shipName = reader[0].ToString();
int shipLength = Convert.ToInt32(reader[1]);
list.Add(Tuple.Create(shipName, shipLength));
}
}
db.Close();
}
return list;
}
EDIT:: removed the single quotes from the query as suggested, but still having the same issue.
Your theme is of type int, and you are enclosing it in single quotes like it is a string value. Remove the quotes, but more importantly, use Parameters
cmd = new SqlCommand(string.Format(#"Select ShipName, ShipLength from Ships Where HistID={0}", theme), db);
Never use string concatenation/string format to build SQL statements, your code is prone to SQL injection.
cmd = new SqlCommand(#"Select ShipName, ShipLength from Ships Where HistID=#theme", db);
cmd.Parameters.AddWithValue("#theme", theme);
//Or more precise
//cmd.Parameters.Add(new SqlParameter("#theme", SqlDbType.Int) {Value = theme});
The reason you are not getting any rows back is, that your field HistID is of numeric type and you are trying to compare it with a string value (by enclosing the value in single quote).
Remove HasRows check and just use the .Read while loop; there's various bug reports on HasRows not being entirely accurate in some cases.
Other than that, it's likely you're making a mistake somewhere. Either theme isn't what you expect it to be, or some environment error like hitting the wrong database.
Related
I'm writing a C# UI against a Postgres DB I do not control (and utilizing Npgsql 4.0.6 to do so); the DB was created with camelcase object names, so it's necessary to escape them with double quotes. In other programming languages, and when even defining DataSet objects in the VS GUI, this is a piece of cake.
But when I manually assign a CommandText to a NpgsqlCommand and then do something like a NpgsqlDataAdapter based on that command, any double quotes put around the column names are ignored and the columns are automatically switched to lower-case (which cases the query to fail). To add insult to injury, the Hint in the resulting exception asks if I meant to use the correctly capitalized column name, which of course I did. I've confirmed that the actual CommandText property of the command I'm running contains properly-escaped double quotes around all the columns -- but the driver seems to override or further transform this value before actually passing it to the DB.
The NpgSQL github page recommends that questions of this sort be posted to Slack Overflow for the fastest response, so I'm hoping that people have run into this one before and have a workaround that doesn't involve renaming all the objects in the DB.
I've tried escaping the double quotes in different ways, including the full table name, using table aliases, etc. In all cases, whenever I pass a SelectCommand into NpgsqlDataAdapter, any double quotes in the CommandText are just ignored when resolved by the driver. This does NOT occur if I set up a DataAdapter object in the Designer, but obviously that approach shouldn't always be necessary.
string strCommand = #"SELECT * FROM fs.""AppUsers"" WHERE ""UserID"" = #UserID;";
string strTable = "SelectedUserInfo";
using (NpgsqlConnection conn = new NpgsqlConnection(Properties.Settings.Default.PostgresConnectionString))
{
using (NpgsqlCommand cmd = new NpgsqlCommand(strCommand, conn))
{
cmd.CommandTimeout = 3000;
cmd.Parameters.Add("#UserID", NpgsqlTypes.NpgsqlDbType.Integer);
cmd.Parameters["#UserID"].Value = _UserID;
using (NpgsqlDataAdapter da = new NpgsqlDataAdapter(cmd))
{
da.Fill(ds, strTable);
DataTable dt = ds.Tables[0];
}
}
}
Results:
cmd.CommandText = "SELECT * FROM fs.\"AppUsers\" WHERE \"AppUsers\".\"UserID\" = #UserID;"
ex.Message: 42703: "column \"userid\" does not exist"
ex.Hint: "Perhaps you meant to reference the column \"AppUsers.UserID\"."
I'm using a MySQL local database, connecting to the database is not a problem (anymore). I have a small-scale database with around 6 different tables, each with around 4-6 columns, and rows <100 (not working with large data).
I am creating a WPF application that only ever needs to SELECT data from these databases, it never needs to add to them. The database is filled with static data which I will need to run SELECT statements on it and then use the results to display in my WPF app.
I need to make a function in my DBHandler class which can then be called from any other class in my system, to query the database with a specified SELECT statement, and then use the results. The problem is that my queries will vary - sometimes I might be calling for one column, such as;
(SELECT id FROM students WHERE name = 'Conor')
Sometimes I might be calling for multiple rows in a more complex statement.. such as this (pseudo):
(SELECT name, address FROM destinations WHERE long, lat intersects_with (SELECT long, lat FROM trains))
Whenever I call this function with a query, I will always be expecting the format of the data response, so if I just return a List<> or array, it should be no problem accessing the data even though the function is generic and not specific for one query or table.
So far I have tried this:
public static MySqlDataReader Query(string SQLQuery)
{
using (MySqlConnection con = new MySqlConnection(connectionString))
{
con.Open();
MySqlCommand command = new MySqlCommand(SQLQuery, con);
MySqlDataReader reader = command.ExecuteReader();
return reader;
}
}
// Some other class
MySqlDataReader reader = DBHandler.Query("SELECT * FROM destinations");
while (reader.Read())
{
MessageBox.Show(reader[0].ToString());
}
This doesn't work, because it complains the reader is closed. I presume I can't simply return a MySqlDataReader object.
My next thought process would be to do the actual query and return all the data in this Query function, and store all the results which can then be returned. But how I return the data is my main issue, because it needs to be generic for variable SELECT queries, so it can't have a fixed size for number of rows or columns returned. I thought maybe I could store it in a List<>, or a List<> within a List<>, but I'm really not sure on how to lay it out.
I know this is asking a lot but it is boggling my mind - I don't know how to make this generic SELECT function, but I know it will be really helpful as I will just need to call this whenever I need to get data in another part of the system.
Thank you!
You cannot try to use a DataReader when its connection has been closed. So, when your code exits the using block, the connection is closed as well the reader. However, you can pass to your Query method an Action delegate that receives a MySqlDataReader. This function will be defined by the caller of Query so you can customize it for your different tables while keeping a generic approach to the boilerplate code used to open, query and read the database.
public static MySqlDataReader Query(string SQLQuery, Action<MySqlDataReader> loader)
{
using (MySqlConnection con = new MySqlConnection(connectionString))
{
con.Open();
using(MySqlCommand command = new MySqlCommand(SQLQuery, con))
using(MySqlDataReader reader = command.ExecuteReader())
{
// here you can pass the reader, you are still inside the using block
while(reader.Read())
loader.Invoke(reader)
}
}
}
In the caller code you could write
List<Destination> destinations = new List<Destination>();
MySqlDataReader reader = DBHandler.Query("SELECT * FROM destinations", dataLoaderForDestination);
Console.WriteLine("Loaded " + destinations.Count + " destinations");
private void dataLoaderForDestination(MySqlDataReader reader)
{
Destination dest = new Destination();
dest.Address = reader.GetString(0);
dest.Nation = reader.GetInt32(1);
...
destinations.Add(dest);
}
Of course in a different point of your code you could pass the reference to a different Action delegate tailored for a different set of data returned by your query
List<Student> students = new List<Student>();
private void dataLoaderForStudents(MySqlDataReader reader)
{
Student st = new Student();
st.Name = reader.GetString(0);
st.Class = reader.GetInt32(1);
students.Add(st);
}
a reader is online, you need to loop inside (using connection), because if you leave the using, the connction is disposed and closed
I am connecting to an SQL Server 2012 database to query for a single value based on an ID. (It may be worth mentioning that this database is on a server on another continent from my development machine, and so latencies are quite high. Somewhere around 100ms).
The query appears to execute successfully. The HasRows property of the SqlDataReader object is set to true, so I try to use the value to assign a variable. When I run the program normally, I encounter an exception with message 'Given key was not present in the dictionary'. If I stop the execution and inspect the SqlDataReader object, and enumerate the results. Firstly I am told 'enumeration yielded no results' and then when I continue execution I get a different exception with the message 'invalid attempt to read when no data is present'
Here is the code in question:
SqlConnection sql_conn = new SqlConnection(ConnectionString);
SqlCommand sql_cmd = new SqlCommand(String.Format("select ItemType from ItemTable where ItemID='{0}'", item_id), sql_conn);
Console.WriteLine(sql_cmd.CommandText);
sql_conn.Open();
SqlDataReader rdr = sql_cmd.ExecuteReader();
rdr.Read();
if (rdr.HasRows) //True
{
item_type= TypesMap[rdr["ItemType"].ToString()]; //Either 'given key not found in dictionary' or 'invalid attempt to read when no data is present'
}
I have executed the SQL statement in SQL Server Management Studio and it is successful. I have tried hardcoding an ItemID into the statement in the C# code, and the same errors exist.
What more can I do to debug this? Everything appears to be okay, until I try to access the results of the query.
You have to debug: it seems that the TypesMap doesn't have the key read from the database:
// Wrap IDisposable into using
using (SqlConnection sql_conn = new SqlConnection(ConnectionString)) {
// Make SQL readable
// Make SQL parametrized (and not formatted) when it's possible
String sql =
#"select ItemType
from ItemTable
where ItemID = #prm_ItemId";
// Wrap IDisposable into using
using (SqlCommand sql_cmd = new SqlCommand(sql, sql_conn)) {
// I don't know ItemID's type that's why I've put AddWithValue
sql_cmd.Parameters.AddWithValue("#prm_ItemId", item_id);
// Wrap IDisposable into using
using (SqlDataReader rdr = sql_cmd.ExecuteReader()) {
// rdr.HasRows is redundant - rdr.Read() returns true if record has been read
if (rdr.Read()) {
String key = Convert.ToString(rdr.GetValue(0));
// Put break point here: what is the "key" value?
item_type = TypesMap[key];
}
}
}
}
Edit: as Luke has mentioned in the comment, the cause of the error was that key comparison is expected to be case insensitive, so the amendment is to explain .Net how to compare keys:
var TypesMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
...
TypesMap.Add("aBc", "xyz");
String test = TypesMap["Abc"]; // return "xyz"; notice "aBc" and "Abc"
As Dmitry pointed out the 'given key not found...' is not a DB thing but a dictionary thing.
Below I've added a simple check to ensure the key is in the dictionary - if it is then we can assigned to item_type.
Also, if HasRows() isn't doing what you expect, try the following. It's the standard way I read from DB:
using (SqlDataReader results = sql_cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
while (results.Read())
{
string Key = rdr["ItemType"].ToString();
if (TypesMap.ContainsKey(Key))
item_type = TypesMap[Key];
}
}
I Converted:
dto.Id = (int)record["Id"];
To:
dto.Id = (int)record[0];
This worked for me.
I have searched far and wide, but most Datareader problem/answer pairs concern getting past the first row, not returning anything, getting single values using datareader, etc.. Nothing quite like waht I'm encountering at the moment.
To be clear, this is an assignment for my evening class, albeit just a very small part of it.
the function takes size as int; the table has two colmuns: col1 and col2 of which col1 holds the index value as double and col2 holds a randomly generated double. table is an excel worksheet in a workbook, don't know if that's relevant.
table has been populated using an insert statement carried out by an ADO command object without problems.
Now, instead of supplying me with the amount of rows as specified by size/#size in the query (as it plays the double role of index/UID in this case), the datareader object fetches seemingly random amounts of rows. I say seemingly, because the number does seem to be fixed to the value of "size" (eg. size = 10 -> datareader contains 3 rows after .executeReader(); size = 2 -> datareader contains 113 rows; size = 5 -> 446 rows).
While debugging I kept track of #size parameter for the query remains 10.0
and I can't put my finger on when/why reader.Read() turns False.
I also substituted the parameter in the query string with a literal (5.0); which resulted in a type mismatch in criteria expression exception. But it's all Doubles, or am I missing something?! I'm guessing this is going to be the kicker somehow, but I'm at a loss right now.
As you can probably guess I'm pretty new at programming, so bear with me please.
What causes my code to behave the way it does?
private Double[] getSelection(int size, string table)
{
List<Double> list = new List<Double>();
Double[] toSort;
OleDbConnection connect = new OleDbConnection(cntstring);
connect.Open();
OleDbCommand command = connect.CreateCommand();
command.CommandType = CommandType.Text;
command.Parameters.Add("#size", OleDbType.Double).Value = Convert.ToDouble(size);
command.CommandText = String.Format("SELECT * FROM [{0}$] WHERE col1 < #size;", table);
try
{
OleDbDataReader reader = command.ExecuteReader();
Double outputReader;
while (reader.Read())
{
outputReader = Convert.ToDouble(reader.GetValue(1)); /for some reason (which is not my main concern at the moment) the reader.getDouble() method returned an invalid cast exception
list.Add(outputReader);
}
toSort = new double[list.Count()];
foreach (double d in list)
{
toSort[list.IndexOf(d)] = d;
}
string output = String.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}", toSort[0], toSort[1], toSort[2], toSort[3], toSort[4], toSort[5], toSort[6], toSort[7], toSort[8], toSort[9]);
//to check for values; the String.Format is where i first encountered the index out of bounds exception
MessageBox.Show(output);
reader.Close();
reader.Dispose();
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
finally
{
connect.Close();
connect.Dispose();
return toSort;
}
}
Did you try single quotes around #size in your select statement, i.e.
'#size'
I am making something that requires MySQL. I have the saving done from in-game, which is simply done by INSERT.
I have a column that will have a password in and I need to check if the inputted password matched any of the rows and then if it is, get all of the contents of the row then save it to variables.
Does anyone have an idea how to do this in C#?
//////////////////////////
I have found how to save and get the string, however it will only get 1 string at a time :(
MySql.Data.MySqlClient.MySqlCommand command = conn.CreateCommand();
command.CommandText = "SELECT * FROM (player) WHERE (pass)";
command.ExecuteNonQuery();
command.CommandType = System.Data.CommandType.Text;
MySql.Data.MySqlClient.MySqlDataReader reader = command.ExecuteReader();
reader.Read();
ayy = reader.GetString(1);
print (ayy);
if(ayy == password){
//something
}
My best practice is to use MySQLDataAdapter to fill a DataTable. You can then iterate through the rows and try to match the password.
Something like this;
DataTable dt = new DataTable();
using(MySQLDataAdapter adapter = new MySQLDataAdaper(query, connection))
{
adapter.Fill(dt);
}
foreach(DataRow row in dt.Rows)
{
//Supposing you stored your password in a stringfield in your database
if((row.Field<String>("columnName").Equals("password"))
{
//Do something with it
}
}
I hope this compiles since I typed this from my phone. You can find a nice explanation and example here.
However, if you are needing data from a specific user, why not specificly ask it from the database? Your query would be like;
SELECT * FROM usercolumn WHERE user_id = input_id AND pass = input_pass
Since I suppose every user is unique, you will now get the data from the specific user, meaning you should not have to check for passwords anymore.
For the SQL statement, you should be able to search your database as follows and get only the entry you need back from it.
"SELECT * FROM table_name WHERE column_name LIKE input_string"
If input_string contains any of the special characters for SQL string comparison (% and _, I believe) you'll just have to escape them which can be done quite simply with regex. As I said in the comments, it's been a while since I've done SQL, but there's plenty of resources online for perfecting that query.
This should then return the entire row, and if I'm thinking correctly you should be able to then put the entire row into an array of objects all at once, or simply read them string by string and convert to values as needed using one of the Convert methods, as found here: http://msdn.microsoft.com/en-us/library/system.convert(v=vs.110).aspx
Edit as per Prix's comment: Data entered into the MySQL table should not need conversion.
Example to get an integer:
string x = [...];
[...]
var y = Convert.ToInt32(x);
If you're able to get them into object arrays, that works as well.
object[] obj = [...];
[...]
var x0 = Convert.To[...](obj[0]);
var x1 = Convert.To[...](obj[1]);
Etcetera.