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
}
}
I'm setting up a point of sales system with counts of products in stock. Unfortunately, I can't seem to get my method to update the quantities of one of my products.
I can execute the query in the command line with a value and it will update the count of the product. The count is stored in the text property of txtBanCount here.
namespace POSSystem
{
public partial class POSsystem : Form
{
public POSsystem()
{
InitializeComponent();
}
MySqlConnection conn;
private void MySqlConnect()
{
string connStr = "server=localhost;user=root;database=possystem;port=3306;password=bhuytr83";
conn = new MySqlConnection(connStr);
try
{
conn.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
throw;
}
}
private void UpdateQuantities()
{
string banSqlUpdate = "Update Products SET count = '" + txtBanCount + "' + WHERE prodName = 'Bananas';";
MySqlCommand cmdBanUpdate = new MySqlCommand(banSqlUpdate,conn);
cmdBanUpdate.ExecuteNonQuery();
}
I expect the 'Count' column of my Products table to be set to the text displayed on txtBanCount.
I assume that txtBanCount is a text control right, so use it's Text
property txtBanCount.Text
so please use below code
private void UpdateQuantities()
{
string banSqlUpdate = "Update Products SET count = '" + txtBanCount.Text + "' WHERE prodName = 'Bananas';";
MySqlCommand cmdBanUpdate = new MySqlCommand(banSqlUpdate,conn);
cmdBanUpdate.ExecuteNonQuery();
}
I've got several forms, some of which rely on information between each form. In this case, the selected index of the chosen option in Form 2 (AKA testSelect) is key to determining what will happen in Form 3 (AKA testPresent). This is placed into an integer called index. Upon closing form 2 the value of index is definitely whatever the selectedindex of the listbox is.
However upon opening and applying it in form 3 it resets to 0 all the time, and I can't figure out why. below is the code where index is used/determined in form 2 as well as the code where it is called in form 3. Also, it is a public int defined at the start of form 2;
private void lstExams_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
//Create a connection object to the DB via string location
con = new OleDbConnection(connectionString);
//Open the connection to the DB
con.Open();
String sql = "SELECT typeID, description FROM TestConfiguration WHERE examID = " +(lstExams.SelectedIndex +1);
OleDbDataAdapter da = new OleDbDataAdapter(sql, con);
//DataSet ds = new DataSet();
DataTable testConfig = new DataTable();
//Set the SQL command text
da.Fill(testConfig);
lstTestType.DisplayMember = "description";
lstTestType.ValueMember = "typeID";
lstTestType.DataSource = testConfig;
index = lstExams.SelectedIndex + 1;
MessageBox.Show("INDEX = " + index);
}
catch (Exception err)
{
//If cannot connect to DB, display the error;
MessageBox.Show("A Database error has occurred: " + Environment.NewLine + err.Message);
}
}
private void btnStart_Click(object sender, EventArgs e)
{
//var testPresent = new testPresent(this);
testPresent testForm = new testPresent();
testForm.Show();
//testForm.difficulty = lstTestType.ValueMember;
this.Close();
MessageBox.Show("INDEX IS " + index);
testForm.eID = (index);
}
And for form 3
public partial class testPresent : Form
{
public int eID = 0;
public String difficulty;
testSelect select = new testSelect();
//Get the connection path to the DB from the static class
String connectionString = staticConnectionString.connectionString;
//Declare connection object
OleDbConnection con;
public testPresent()
{
InitializeComponent();
}
public void testPresent_Load(object sender, EventArgs e)
{
try
{
int qID;
Random random = new Random();
int examID;
bool valid = false;
String theQuestion;
eID += select.index;
//Create a connection object to the DB via string location
con = new OleDbConnection(connectionString);
//Open the connection to the DB
con.Open();
MessageBox.Show("eID = " + select.index);
if (eID == 1)
{
...
}
if (eID == 2)
{
...
}
if (eID == 3)
{
...
}
}
catch (Exception err)
{
//If cannot connect to DB, display the error;
MessageBox.Show("A Database error has occurred: " + Environment.NewLine + err.Message);
}
}
Oh yeah, this also uses Microsoft Access Databases to populate the listbox.
You set the value:
testForm.eID = (index);
But you do so after the form has already been loaded, so testPresent_Load has already used its default value:
testPresent testForm = new testPresent();
testForm.Show(); // this is where it uses the value
// ...
testForm.eID = (index); // this is where you set it
If the value is required for the testPresent form entirely, and it needs the value right away, try including the value in the constructor:
public testPresent(int eid)
{
InitializeComponent();
eID = eid;
}
Then when you create an instance of the form you'd have to pass it that value:
testPresent testForm = new testPresent(index);
testForm.Show();
At that point there'd be no need to set the value externally, so it should be private. (Data members should be private on objects anyway.):
private int eID;
I'm selecting about 20,000 records from the database and then I update them one by one.
I looked for this error and I saw that setting the CommandTimeout will help, but not in my case.
public void Initialize()
{
MySqlConnectionStringBuilder SQLConnect = new MySqlConnectionStringBuilder();
SQLConnect.Server = SQLServer;
SQLConnect.UserID = SQLUser;
SQLConnect.Password = SQLPassword;
SQLConnect.Database = SQLDatabase;
SQLConnect.Port = SQLPort;
SQLConnection = new MySqlConnection(SQLConnect.ToString());
}
public MySqlDataReader SQL_Query(string query)
{
MySqlCommand sql_command;
sql_command = SQLConnection.CreateCommand();
sql_command.CommandTimeout = int.MaxValue;
sql_command.CommandText = query;
MySqlDataReader query_result = sql_command.ExecuteReader();
return query_result;
}
public void SQL_NonQuery(string query)
{
MySqlCommand sql_command;
sql_command = SQLConnection.CreateCommand();
sql_command.CommandTimeout = int.MaxValue;
sql_command.CommandText = query;
sql_command.ExecuteNonQuery();
}
And here is my method which makes the select query:
public void CleanRecords()
{
SQLActions.Initialize();
SQLActions.SQL_Open();
MySqlDataReader cashData = SQLActions.SQL_Query("SELECT `cash`.`id`, SUM(`cash`.`income_money`) AS `income_money`, `cash_data`.`total` FROM `cash_data` JOIN `cash` ON `cash`.`cash_data_id` = `cash_data`.`id` WHERE `user`='0' AND `cash_data`.`paymentterm_id`='0' OR `cash_data`.`paymentterm_id`='1' GROUP BY `cash_data_id`");
while(cashData.Read()){
if(cashData["income_money"].ToString() == cashData["total"].ToString()){
UpdateRecords(cashData["id"].ToString());
}
}
SQLActions.SQL_Close();
}
And here is the method which makes the update:
public void UpdateRecords(string rowID)
{
SQLActions.Initialize();
SQLActions.SQL_Open();
SQLActions.SQL_NonQuery("UPDATE `cash_data` SET `end_date`='" + GetMeDate() + "', `user`='1' WHERE `id`='" + rowID + "'");
SQLActions.SQL_Close();
}
Changing the database structure is not an option for me.
I thought that setting the timeout to the maxvalue of int will solve my problem, but is looks like this wont work in my case.
Any ideas? :)
EDIT:
The error which I get is "Fatal error encoutered during data read".
UPDATE:
public void CleanRecords()
{
StringBuilder dataForUpdate = new StringBuilder();
string delimiter = "";
SQLActions.Initialize();
SQLActions.SQL_Open();
MySqlDataReader cashData = SQLActions.SQL_Query("SELECT `cash`.`id`, SUM(`cash`.`income_money`) AS `income_money`, `cash_data`.`total` FROM `cash_data` JOIN `cash` ON `cash`.`cash_data_id` = `cash_data`.`id` WHERE `user`='0' AND `cash_data`.`paymentterm_id`='0' OR `cash_data`.`paymentterm_id`='1' GROUP BY `cash_data_id`");
while (cashData.Read())
{
if (cashData["income_money"].ToString() == cashData["total"].ToString())
{
dataForUpdate.Append(delimiter);
dataForUpdate.Append("'" + cashData["id"].ToString() + "'");
delimiter = ",";
}
}
SQLActions.SQL_Close();
UpdateRecords(dataForUpdate.ToString());
}
public void UpdateRecords(string rowID)
{
SQLActions.Initialize();
SQLActions.SQL_Open();
SQLActions.SQL_NonQuery("UPDATE `cash_data` SET `end_date`='" + GetMeDate() + "', `user`='1' WHERE `id` IN (" + rowID + ")");
SQLActions.SQL_Close();
}
You may be able to use
UPDATE cash_data .... WHERE id IN (SELECT ....)
and do everything in one go. Otherwise, you could do it in two steps: first the select collects all the ids, close the connection and then do the update in obne go with all the ids.
The code for the second option might look something like this:
public void CleanRecords()
{
StringBuilder builder = new StringBuilder();
string delimiter = "";
SQLActions.Initialize();
SQLActions.SQL_Open();
MySqlDataReader cashData = SQLActions.SQL_Query("SELECT `cash`.`id`, SUM(`cash`.`income_money`) AS `income_money`, `cash_data`.`total` FROM `cash_data` JOIN `cash` ON `cash`.`cash_data_id` = `cash_data`.`id` WHERE `user`='0' AND `cash_data`.`paymentterm_id`='0' OR `cash_data`.`paymentterm_id`='1' GROUP BY `cash_data_id`");
while(cashData.Read()){
if(cashData["income_money"].ToString() == cashData["total"].ToString()){
builder.Append(delimiter);
builder.Append("'" + cashData["id"].ToString() + "'");
delimiter = ",";
}
}
SQLActions.SQL_Close();
UpdateRecords(builder.ToString());
}
public void UpdateRecords(string rowIDs)
{
SQLActions.Initialize();
SQLActions.SQL_Open();
SQLActions.SQL_NonQuery("UPDATE `cash_data` SET `end_date`='" + GetMeDate() + "', `user`='1' WHERE `id` IN (" + rowIDs + ")";
SQLActions.SQL_Close();
}
There are multiple problem:
First: You have reading information around 20K using data reader and then doing update one by one in reader itself. Reader holds the connection open until you are finished. So this is not the good way to do it. Solution: We can read the information using Data Adapter.
Second: Rather than doing one by one update, we can update in bulk in one go. There are multiple option for bulk operation. In SQL u can do either by sending information in XML format or u can use Table Valued Parameter (TVP) (http://www.codeproject.com/Articles/22205/ADO-NET-and-OPENXML-to-Perform-Bulk-Database-Opera) OR (http://dev.mysql.com/doc/refman/5.5/en/load-xml.html)
I was hoping someone could help me out with this stupid problem I'm having with the following SQL statement:
public void ApplyInference(string AnswerSelected)
{
int InferenceID;
int QuestionID;
string AnswerInference;
int PainValue;
int AnxietyValue;
int DepressionValue;
int FearValue;
int TransportValue;
int EmotionalValue;
int FinancialValue;
int PhysicalValue;
int SpiritValue;
int SocialValue;
SqlConnection NewConnection = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\QuestionsDatabase.mdf;Integrated Security=True;User Instance=True"); //"Initial Catalog=Northwind;Integrated Security=SSPI");
SqlDataReader ReadIn = null;
try
{
NewConnection.Open();
SqlCommand GetInference = new SqlCommand("SELECT * FROM InferenceDB WHERE QuestionID =" + this.QuestionID + "AND AnswerInference =" + AnswerSelected, NewConnection);
ReadIn = GetInference.ExecuteReader();
while (ReadIn.Read())
{
InferenceID = Convert.ToInt32(ReadIn[0]);
QuestionID = Convert.ToInt32(ReadIn[1]);
AnswerInference = Convert.ToString(ReadIn[2]);
PainValue = Convert.ToInt32(ReadIn[3]);
AnxietyValue = Convert.ToInt32(ReadIn[4]);
DepressionValue = Convert.ToInt32(ReadIn[5]);
FearValue = Convert.ToInt32(ReadIn[6]);
TransportValue = Convert.ToInt32(ReadIn[7]);
EmotionalValue = Convert.ToInt32(ReadIn[8]);
FinancialValue = Convert.ToInt32(ReadIn[9]);
PhysicalValue = Convert.ToInt32(ReadIn[10]);
SpiritValue = Convert.ToInt32(ReadIn[11]);
SocialValue = Convert.ToInt32(ReadIn[12]);
MessageBox.Show("InferenceID: " + InferenceID + "\nAnswer Value: " + AnswerInference + "\nPain value: " + PainValue + "\nSocial value: " + SocialValue);
//LoadQuestionForm(this.FormStyle);
}
}
finally
{
if (ReadIn != null)
{
ReadIn.Close();
}
if (NewConnection != null)
{
NewConnection.Close();
}
}
}`
Now the code works tor every other column in the table except for the one I need which is the AnswerInference one. I am feeding the AnswerInference value in from another method which looks like this:
private void Answer1Button_Click(object sender, EventArgs e)
{
parent.ApplyInference("Ans1");
CloseForm();
}
Unfortunately I can't get the code to work using string data found in the table I'm using. I know this should be an easy fix, but I can for the life of me work out what's going on. Can someone suggest what I'm doing wrong?
You need a space between the question-id and the and, but more importantly: you should use parameters. Look into "SQL injection", query-plan re-use, etc. The most appropriate way to do this is with a command like:
using(var GetInference = new SqlCommand(#"
SELECT *
FROM InferenceDB
WHERE QuestionID = #questionID AND AnswerInference = #answerInference"
, NewConnection);
{
GetInference.Parameters.AddWithValue("questionID", QuestionID);
GetInference.Parameters.AddWithValue("answerInference", AnswerSelected);
....
}
Also - that complex-looking try/finally can be simplified with using here.
You need a space before the AND
"SELECT *
FROM InferenceDB
WHERE QuestionID = " + this.QuestionID + " AND AnswerInference = " + AnswerSelected