Read multiple rows and multiple columns using SqlDataReader in C# - c#

I need to know how I can get the values returned by multiple rows and multiple columns of a query using SqlDataReader in C#.
try
{
SqlConnection connection = new SqlConnection(ConfigurationManager.ConnectionStrings["conn"].ConnectionString);
connection.Open();
string query = ("select cardname,cardnumber,expiry,cardballance from vwallet where username='" + uname + "'");
SqlCommand cmd = new SqlCommand(query, connection);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
//cardname = reader[0].ToString();
//cardnumber = reader[1].ToString();
//expiry = reader[2].ToString();
//cardballance = reader[3].ToString();
reader.
}
}
Note: I want to display the result returned by the query i.e cardnames, cardnumbers, expiry and cardballance into labels.
My current understanding is that the code I wrote will read only one row's column and assign to variables (declared already in the code, not pasted declaration here).
Data returned through table:
I want to display all of these in labels.
How to read all the data returned from table (table data shown in picture).

You are almost there. You just need an array or collection to store the rows in.
public class MyCard
{
public string Name { get; set; }
public string Number { get; set; }
public string Expiry { get; set; }
public string Balance { get; set; }
//Please note: This needs updating to match the data type used in your DB table.
//I have used string to show you a simple example.
}
Then update your code to include:
SqlDataReader reader = cmd.ExecuteReader();
List<MyCard> MyCardList = new List<MyCard>();
while (reader.Read())
{
MyCard mycard = new MyCard();
mycard.Name = reader[0].ToString();
mycard.Number = reader[1].ToString();
mycard.Expiry = reader[2].ToString();
mycard.Balance = reader[3].ToString();
MyCardList.Add(mycard);
}
//Remember to close the reader and dispose of objects correctly.
Then you have a list of MyCard objects with all your data.

Related

Display 2 SQL tables using c#

I am trying to display two SQL tables from the same DB in a webpage but the code below is displaying the 'BottomStock' table twice and everything I try seems to either get part of the data from the 'TopStock' table or none at all. I have scroll through countless forums but I have been unable to find a suitable solution. Any help would be appreciated.
public class Test4Model : PageModel
{
public List<FreezerInfo> listTopFreezer = new List<FreezerInfo>();
public List<FreezerInfo> listBottomFreezer = new List<FreezerInfo>();
public void OnGet()
{
try
{
using (var connection = new SqlConnection("Data Source=SDS-
LAPTOP\\SQLEXPRESS;Initial Catalog=test;user id=sa;password=wis09"))
{
connection.Open();
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT * FROM TopStock";
command.ExecuteNonQuery();
command.CommandText = "SELECT * FROM BottomStock";
command.ExecuteNonQuery();
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
//reader.Read();
{
FreezerInfo TopStock = new FreezerInfo();
TopStock.Description = reader.GetString(1);
TopStock.Quantity = reader.GetString(2);
listTopFreezer.Add(TopStock);
FreezerInfo BottomStock = new FreezerInfo();
BottomStock.Description = reader.GetString(1);
BottomStock.Quantity = reader.GetString(2);
listBottomFreezer.Add(BottomStock);
}
}
}
}
}
catch (Exception ex)
{
}
}
}
public class FreezerInfo
{
public string Description { get; set; }
public string Quantity { get; set; }
}
You are using SqlCommand completely wrong. ExecuteNonQuery does not return results. Only ExecuteScalar or ExecuteReader do. Furthermore, you have two batches each with a SELECT, but you are only executing one and somehow expecting the results to be interleaved.
I would advise you to use one batch of two SELECT statements, you can use NextResult to move to the next resultset within the batch.
Store your connection string in a settings file, not hard-coded.
Only select the columns you need, rather than SELECT *.
Use column names rather than ordinals, especially if you are using SELECT *.
Do not swallow exceptions. Handle them or allow them to bubble back to the caller.
Consider using async to allow the caller to continue asynchronously.
Reconsider the data types of the columns.
Consider why you have two almost identical tables in the first place. Perhaps they should be merged.
public void OnGet()
{
try
{
const string query = #"
SELECT Description, Quantity
FROM TopStock;
SELECT Description, Quantity
FROM BottomStock;
";
using var connection = new SqlConnection(connectionStringFromSettingsFileHere);
using var command = new SqlCommand(query, connection);
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
while (reader.Read())
{
FreezerInfo TopStock = new FreezerInfo
{
Description = (string)reader["Description"],
Quantity = (string)reader["Quantity"], // shouldn't it be an int???
};
listTopFreezer.Add(TopStock);
}
reader.NextResult();
while (reader.Read())
{
FreezerInfo BottomStock = new FreezerInfo
{
Description = (string)reader["Description"];
Quantity = (string)reader["Quantity"], // shouldn't it be an int???
};
listBottomFreezer.Add(BottomStock);
}
}
catch (Exception ex)
{
// exception handling here. DO NOT SWALLOW
}
}

C# & SQL Unable to pull Data from Permissions Table

I have a permissions database that I created which has 10 data points in it. I can insert data into this table through the program, but when I try to pull the data it's been giving me a lot of headaches. I've attempted to use a Using loop and it errors out every time, so I'm trying to just go super basic, and pull the data line by line but it's not working at all. My goal is to pull all the data into a List Variable so that I can call each individual permission later. What is stored in each permission is simply the Text True or False, with the exception of the first one Emp_ID being an Int.
Con is my connection script, and it's working perfectly, as it works everywhere else within the program.
Settings.Emp_ID is the Emp_ID of the user that's currently logged in. This means that we can skip the Emp_ID from the permissions when pulling the data, but I've attempted to do an EXCLUDE or SKIP but it's failed every time as well.
SQL Format, Emp_ID = INT, all others = Text
As for Error:
System.NullReference Exception 'Object Reference Not set to an instance of an object.
namespace TMS
{
public partial class Login_Form : Form
{
string[] Data;
void verify()
{
SqlDataReader rdr = null;
SqlCommand cmd = new SqlCommand("SELECT * FROM Permissions WHERE Emp_ID = '"
+ Settings.Emp_ID + "'", Con);
try
{
Con.Open();
rdr = cmd.ExecuteReader();
while (rdr.Read())
{
int Emp_ID = (int)rdr["Emp_ID"];
Data[0] = (string)rdr["Check_Out"];
Data[1] = (string)rdr["Check_In"];
Data[2] = (string)rdr["Self_His_Tool"];
Data[3] = (string)rdr["Self_His_User"];
Data[4] = (string)rdr["Tool_His"];
Data[5] = (string)rdr["User_His"];
Data[6] = (string)rdr["Add_Users"];
Data[7] = (string)rdr["Add_Tools"];
Data[8] = (string)rdr["Remove_Users"];
Data[9] = (string)rdr["Remove_Tools"];
}
}
finally
{
if (rdr != null)
{
rdr.Close();
}
if (Con != null)
Con.Close();
}
}
The Data array is still null, because this line does not actually create an array object:
string[] Data;
All it does is create a variable that might someday refer to an array object.
Later, when you have this and lines like it:
Data[0] = (string)rdr["Check_Out"];
You end up with the NullReferenceException. Data is still null, and so trying to access Data[0] is not allowed.
We want something more like this:
public class Permissions
{
//probably these should be "bool", but I adapted the types from the old code
public string Check_Out {get;set;}
public string Check_In {get;set;}
public string Self_His_Tool {get;set;}
public string Self_His_User {get;set;}
public string Tool_His {get;set;}
public string User_His {get;set;}
public string Add_Users {get;set;}
public string Add_Tools {get;set;}
public string Remove_Users {get;set;}
public string Remove_Tools {get;set;}
}
// ...
Permissions Data;
Permissions verify()
{
string SQL = "SELECT * FROM Permissions WHERE Emp_ID = #Emp_ID";
// Do NOT re-use the some connection object throughout an app or class!
// Only re-use the connection string.
// using directive will ensure connection is closed, so no need for finally block
using var con = new SqlConnection("connection string here");
using var cmd = new SqlCommand(SQL, con);
// Do NOT use string concatation to substitute data into a query!
cmd.Parameters.Add("#Emp_ID", SqlDbType.Int).Value = Settings.Emp_ID;
// don't need a "try" if there's no catch or finally
con.Open();
rdr = cmd.ExecuteReader();
Permissions result = null;
if (rdr.Read()) //Don't need "while" if we only expect one record
{
result = new Permissions();
int Emp_ID = Settings.Emp_ID;
result.Check_out =(string)rdr["Check_Out"];
result.Check_In = (string)rdr["Check_In"];
result.Self_His_Tool = (string)rdr["Self_His_Tool"];
result.Self_His_User = (string)rdr["Self_His_User"];
result.Tool_His = (string)rdr["Tool_His"];
result.User_His = (string)rdr["User_His"];
result.Add_Users = (string)rdr["Add_Users"];
result.Add_Tools = (string)rdr["Add_Tools"];
result.Remove_Users = (string)rdr["Remove_Users"];
result.Remove_Tools = (string)rdr["Remove_Tools"];
}
return result;
}

How to check if OleDbDataReader is empty?

This is the code i am using to select Maximum RollNo based on Class field value.
But when there is no data about Class Field in Table. Then Error is generated.
using (var conn = new OleDbConnection(DatabaseObjects.ConnectionString))
{
conn.Open();
command = new OleDbCommand("select max(RollNo) as Roll from Students where Class = '"+cmbClass.Text+"'", conn);
OleDbDataReader dr = command.ExecuteReader();
if (!dr.IsDBNull(0))
{
while (dr.Read())
{
i = Convert.ToInt32(dr["Roll"]);
}
}
InvalidOperation Exception is occurring. I want to get value of RollNo if data is available in Table. If data is not available in Table then what should I do?
you are inversing the steps :
open the connection;
check is there is comming data;
check if the value is not null;
read the data;
try this :
while (dr.Read())
{
if (!dr.IsDBNull(0))
{
i = Convert.ToInt32(dr["Roll"]);
}
}
while your are attending just single value, use executeScalar to get the value ;
conn.Open();
command = new OleDbCommand("select isnull(max(RollNo),-1) as Roll from Students where Class = '"+cmbClass.Text+"'", conn);
int rollNo = (int) command.ExecuteScallar();
if(rollno !=-1)
{
// TODO :
}

Initializing Combo Box in C# Access

I was trying to add a combo box which could get all the product name but unfortunately I follow some tutorials and end up like this.
void fillCombo()
{
try
{
con.Open();
OleDbCommand command = new OleDbCommand("Select * from IblInventory");
command.Connection = con;
OleDbDataReader reader = command.ExecuteReader();
while (reader.Read())
{
String product = reader.GetString("ProductName"); // << invalid argument
cmbProduct.Items.Add(product);
}
con.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
What could possibly the reason?
From the documentation of OleDbDataReader.GetString you will notice that the argument required by the method is an integer representing the position of the column in the returned record not its name.
If you (rightly) prefer to use the column name then you need to take a detour and use the GetOrdinal method to retrieve the position of the column given the name.
while (reader.Read())
{
int pos = reader.GetOrdinal("ProductName");
String product = reader.GetString(pos);
cmbProduct.Items.Add(product);
}
Another example, practically identical to your situation, can be found in the documentation page on MSDN about OleDbDataReader.GetOrdinal
It is also a common practice to write an extension method that allows you to write code as yours hiding the details of the mapping between name and position. You just need a static class with
public static class ReaderExtensions
{
public string GetString(this OleDbDataReader reader, string colName)
{
string result = "";
if(!string.IsNullOrEmpty(colName))
{
int pos = reader.GetOrdinal(colName);
result = reader.GetString(pos);
}
return result;
}
... other extensions for Int, Decimals, DateTime etc...
}
Now with this class in place and accessible you can call
string product = reader.GetString("ProductName");
it is working in my project
First fill your data in to datatable see the below code
DataTable results = new DataTable();
using(OleDbConnection conn = new OleDbConnection(connString))
{
OleDbCommand cmd = new OleDbCommand("Select * from IblInventory", conn);
conn.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter(cmd);
adapter.Fill(results);
}
Now
cmbProduct.DataSource = results ;
cmbProduct.DisplayMember = "ProductName";
cmbProduct.ValueMember = "Id feild of IblInventory table";

Filling custom C# objects from data received stored procedure

public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Address
{
public string City { get; set; }
public string Country { get; set; }
}
/*
* There are 2 c# objects i have shown
* There is a stored procedure in my application which
* returns data for both objects simultaneously
* eg
* select FirstName, LasteName from Users where something="xyz"
* select City,Country from Locations where something="xyz"
*
* both queries are run by single procedure
* Now how can i fill both objects with from that stored procedure in asp.net using c#
*/
Use ADO.NET, open a SqlDataReader on a SqlCommand object executing the SP with the parameters. Use the SqlDataReader.NextResult method to get the second result set.
Basically:
SqlConnection cn = new SqlConnection("<ConnectionString>");
cn.Open();
SqlCommand Cmd = new SqlCommand("<StoredProcedureName>", cn);
Cmd.CommandType = System.Data.CommandType.StoredProcedure;
SqlDataReader dr = Cmd.ExecuteReader(CommandBehavior.CloseConnection);
while ( dr.Read() ) {
// populate your first object
}
dr.NextResult();
while ( dr.Read() ) {
// populate your second object
}
dr.Close();
You could use ADO.net and design a dataset which will create the classes for you so your queries will execute and read into classes which store the data you got.
http://msdn.microsoft.com/en-us/library/aa581776.aspx
That is an excellent tutorial on how to create a data access layer, which is what it sounds like you want to do.
using(SqlConnection connexion = new Sqlconnection(youconenctionstring))
using(SqlCommand command = conenxion.Createcommand())
{
command.Commandtext = "yourProcName";
command.CommandType = CommandType.StoredProcedure;
command.Paramters.Add("#yourparam",yourparamvalue);
connexion.Open();
SqlDataReader reader = command.ExecuteReader();
List<User> users = new List<User>;
List<Adress> adresses = new List<User>;
while(read.Read())
{
User user = new User();
user.firstName = (string) read["FirstName"];
users.Add(user);
}
read.NextResult();
while(read.Read)
{
Address address = new Address();
address.City = (string) read["Name"];
adresses.Add(address);
}
//make what you want with your both list
}
Linq to SQL, Entity Framework, or NHibernate would be my suggestions.
Check out the Enterprise library, specifically the Data Access block from microsoft patterns and practices. Even if you don't use it, you can steal, er... borrow code from it to do what you want.
http://www.codeplex.com/entlib

Categories

Resources