Iterate trougth database table rows C# - c#

I have in my database a table called students that have the number and name, address....
I have a form where I load all information for one student at a a time , and I have a next button and a back button.
How can I iterate to the next row (or previous row) in mysql (to be able to see the info of the next student) ?
I tried to use the primary key (auto increment) to iterate and when I want to see the next record I add 1 to the id or subtract 1 to see the previous record.
But if one record is deleted it will show an empty record.
Can you point me in the rigth direction?
I´m using WinForms
Sorry about my english..
string config = "server=localhost; userid = root; database = databaseName";
MySqlConnection con = new MySqlConnection(config);
MySqlDataReader reader = null;
string query = "SELECT * FROM students WHERE id = " + id; //id is the primary Key (auto increment)
MySqlCommand command = new MySqlCommand(query, con);
con.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
string studentName = (string)reader["studentName"];
string studentNum = (string)reader["studentNum"];
tbstudentName.Text = Convert.ToString(studentName);
tbstudentNum.Text = Convert.ToString(studentNum);
.....
}
con.Close();

You should not be calling the database each time you want to view the next record. Try reading all the data into a List.
I am not sure what you are using.. WinForms? WPF?
If WinForms you will need to do something like this.
public class Student
{//First create a class to hold your data in
public string Name { get; set; }
public string Num { get; set; }
}
public class MyForm : Form
{
int Index = 0;
List<Student> FormData { get; set; }
void GetData()
{
//This will hold all your data in memory so you do not have to make a database call each and every "iteration"
List<Student> dbData = new List<Student>();
string config = "server=localhost; userid = root; database = databaseName";
MySqlConnection con = new MySqlConnection(config);
MySqlDataReader reader = null;
string query = "SELECT * FROM students";
MySqlCommand command = new MySqlCommand(query, con);
con.Open();
reader = command.ExecuteReader();
while (reader.Read())
{
Student newStudent = new Student();
newStudent.Name = (string)reader["studentName"];
newStudent.Num = (string)reader["studentNum"];
//Add data to the list you created
dbData.Add(newStudent);
.....
}
con.Close();
//set the Form's list equal to the one you just populated
this.FormData = dbData;
}
private void BindData()
{
//If winforms
tbstudentName.Text = FormData[Index].Name;
tbstudentNum.Text = FormData[Index].Num;
//If wpf you will have to use view models and bind your data in your XAML but I am assuming you are using
//winforms here.
}
private void NextRecord()
{ //If you reached the end of the records then this will prevent IndexOutOfRange Exception
if (Index < FormData.Count - 1)
{
Index++;
BindData();
}
}
private void PreviousRecord()
{
if (Index != 0)
{
Index--;
BindData();
}
}
}
Now the above scenario will get it working quickly; however, there are better ways in doing this that would help you when you need to alter that data. I would recommend WinForms Binding. You can check it out here http://msdn.microsoft.com/en-us/library/c8aebh9k(v=vs.110).aspx

To get the next you can write:
select * from students where id > #id
order by id asc
limit 1
And to get previous
select * from students where id < #id
order by id desc
limit 1

DataReader Designed to quick one-time read.
If you want to hold the data, you need to fill memory arrays.
the DataTable implements it very well.

You will need to think a little different.
Getting id+1 you are being very careless.. Even identity, the Id can be another value and you will get an Exception.. I suppose that you don't want it.
You will need to Adjust your logic to return lines with top or, in mysql, limit statement..
This will be easy using lambda to use .Take() and Skip() methods...
You also can use the limit parameter to pass throug this sample.. you can understand..
MySQL skip first 10 results
Hope it helps.

Related

Get ID of value in a ComboBox

I have brought the data as below into the combobox. In my "BRANDS" table, the first column is brand_id and the second column is brand_name. I'm getting the names in the combobox, but I need to get the id's when saving it to the database. How can I do it ?
void markaekle() {
SqlCommand komut = new SqlCommand("Select * from MARKALAR", bgl.baglanti());
SqlDataReader dr = komut.ExecuteReader();
while (dr.Read())
{
comboBoxMarka.Properties.Items.Add(dr[1]);
}
bgl.baglanti().Close();
}
I need to save the id value to the database with a button like below:
private void BtnAnaGrupKaydet_Click(object sender, EventArgs e){
SqlCommand komut = new SqlCommand("INSERT INTO ANA_GRUP (marka_id,anagrup_isim,create_date) values (#p1,#p2,#p3)", bgl.baglanti());
komut.Parameters.AddWithValue("#p1", int.Parse(comboBoxMarka.Text));
komut.Parameters.AddWithValue("#p2", txtAnaGrup.Text);
komut.Parameters.AddWithValue("#p3", dateAnaGrup.DateTime);
komut.ExecuteNonQuery();
bgl.baglanti().Close();
MessageBox.Show("Ana Grup Sisteme Eklendi", "Bilgi", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
You can do many implementations.
Use Dataset and after executing your query, you can fill the dataset with the result. so you have all your data in your dataset. (define your database global) and after that when your button hits, you know which index of your combobox is selected. the index is the index of your dataset so you can access the id.
The simplest way is that use a Dictionary with Id and Name. after that is the same above.
To sum up, you need to have a global variable which stores the Id and Name of your query result. this variable can be a Dictionary or a dataset or ....
In order to get selected value instead of text. you would bind combobox like following:
List<DropDownKeyValue> list = new List<DropDownKeyValue>();
List.Add(new DropDownKeyValue
{
ID = 1,
Name = "Designation"
});
cmbSortBy.DisplayMember = "Name";
cmbSortBy.ValueMember = "ID";
cmbSortBy.DataSource = list;
to get selected ID you would use: cmbSortBy.SelectedValue object.
DropDownKeyValue class:
public class DropDownKeyValue
{
public int ID { get; set; }
public string Name { get; set; }
}
You need to bind your ComboBox to a DataSource. The easiest way for you to do this would be to amend your code along these lines:
SqlDataReader dr = komut.ExecuteReader();
var dt = new DataTable();
dt.Load(dr);
yourComboBox.DataSource = null;
yourComboBox.Items.Clear();
yourComboBox.DisplayMember = "brand_name";
yourComboBox.ValueMember = "brand_id";
yourComboBox.DataSource = dt;
You will now be able to access the id using yourComboBox.SelectedValue.
On the line below change values like.
komut.Parameters.AddWithValue("#p1",int.Parse(comboBoxMarka.SelectedValue.ToString()));

How to avoid storing duplicate date in text box in windows form application?

The below code is storing patients id in both text box 1 and text box 2. It should store the name of the patient in textbox 2. I have tried changing the values in dr.GetValue(1).ToString(), but it's not working! Can anyone please help?
private void button1_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection(#"Data Source=DESKTOP-A85V0ME\SQLEXPRESS;Initial Catalog=Hospitalmanagement;Integrated Security=True");
con.Open();
if (textBox1.Text != "")
{
try
{
string getCust = "select id,name,gen,age,date,cont,addr,disease,status,r_type,building,r_no,price from patient where id=" + Convert.ToInt32(textBox1.Text) + " ;";
SqlCommand cmd = new SqlCommand(getCust, con);
SqlDataReader dr;
dr = cmd.ExecuteReader();
if (dr.Read())
{
textBox2.Text = dr.GetValue(0).ToString();
if (dr[1].ToString() == "Male")
{
radioButton1.Checked = true;
}
else if (dr[1].ToString() == "Female")
{
radioButton2.Checked = true;
}
textBox3.Text = dr.GetValue(1).ToString();
textBox3.Text = dr.GetValue(3).ToString();
textBox4.Text = dr.GetValue(4).ToString();
textBox5.Text = dr.GetValue(5).ToString();
textBox6.Text = dr.GetValue(6).ToString();
textBox7.Text = dr.GetValue(7).ToString();
textBox8.Text = dr.GetValue(8).ToString();
textBox9.Text = dr.GetValue(10).ToString();
textBox10.Text = dr.GetValue(9).ToString();
textBox11.Text = dr.GetValue(11).ToString();
textBox12.Text = dr.GetValue(12).ToString();
// textBox12.Text = dr.GetValue(12).ToString();
}
else
{
MessageBox.Show(" Sorry, This ID, " + textBox1.Text + " Staff is not Available. ");
textBox1.Text = "";
}
}
catch (SqlException excep)
{
MessageBox.Show(excep.Message);
}
con.Close();
}
}
Your sql is:
select id,name,gen,age,date,cont
Which means, in terms of column numbering, that 0 is id, 1 is name...
You said:
It should store the name of patient in the textbox 2
And you wrote:
textBox2.Text = dr.GetValue(0).ToString();
0 is the id.
Perhaps you should switch to names, for everything including giving your controls sensible names. Look how much easier it makes it to understand the code:
nameTextBox.Text = dr["name"].ToString();
Maybe this is true textBox2.Text = dr.GetValue(1).ToString();
If you would separate you concerns, and let every method do only one thing, it would be easy to create tests to see why your code is not working.
Consider to separate your database handling from displaying fetched data. This way you can easily change where you fetch your data or change how you use the fetched data.
Apart from that this will help you to find the source of your problems, it will also make your code better to understand, easy to reuse and change.
You should especially take care that you separate your data (= model) from how you display it (= view) and how you store it.
class Patient
{
public int Id {get; set;}
public string Name {get; set;}
public Gender Gender {get; set;} // Gender is an enum
public DateTime BirthDate {get; set;}
...
}
Of course you don't save the patient's age in the database. If you did, you'd have to update your database daily.
public int Age => // some formula to get the Age from the current Date
Use DateTime.ToDay, Patient.BirthDate, and Today.DayOfYear >= BirthDate.DayOfYear
Fetching data from the database is done in a repository class. All that users of the class (= code, not operators) want to know is, that they can Store Patients in it, and fetch them back, even if the program is restarted. They don't care whether you store it in a database, or in an XML file, or maybe even store it somewhere in the internet.
This will also give you the freedom to change this, without users noticing.
class PatientRepository
{
public string DbConnectionString {get; set;}
public Patient FetchPatient(int patientId)
{
const string sqlTextFetchPatient = "select top 1"
+ " id,name,gen,age,date,cont,addr,disease,status,r_type,building,r_no,price"
+ " from patient"
+ " where id = #Id;";
using (var connectedDb = new SqlConnection(this.DbConnectionString)
{
using (var dbCommand = connectedDb.CreateCommand())
{
dbCommand.CommandText = sqlTextFetchPatient;
dbCommand.Parameters.AddWithValue("#Id", patientId);
connectedDb.Open();
using (var dbReader = dbCommand.ExecuteReader())
{
// expect only one Patient or null
if (dbReader.Read())
{
// there is a Patient:
return new Patient
{
Id = dbReader.GetInt32(0),
Name = dbReader.GetString(1),
Gender = (Gender)dbReader.GetInt32(2),
BirthDate = dbReader.GetDateTime(3),
...
}
}
}
}
}
}
}
Of course your PatientRepository will also have methods to Add / Update / Remove Patients.
By the way, did you see that I used using for every IDisposable object? This way you can be certain that the objects are Disposed as soon as you don't need them anymore, even after exceptions.
Furthermore, I made patientId a parameter and I only selected Top 1.
It seems that you want to show several Patients in a DataGridView. Add the following to your PatientRepository:
IEnumerable<Patient> FetchAllPatients()
{
... same as above
while (dbreader.Read())
{
yield return new Patient
{
Id = dbReader.GetInt32(0),
...
}
}
}
Or maybe You want to fetch Patients "per page"
IEnumerable<Patient> FetchPatientPage(int pageNr, int pageSize)
Do OrderBy and Skip PageNr * PageSize items and Select PageSize items. How to do this depends on your Sql dialect.
Now that we have a method to Fetch a Patient, you can easily unit test it. If you did, you will probably have found your error.
Display fetched patients
Somewhere in your form you have a method to fetch the Patients that you want to show:
private readonly string dbConnectionString = ...
protected Repository PatientRepository {get;} = new PatientRepository(this.dbConnectionString);
protected IEnumerable<Patient> FetchPatients()
{
this.PatientRepository.FetchPatients();
// or fetch per page
}
You want to show them in a DataGridView. Using visual studio designer, you have code like this:
DataGridView dgv1 = new DataGridView();
DataGridViewColumn columnId = new DataGridViewColumn();
DataGridViewColumn columnName = new DataGridViewColumn();
DataGridViewColumn columnGender = new DataGridViewColumn();
...
columnId.DataPropertyName = nameof(Patient.Id);
columnName.DataPropertyName = nameof(Patient.Name);
columnGender.DataPropertyName = nameof(Patient.Gender);
If you only want to Display the Patients:
this.dgv1.DataSource = this.FetchPatients().ToList();
This is display only. If you want to enable editing you should put the Patients in an object that implements IBindingList, like a BindingList
this.dgv1.DataSource = new BindingList(this.FetchPatients().ToList());
Now all changes that the operator makes: edit cells / add new row / delete rows, etc are automatically updated in the BindingList.
A proper design would add the following methods to your form:
BindingList<Patient> DisplayedPatients
{
get => (BindingList<Patient>)this.dgv1.DataSource;
set => this.dgv1.DataSource = value;
}
And if you want to handle items in selected item
Patient CurrentPatient => this.dgv1.CurrentRow?.DataBoundItem as Patient;
IEnumerable<Patient> SelectedPatients => this.dgv1.SelectedRows
.Select(row => row.DataBoundItem)
.Cast<Patient>();
After this, handling removed / added / changed Patients will be one-liners.
To display a Patient in your text boxes:
void DisplayPatient(Patient patient)
{
this.TextId = patient.Id;
this.TextBoxName = patient.Name;
...
}
To Display the current Patient in your text boxes:
public void DataGridViewCurrentCellChanged(object sender, ...)
{
this.DisplayPatient(this.CurrentPatient);
}
You could use Dapper Makes Life Easy with less Code;
using Dapper;
create a class like
public class Patient
{
public int id { get; set; }
public string name { get; set; }
public string gen { get; set; }
public int age { get; set; }
}
create a method to get Data
var ConnectionString= #"Data Source=DESKTOP-A85V0ME\SQLEXPRESS;Initial
Catalog=Hospitalmanagement;Integrated Security=True");
public Patient GetPatient(int id)
{
using (IDbConnection con = new SqlConnection(ConnectionString))
{
if (con.State == ConnectionState.Closed)
con.Open();
return con.QueryFirstOrDefault<Patient>("select * from Patient
where id=#id", new {id},
commandType: CommandType.Text);
}
}
call your method on Button Click event like
private void button1_Click(object sender, EventArgs e)
{
var id = Convert.ToInt32(textBox1.Text);
var data = GetPatient();
if (data != null)
{
textBox1.Text = data.id.ToString();
textBox2.Text = data.name;
textBox2.Text = data.age.ToString();
if (data.gen == "Male")
{
radioButton1.Checked = true;
}
else if (data.gen == "Female")
{
radioButton2.Checked = true;
}
// ...............etc
}
}

Change text of button when button is clicked c#

First time messing around with c# and I am trying to change the value of a button when the user clicks it.
The database has four names in it, when the code executes it only shows the name that is in the last row. I would like it to start with the first name, and then each time the button is pressed it should show another name until it has gone through all the names in the DB.
dataReader = command.ExecuteReader();
while (dataReader.Read())
{
bText = dataReader.GetValue(1).ToString();
button1.Text = bText;
}
The while loop does not work the way you think it does. What it does is looping over every result and set the button text. It does not pause anywhere (because there is nothing that tells the loop it should). So it simply iterates over every element as fast as it can. This explains why you only see the last result.
What you can do is to store a counter somewhere that you increment every time the button is pressed and only use the n-th element. Either by skipping the first few entries or depending on the database you use, you could only fetch a specific result entry (in MySQL this would be LIMIT and OFFSET in the select query)
For your specific problem (only 4 entries) I would fetch all entries at the start of the application and store them in an array. Then use the entry at the current index (=your counter) to set the text of the button.
You can try this. I've table named Colors having two columns: ID and Name.
public partial class FrmColor : Form
{
SqlConnection con = new SqlConnection(#"<YOUR CONNECTION STRING HERE>");
int pointer = 1;
int rows = 0;
public FrmColor()
{
InitializeComponent();
}
private void btnColor_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand("Select Name from Colors Where ID = #ID", con);
cmd.Parameters.AddWithValue("#ID", pointer);
con.Open();
SqlDataReader rdr = cmd.ExecuteReader();
rdr.Read();
string colorname = rdr.GetValue(0).ToString();
con.Close();
btnColor.Text = colorname;
if (pointer == rows)
{
pointer = 1;
}
else
{
pointer++;
}
}
private void FrmColor_Load(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand("Select count(Name) From Colors", con);
con.Open();
rows = (Int32)cmd.ExecuteScalar();
con.Close();
}
}
Don't forget to add using System.Data.SqlClient;
Hope this helps.

How to get select multiple records of subtable in lists in mysql and c#?

I have two tables "user" and "user_media" in mysql. The tables are as follows:
User:
id, name, phone, address
1, abc, 000000000, x lane, y city
user_media:
mid, user_id, media_url
j432, 1, http://.....sdfl.png
j84d, 1, http://.....snx8.png
I want to access the data in c# as:
string id;
string name;
string phone;
string address;
List<string> mediaImages;
How can I access the data in this way.
An INNER JOIN returns all the rows from the first table with all the rows from the second table that matches the ON condition. The matching row from the first table are duplicated to accomodate the data coming from the second table. It is your code that should insert the data from the Media table in the required list.
// This list will store all the records retrieved
List<User> usersList = new List<User>();
string cmdText = "SELECT * FROM user INNER JOIN user_media ON user.id = user_media.user_id";
using(MySqlConnection con = new MySqlConnection(.....))
using(MySqlCommand cmd = new MySqlCommand(cmdText, con))
{
// Starting values
int currentUser = -1;
User usr = null;
con.Open();
using(MySqlDataReader reader = cmd.ExecuteReader())
{
// Loop over the results till the end
while(reader.Read())
{
// If the current record is for a different user.....
if(reader.GetInt32("id") != currentUser)
{
// Create it
usr = new User();
// this should go in the constructor of User....
usr.MediaImages = new List<string>();
// Fill the field for the current user...
usr.id = reader.GetInt32("id");
usr.name = reader.GetString("name");
usr.phone = reader.GetString("phone");
usr.address = reader.GetString("address");
// Add the retrieved user to the whole list
userList.Add(usr);
// Prevent adding other elements to the list for the same user....
currentUser = usr.ID;
}
// For the current user add only the url info
usr.MediaImages.Add(reader.GetString("media_url"));
}
}
}
As you can see this is a lot of boilerplate code.... If you really want to abstract your code from these details I suggest to look at ORM (Object Relational Mapper) tools that could do this for you.

getting a select statement column info from combobox

Hopefully i don't sound confusing but i am not sure if what i am trying to get at is possible.
I have a select statement to get name, id, guid. I am setting the display to name and the value to Id for each combobox. Is there a way that i could also assign the guid to the combo box so that i could use it in my winforms app?
here is what i have for select statement:
private void secondChild_drp_SelectedIndexChanged(object sender, EventArgs e)
{
string secondChildId = secondChild_drp.SelectedValue.ToString();
using (SqlConnection con = new SqlConnection(conString))
{
con.Open();
using (SqlDataAdapter sda = new SqlDataAdapter("SELECT ... WHERE em.ChildID = (" + secondChildId + ")", conString))
{
DataTable dt = new DataTable();
sda.Fill(dt);
thirdChild_drp.ValueMember = "ID";
thirdChild_drp.DisplayMember = "DisplayName";
thirdChild_drp.DataSource = dt;
}
}
cmd.CommandText="StoreProcName";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#ChildID", secondChildId);
cmd.Connection = con2;
con2.Open();
reader = cmd.ExecuteReader();
var guid = reader.ToString();
reader.Close();
con2.Close();
}
right now when i run this it tells me reader = cmd.ExecuteReader(); has Procedure or function StoreProcName has too many arguments specified.
i just want to get the guid associated with the id i passed in.
You can get the guid from your datatable as follows where yourselectedid is the combobox selecteditem id.
var results = from row in dt.AsEnumerable()
where row.Field<int>("ID") == yourselectedid
select row;
now from results you can get all the desired columns you want
Basically the same answer as I already posted here:
You could define a simple object which you are filling from your data base query:
public class Item
{
public int ID { get; set; }
public string DisplayName { get; set; }
public Guid Guid{ get; set; }
}
Your implementation could look something like this (some mockup data):
listBox1.DataSource = items;
listBox1.DisplayMember = "DisplayName";
listBox1.ValueMember = "ID";
Then based on the value you selected, you can query through your items and get the item:
var key = (int)listBox1.SelectedValue;
foreach (var existingItem in items)
{
if (existingItem.Key == key)
{
//woohoo got it!
Debug.Print(existingItem.Guid.ToString())
}
}
you can put both of the value in the value member, separated by whichever character for separator like : "12;0000-000-0000" then separate again the Value Menber with a String.Split.

Categories