C# ExecuteReader fails if condition - c#

Why only the else condition runs? Postalcode column is float, City column is nvarchar. I think the fail is the string may be mistake.
private void txt_st_postalcode_Leave(object sender, EventArgs e)
{
using (var connection = new SqlConnection("Data Source=mypublicip\\SQLEXPRESS2017;Initial Catalog=studentreg; User = myusername; Password=mypassword;"))
{
connection.Open();
using (var command = new SqlCommand("SELECT City FROM Cities WHERE Postcode=#Postcode", connection))
{
command.Parameters.AddWithValue("#Postcode", "10101");
using (var reader = command.ExecuteReader())
{
string txt_st_postalcode = reader.Read() ?
reader[1] as string : ("City");
if (reader.Read())
{
txt_st_city.Text = reader.GetString(reader.GetOrdinal("City"));
}
else
{
MessageBox.Show("Sh*t!");
}
}
}
}
}

Not too sure how these postcodes work (different in the UK), but given that you're using ExecuteReader you appear to be expecting multiple results. As the comments have pointed out, you're currently reading the results twice; however, you should probably have some form of loop; for example:
using (var command = new SqlCommand("SELECT City FROM Cities WHERE Postcode=#Postcode", connection))
{
command.Parameters.AddWithValue("#Postcode", "10101");
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
string txt_st_postalcode = reader[0] as string;
//txt_st_city.Text = reader.GetString(reader.GetOrdinal("City"));
// Depends what you're doing here?
}
}
}
If you only expect a single result, try using ExecuteScalar() which will return a single result; for example:
using (var command = new SqlCommand("SELECT City FROM Cities WHERE Postcode=#Postcode", connection))
{
command.Parameters.AddWithValue("#Postcode", "10101");
txt_st_city.Text = command.ExecuteScalar();
}

Firstly, if you need to read PostCode from the reader, you have to select first. So, change your query.
using (var command = new SqlCommand("SELECT City, PostCode FROM Cities WHERE Postcode=#Postcode", connection))
Secondly, calling read once per row before getting data.
if (reader.Read())
{
txt_st_postalcode .Text = reader.GetString(reader.GetOrdinal("PostCode"));
txt_st_city.Text = reader.GetString(reader.GetOrdinal("City"));
}
else
{
MessageBox.Show("Sh*t!");
}

Related

Read all record in sql table using SqlDataReader

I want to read all records from "product" table and create objects from each records.
it only gets one records from the database, any ideas might help ?
public IReadOnlyList<Product> Search(string name)
{
var result = new List<Product>();
using (var conn = new SqlConnection(connectionString))
{
if (name == null)
{
var command = new SqlCommand("SELECT * FROM Product ", conn);
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], (string)reader["Name"],
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
(string)reader["Description"]);
result.Add(prod);
reader.NextResult();
}
reader.Close();
conn.Close();
return result;
};
}
}
If you have several result sets, you should loop over them, i.e. you should put one more outer loop, e.g.
using var reader = command.ExecuteReader();
do {
while (reader.Read()) {
var prod = new Product(
Convert.ToInt32(reader["ID"]),
Convert.ToString(reader["Name"]),
Convert.ToDouble(reader["Price"]), // decimal will be better for money
Convert.ToInt32(reader["Stock"]),
Convert.ToInt32(reader["VATID"]),
Convert.ToString(reader["Description"])
);
result.Add(prod);
}
}
while (reader.NextResult());
Note outer do .. while loop since we always have at least one result set.
You use NextResult which advances the reader to the next result set. This makes sense if you have multiple sql queries and you'd use it after the while-loop. Here it's just unnecessary and wrong.
You are already advancing the reader to the next record with Read.
If I get rid of it, this error occur : Unable to cast object of type
'System.DBNull' to type 'System.String.
You can use IsDBNull:
int nameIndex = reader.GetOrdinal("Name");
string name = reader.IsDBNull(nameIndex) ? null : reader.GetString(nameIndex);
int descIndex = reader.GetOrdinal("Description");
string description = reader.IsDBNull(descIndex) ? null : reader.GetString(descIndex);
var prod = new Product((int)reader["ID"],
name,
(double)reader["Price"],
(int)reader["Stock"],
(int)reader["VATID"],
description);
Use it for every nullable column, for the numeric columns you could use nullable types like int?.
You have an error in your code:
Remove the line reader.NextResult();
NextResult is used for moving to next result set not next record.
Definitely remove the NextResult(). That does NOT move between individual records in the same query. Read() does this for you already. Rather, NextResult() allows you to include multiple queries in the same CommandText and run them all in one trip to the database.
Try this:
public IEnumerable<Product> Search(string name)
{
using (var conn = new SqlConnection(connectionString))
using (var command = new SqlCommand("SELECT * FROM Product ", conn))
{
if (!string.IsNullOrEmpty(name) )
{
command.CommandText += " WHERE Name LIKE #Name + '%'";
command.Parameters.Add("#Name", SqlDbType.NVarChar, 50).Value = name;
}
conn.Open();
using var reader = command.ExecuteReader();
{
while (reader.Read())
{
var prod = new Product((int)reader["ID"], reader["Name"].ToString(),
(double)reader["Price"], (int)reader["Stock"], (int)reader["VATID"],
reader["Description"].ToString());
yield return prod;
}
}
}
}

Refering to view values one by one

I have the following code which establishing an SQL connection inside of a project I am working on. What I want to do is to create a for loop which contains a method and every time the loop repeats the method runs with a different value until all views of the returned query are used.
I can't figure out how to refer to every value of the view without saving the view to a list or an array first. Any ideas?
SqlConnection Con = new SqlConnection(#"Data Source=localhost\**;Initial Catalog=ML;User Id=sa;Password='**'");
string sql = #"select product_id,Name from E_PRODUCT_PROPERTY";
var mylist = new List<WineRating>();
using (var command = new SqlCommand(sql, Con))
{
Con.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
new WineRating { product_id = reader.GetInt32(0), Name = reader.GetString(1) };
///Here goes the code I suppose
method_name(reader.GetInt32(0), reader.GetString(1));
}
}
public static int method_name(int product_id, string Name)
{
int num = x *2;
Console.WriteLine(num + Name);
}
Perhaps like this:
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
MyMethodToPrintToScreen(reader.GetInt32(0), reader.GetString(1));
}
}
With the method to print to screen
private static void MyMethodToPrintToScreen(int id, string product)
{
//Do whatever you wish with the data: example
Console.WriteLine($"My id: {id} | Product: {product}");
}
Edit
Let me make it even more obvious(using your exact code):
SqlConnection Con = new SqlConnection(#"Data Source=localhost\**;Initial Catalog=ML;User Id=sa;Password='**'");
string sql = #"select product_id,Name from E_PRODUCT_PROPERTY";
var mylist = new List<WineRating>();
using (var command = new SqlCommand(sql, Con))
{
Con.Open();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
method_name(reader.GetInt32(0), reader.GetString(1));
}
}
}

Check a string exists in the query response c#

I am getting a response from the select query and storing them in to the string list
string abc = "123";
List<string> numb = new List<string>();
string selectQuery = "SELECT Number FROM NumberList;";
using (MySqlConnection conn = new MySqlConnection(connStr))
using (MySqlCommand cmd = new MySqlCommand(selectQuery, conn))
{
conn.Open();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
numb.Add(reader.GetString(0));
}
I am not sure how to check the string abc exists in numb. I dont want to use for loop for this because the count of records returned from query response is high. Is there any easy of checking if the 123 exists in the list or not.
This?
if (numb.Contains(abc))
{
// List contains the string contained in variable abc
}
while (reader.Read())
{
var s = reader.GetString(0);
if (!numb.Contains(s))
{
numb.Add(s);
}
}

Using IEnumerable<IDataRecord> to return data

I am trying to return data using IEnumerable with given fields, where I am calling the the method I want to reference the data with given field name and return that.
Example, here is the function
public IEnumerable<IDataRecord> GetSomeData(string fields, string table, string where = null, int count = 0)
{
string sql = "SELECT #Fields FROM #Table WHERE #Where";
using (SqlConnection cn = new SqlConnection(db.getDBstring(Globals.booDebug)))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add("#Fields", SqlDbType.NVarChar, 255).Value = where;
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return (IDataRecord)rdr;
}
}
}
}
Calling:
IEnumerable<IDataRecord> data = bw.GetSomeData("StaffCode, Perms", "BW_Staff", "StaffCode = 'KAA'");
What must I do to return the data this way or what way ?
string staffCode = data["StaffCode"].ToString();
string perms = data["Perms"].ToString();
Thanks for any help
your data variable is a collection of rows. You need to iterate over the collection to do something interesting with each row.
foreach (var row in data)
{
string staffCode = row["StaffCode"].ToString();
string perms = row["Perms"].ToString();
}
Update:
Based on your comment that you only expect GetSomeData(...) to return a single row, I'd suggest 1 of two things.
Change the signature of GetSomeData to return an IDataRecord. and remove "yield" from the implementation.
public IDataRecord GetSomeData(string fields, string table, string where = null, int count = 0)
{
string sql = "SELECT #Fields FROM #Table WHERE #Where";
using (SqlConnection cn = new SqlConnection(db.getDBstring(Globals.booDebug)))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cmd.Parameters.Add("#Fields", SqlDbType.NVarChar, 255).Value = where;
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
return (IDataRecord)rdr;
}
}
}
}
}
Or
var row = data.FirstOrDefault();
if (row != null)
{
string staffCode = row["StaffCode"].ToString();
string perms = row["Perms"].ToString();
}
Remarks:
Your implementation of GetSomeData is incomplete. You are not even using several of the parameters, most importantly the fields parameter. And conceptually in SQL you can't parameterize which fields get returned or which table gets used (etc.), but rather you need to construct a dynamic query and execute it.
Update 2
Here is an implementation of GetSomeData that constructs a proper query (in C# 6, let me know if you need it in an earlier version).
public IEnumerable<IDataRecord> GetSomeData(IEnumerable<string> fields, string table, string where = null, int count = 0)
{
var predicate = string.IsNullOrWhiteSpace(where) ? "" : " WHERE " + where;
string sql = $"SELECT { string.Join(",", fields) } FROM {table} {predicate}";
using (SqlConnection cn = new SqlConnection(db.getDBstring(Globals.booDebug)))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return (IDataRecord)rdr;
}
}
}
}
And here is how you would use it.
IEnumerable<IDataRecord> data = bw.GetSomeData(new[] { "StaffCode", "Perms" }, "BW_Staff", "StaffCode = 'KAA'");
You can either enumerate it or call .FirstOrDefault, it's your choice. Each time you call GetSomeData, it will run the query.
Update 3
GetSomeData implemented with earlier versions of C#
public IEnumerable<IDataRecord> GetSomeData(IEnumerable<string> fields, string table, string where = null, int count = 0)
{
var predicate = string.IsNullOrEmpty(where) ? "" : " WHERE " + where;
string sql = string.Format("SELECT {0} FROM {1} {2}", string.Join(",", fields), table, predicate);
using (SqlConnection cn = new SqlConnection(db.getDBstring(Globals.booDebug)))
using (SqlCommand cmd = new SqlCommand(sql, cn))
{
cn.Open();
using (IDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
yield return (IDataRecord)rdr;
}
}
}
}

Using SQLiteCommand and SQLiteDataReader in same foreach loop for multiple times

In my WPF application i ve to use SQLiteCommand and SQLiteDataReader multiple times in same foreach loop. Here is how i am doing but its not working as expected. I am expecting to get all the records from ArtistSong_tbl which match the song_cord_ids datas. But for now i only got single record.
Here is code.
try
{
conn.Open();
var command = conn.CreateCommand();
command.CommandText = "SELECT song_id FROM fav_tbl";
SQLiteDataReader sdr = command.ExecuteReader();
while(sdr.Read())
{
song_cord_ids.Add(sdr.GetString(0));
}
sdr.Close();
if(song_cord_ids.Count>0)
{
foreach (var songId in song_cord_ids)
{
command.CommandText = "SELECT * FROM ArtistSong_tbl WHERE Artist_id='" + songId + "'";
SQLiteDataReader sdr1 = command.ExecuteReader();
if (sdr1.HasRows)
{
while (sdr1.Read())
{
favsongs.Add(new Song()
{
_id = sdr1.GetString(1),
title = Crypto_Engine.Decrypt(sdr1.GetString(2), "songcord-ekbana-crypto11"),
isTabs = sdr1.GetString(3)
});
}
}
else
{
//ToDo visibility control
}
sdr1.Close();
}
this.FavSongs = favsongs.OrderBy(s=> s.title).ToList();
}
}
So in code i have foreach loop and in that loop i am getting specific songs(by songs id) from database so when loop starts for 1 time it works fine after that at bottom i closed sdr1(SQLiteDataReader variable) so in next loop command.ExecuteReader() doesn't work because sdr1 variable is closed already and if i didn't close that then i am unable to load another command and execute while sdr1 is still open. So how can i make it work?
You don't need two readers, you should re-write your query with JOIN:
SELECT
ArtistSong_tbl.*
FROM
ArtistSong_tbl JOIN
fav_tbl ON ArtistSong_tbl.Artist_id = fav_tbl.song_id
Then code could be refactored like this:
using (var conn = /* create connection */)
{
conn.Open();
using (var command = conn.CreateCommand())
{
command.CommandText = "[Query text from above]";
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
favsongs.Add(new Song
{
_id = sdr1.GetString(1),
title = Crypto_Engine.Decrypt(sdr1.GetString(2), "songcord-ekbana-crypto11"),
isTabs = sdr1.GetString(3)
});
}
}
}
this.FavSongs = favsongs.OrderBy(s=> s.title).ToList();
}

Categories