I have a database with the following tables:
**Camping Spot**
PK - id - int
FK - location_id - int
number - int
capacity - int
**Location**
PK - id - int
name - string
street - varchar
etc.
**Event**
PK - id - int
FK - location_id - int
name - string
datestart - datetime
etc.
I have the following classes
namespace modellen
{
public class Spot
{
public int Id { get; set; }
public int Number { get; set; }
public int Capacity { get; set; }
}
}
and
namespace DAL_laag
{
public class SpotDal
{
List<Spot> spots = new List<Spot>();
private Database database = new Database();
public GiveAvailiableSpots(int event_id)
{
string query = "A query that gets Id, Number and Capacity";
return ?
}
}
I want to get the id, number and capacity values from the table with a mssql query. Then I want to create a new Spot object and add the object to my list of Spots.
I can't figure out what the query would be to get these three value and the code to create a new object with these three values.
How would I do this?
I think your query needs to look something like this.
string query = #"select id,
number,
capacity
from tblCampingSport cs
left join tblLocation l on cs.location_id == l.id
left join tblEvent e on e.location_id = l.id
where e.id = #eventId";
You're call to the db will look something like this:
List<QueryResult> results = new List<QueryResult>();
using(SqlConnection conn = new SqlConnection(ConnectionString))
{
conn.Open();
using(SqlCommand cmd = new SqlCommand(query, conn)
{
cmd.Parameters.AddWithValue("#eventId", event_id);
var reader = cmd.ExecuteReader();
if(reader.HasRows())
{
while(reader.Read())
{
QueryResult result = new QueryResult();
result.EventId = (int)reader["id"];
result.Number = (int)reader["number"];
result.Capacity = (int)reader["capacity"];
results.Add(result);
}
}
}
}
Your class to store the results:
class QueryResult
{
int EventId { get; set;}
int Number { get; set;}
int Capacity { get; set;}
}
None of this is tested or even compiled (it was written straight into this textbox), but I think it's a rough outline of how to get what you want from your tables.
Assuming you are using the MySql Libary to connect to your database. You need a class that allows you to connect to the database which looks like the code segment below; I am using the UWP for Windows 10 with c# so the code should work, however, there may be slight changes but nothing major.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MySql.Data.MySqlClient;
using Windows.UI.Popups;
using Walsall_College_Auditor.Classes;
namespace Walsall_College_Auditor.Models
{
class dbConnect
{
private MySqlConnection connection;
private string server;
private string database;
private string uid;
private string password;
//Constructor
public dbConnect()
{
Initialize();
}
//Initialize values
private void Initialize()
{
//Prevent the application from throwing "windows-1252' is not a supported encoding name."
System.Text.EncodingProvider ppp;
ppp = System.Text.CodePagesEncodingProvider.Instance;
Encoding.RegisterProvider(ppp);
server = "localhost";
database = "your_db_name"; //Put the new database name here
uid = "root"; //Your db Login/Username
password = ""; //Your db login password
string connectionString = "SERVER=" + server + ";" + "DATABASE=" + database + ";"
+ "UID=" + uid + ";" + "PASSWORD=" + password + ";SslMode=None";
connection = new MySqlConnection(connectionString);
}
//open connection to database
private bool OpenConnection()
{
try
{
connection.Open();
return true;
}
catch (MySqlException ex)
{
//When handling errors, you can your application's response based
//on the error number.
//The two most common error numbers when connecting are as follows:
//0: Cannot connect to the server.
//1045: Invalid username and/or password.
switch (ex.Number)
{
case 0:
var dialog1 = new MessageDialog("Cannot connect to server. Contact administrator");
dialog1.Title = "Connection Error";
dialog1.ShowAsync();
break;
case 1045:
var dialog2 = new MessageDialog("Invalid username/password, please try again");
dialog2.Title = "Connection Error";
dialog2.ShowAsync();
break;
}
return false;
}
}
//Close connection
private bool CloseConnection()
{
try
{
connection.Close();
return true;
}
catch (MySqlException ex)
{
var dialog = new MessageDialog(ex.Message);
dialog.Title = "Disconnecting Error";
dialog.ShowAsync();
return false;
}
}
There are other methods that I can post if you require them. Personally, I used this Connect c# to MySql to help me develop my methods for querying the database. If you are using windows forms then this will be perfect and if you are using the Universal Windows Platform then you will require a different version of the DLL file and adjustments to your code that are not shown on the provided link.
However to answer your question correctly: The code below exists in the same DB class and the code is a function that returns a list of companies.
public List<company> getSots()
{
string query = "SELECT * FROM tbl_spot"; //Your table name here
List<Spot> dbSpots = new List<Spot>(); //List to store the gathered spots
if (this.OpenConnection() == true)
{
MySqlCommand cmd = new MySqlCommand(query, connection);
MySqlDataReader dataReader = cmd.ExecuteReader();
while (dataReader.Read())
{
//Create a new company object and populate with a row at a time
Spot x = new Spot();
x.Id = int.Parse(dataReader["id_spot"].ToString());
x.Number = int.Parse(dataReader["number"].ToString());
x.Capacity = int.Parse(dataReader["capacity"].ToString());
dbSpots.Add(x); //Add created Spot to the Spots list
}
dataReader.Close();
this.CloseConnection();
return dbCmpys; //Return the gathered db companies
}
else { return dbCmpys; }
}
As you can see the code creates a list of the object type (in your case Spot), populates it using a loop to cycle through all the database records and returns the list once completed.
To use the function: In another class or form, create a new instance of the DB class, create a list that equals the function; like so:
//Your form code/class
class SpotForm
{
private dbConnect dbConnection = new dbConnect(); //Database connection
private List<Spot> listSpots = new List<Spot>(); //Local list
// Constructor
public SpotForm()
{
listSpots = dbConnection.getSpots();
}
You now will have a list of the Spots from the database to do with as you please such as looping through to gather or manipulate the data.
Related
I'm trying to write some code to loop through a data range and add new documents to SAP based on a query input. I need the values to be added to the documents based on the supplier field and when the supplier changes create a new document. Currently I am only able to loop through adding items to the document and rather than moving to the next supplier it just loops the items again. I'm pretty new to C# so looping is pretty new to me but hoping someone can help?
int recordCount = oRecordset.RecordCount;
string Supplier = oRecordset.Fields.Item(1).Value.ToString();
string Item = oRecordset.Fields.Item(0).Value.ToString();
Qty = Convert.ToInt32(oRecordset.Fields.Item(3).Value.ToString());
if(recordCount>0)
application.MessageBox("Adding PQ");
System.Threading.Thread.Sleep(2);
{
for(int i = 0; i < recordCount; i++)
{
OPQT.CardCode = Supplier ;
OPQT.DocDate = DateTime.Now;
OPQT.DocDueDate = DateTime.Now;
OPQT.RequriedDate = DateTime.Now;
OPQT.Lines.ItemCode = Item;
OPQT.Lines.RequiredQuantity = Qty;
OPQT.Lines.Add();
oRecordset.MoveNext();
}
OPQT.Add();
application.MessageBox("PQ Added");
}
You'd be much better of starting to learn with SqlDataReader, IMO.
Adapting to your business case you'd get something like this, this is not working code, I don't have enough info for that. However this should help you progress in the right direction.
using System;
using System.Data;
using System.Data.SqlClient;
class Program
{
static void Main()
{
string connectionString = "Data Source=(local);Initial Catalog=...";
ReadOData( connectionString);
}
private static IEnumerable<OPQT> ReadOrderData(string connectionString)
{
string queryString =
#"
SELECT
T0.[ItemCode],
T0.[CardCode],
'10' [Qty]
FROM
[OSCN] T0
JOIN
[OCRD] T1
ON T0.[CardCode] = T1.[CardCode]
WHERE
T1.[CardType] ='S'";
using (SqlConnection connection =
new SqlConnection(connectionString))
{
SqlCommand command =
new SqlCommand(queryString, connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
try
{
// Call Read before accessing data.
while (reader.Read())
{
yield return ReadSingleRow((IDataRecord)reader);
}
}
finally
{
// Call Close when done reading.
reader.Close();
}
}
}
private static OPQT ReadSingleRow(IDataRecord dataRecord)
{
return new OPQT
{
Lines.ItemCode = dataRecord[0],
CardCode = dataRecord[1],
Lines.RequiredQuantity = dataRecord[2]
};
}
}
I am new to C# and this was my general idea for the C# form: user (who has the role of a Technician) would be able to view Service Requests stored in the table RequestHistory, and would be able to update the details of the request such as Description (typed in a textbox), select service status (Combobox) and make an input for the date of completion (where the user selects a date using MonthCalendar).
However, in the error codes the program states that
"A namespace cannot directly contain members such as fields or methods" and that "the name "status", "description", "collect", "requestid" does not exist in the current context" and I'm having some difficulties in understanding exactly where I went wrong.
This was how I programmed for the Update Button
(pls ignore some of the //comments in the middle, those were past codes made before editing to the current version)
private void btnUpdate_Click(object sender, EventArgs e)
{
Technician objUpdate = new Technician(mthCalCompletionDate.SelectionEnd.Day, txtboxDescription.Text, cboServiceStatus.SelectedValue);
MessageBox.Show(objUpdate.UpdateService());
}
While this code is stored in a class called 'Technician'
using System;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;
using System.Windows.Forms;
namespace ASSIGNMENT
{
//public class Technician
public class Technician
{
private string username;
private string password;
private string role;
private int requestid;
private string description;
private string status;
private int collect;
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["myCS"].ToString());
public string Description { get => description; set => description = value; }
public string Status { get => status; set => status = value; }
public int Collect { get => collect; set => collect = value; }
}
//Method to update Service Requests
public string UpdateService(string des, string stat, int dateCollect)
{
String UpdateStatus;
description = des;
status = stat;
collect = dateCollect;
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["myCS"].ToString());
con.Open();
//SqlCommand objectcmd = new Constructor(sqlQuery, connectionString)
SqlCommand cmd = new SqlCommand("update RequestHistory set Collection_Date ='" +dateCollect+ "', Description = '" + des + "', Service_Status= '" +stat+ "' where ServiceID = '" +requestid+ "' ", con);
//Update Request Descriptions, Date and Status WHERE ServiceID = RequestID textbox
//cmd.Parameters.AddWithValue("#dateCollect", mthCalCompletionDate.SelectionEnd.Day);
//cmd.Parameters.AddWithValue("#description", txtboxDescription.Text);
//cmd.Parameters.AddWithValue("#status", cboServiceStatus.SelectedValue);
cmd.ExecuteNonQuery();
int i = cmd.ExecuteNonQuery();
if (i != 0)
UpdateStatus = "Request Updated!";
else
UpdateStatus = "Unable to Update.";
con.Close();
return UpdateStatus; //If successful update = i>0
}
}
This is my first assignment using the language C#, so please forgive me for any dumb questions I may ask. Any help is much appreciated
I am trying to make an update on my MySql database, but I don't understand why it doesn't work when executing from MySqlCommand, I have the famous message "check the manual that corresponds to your MySQL server version" (I already have hundreds of queries working correctly, so I guess something is with syntax?).
edit 2 : Here is the part "near " :
'В-513',PRIORITY=1050,QUANTITY_INIT=28,QUANTITY_REMAINING=0,FICHIER='C:\\Actcut3' at line 1'
Here is the update query :
UPDATE launching_order_details SET
ID_LO=1935,
ID_CONTRACT=4228,
ID_PHASE=11765,
ID_ASS=235314,
LIST_REP_ORI='1005817//В-513//235314//В1007//11765//1//30',IS_SUBDETAIL=0,
REF_DETAIL='3201\\1\\В1007\\В-513\\',
NAME='В-513',
PRIORITY=1050,QUANTITY_INIT=28,QUANTITY_REMAINING=0,
FICHIER='C:\\Actcut3.10\\Data\\Parts\\3201\\1\\В1007\\В-513.ini' WHERE ID=27701
Of course I send it in a same line, I just splitted it here for better readability.
If I make a copy/paste of query, then execute it from phpMyadmin, all is working fine.
Edit : C# code :
DBConnect class :
public void Update(string query)
{
if (this.OpenConnection() == true)
{
if (isMySQL)
{
MySqlCommand cmd = new MySqlCommand(query.Replace("[vsteel].", ""), connection);
cmd.ExecuteNonQuery();
this.CloseConnection();
}
else
{
SqlCommand command = new SqlCommand(query, MSconnection);
command.Parameters.Add(new SqlParameter("0", 1));
//command.Connection = this.MSconnection;
command.ExecuteNonQuery();
this.CloseConnection();
}
}
}
public bool OpenConnection()
{
if (isMySQL)
{
try
{
connection.Open();
return true;
}
catch (MySqlException ex)
{
//When handling errors, you can your application's response based
//on the error number.
//The two most common error numbers when connecting are as follows:
//0: Cannot connect to server.
//1045: Invalid user name and/or password.
switch (ex.Number)
{
case 0:
MessageBox.Show("Cannot connect to server. Contact administrator");
break;
case 1045:
MessageBox.Show("Invalid username/password, please try again");
break;
}
System.Windows.Forms.Application.Exit();
Global.is_restarted = true;
return false;
}
}
else
{
try
{
MSconnection.Open();
return true;
}
catch (MySqlException ex)
{
//When handling errors, you can your application's response based
//on the error number.
//The two most common error numbers when connecting are as follows:
//0: Cannot connect to server.
//1045: Invalid user name and/or password.
switch (ex.Number)
{
case 0:
MessageBox.Show("Cannot connect to server. Contact administrator");
break;
case 1045:
MessageBox.Show("Invalid username/password, please try again");
break;
}
System.Windows.Forms.Application.Exit();
Global.is_restarted = true;
return false;
}
}
}
RepereLO class :
private void update()
{
this.listRepereOri = this.listRepereOri.OrderBy(x => x.Priority).ThenBy(x => x.ID).ToList();
DBConnect DataBase = new DBConnect();
string query = "UPDATE [vsteel].launching_order_details SET " +
"ID_LO=" + this.launchingOrder.ID + "," +
"ID_CONTRACT=" + this.contract.ID + "," +
"ID_PHASE=" + this.phase.ID + "," +
"ID_ASS=" + this.assembly.ID + "," +
"LIST_REP_ORI=\'" + convertListRepereOriToString() + "\'," +
"IS_SUBDETAIL=" + Convert.ToInt32(this.isSubRepere) + "," +
"REF_DETAIL=\'" + this.refDetail + "\'," +
"NAME=\'" + this.name + "\'," +
"PRIORITY=" + this.priority + "," +
"QUANTITY_INIT=" + this.quantity + "," +
"QUANTITY_REMAINING=" + this.remainingQuantity + "," +
"FICHIER=\'" + Global.ReplaceSpecialCharacters(this.fileName) + "\' " +
"WHERE ID=" + this.id;
DataBase.Update(query);
}
EDIT 2 : Parametirezed query
my DBConnect class
public void UpdateNew(string query, MySqlParameter[] myParamArray)
{
if (this.OpenConnection() == true)
{
using (MySqlCommand cmd = new MySqlCommand(query.Replace("[vsteel].", ""), connection))
{
for (int i = 0; i < myParamArray.Count(); i++)
{
cmd.Parameters.Add(myParamArray[i]);
}
cmd.Prepare();
cmd.ExecuteNonQuery();
}
}
}
In object :
private void update()
{
this.listRepereOri = this.listRepereOri.OrderBy(x => x.Priority).ThenBy(x => x.ID).ToList();
string query = "UPDATE [vsteel].launching_order_details SET " +
"ID_LO=#idLo," +
"ID_CONTRACT=#idContract," +
"ID_PHASE=#idPhase," +
"ID_ASS=#idAss," +
"LIST_REP_ORI=#listRepOri," +
"IS_SUBDETAIL=#isSubdetail," +
"REF_DETAIL=#refDetail," +
"NAME=#name," +
"PRIORITY=#priority," +
"QUANTITY_INIT=#qtyInit," +
"QUANTITY_REMAINING=#qtyRemaining," +
"FICHIER=#fichier" +
" WHERE ID=#id";
MySqlParameter[] listParams = new MySqlParameter[]
{
new MySqlParameter("id", this.id),
new MySqlParameter("idLo", this.launchingOrder.ID),
new MySqlParameter("idContract", this.Contract.ID),
new MySqlParameter("idPhase", this.Phase.ID),
new MySqlParameter("idAss", this.Assembly.ID),
new MySqlParameter("listRepOri", this.convertListRepereOriToString()),
new MySqlParameter("isSubdetail", this.isSubRepere),
new MySqlParameter("refDetail", this.refDetail),
new MySqlParameter("name", this.name),
new MySqlParameter("priority", this.priority),
new MySqlParameter("qtyInit", this.quantity),
new MySqlParameter("qtyRemaining", this.remainingQuantity),
new MySqlParameter("fichier", this.fileName),
};
DBConnect DataBase = new DBConnect();
DataBase.UpdateNew(query, listParams);
}
The actual problem is using string concatenation to construct a query from external input. This leaves the code wide open to SQL injection, conversion errors (what date format? decimal separator?) and ... syntax errors like this. What if Name is O'Reily for example? Or a user entered ' DROP TABLE Students; # ? No amount of escaping or replacing is going to fix the real bug - using string concatenation.
The correct way to do this is to use parameterized queries. This is actually easier than concatenating strings. If you use a library like Dapper, it's as easy as :
string sql=#"UPDATE [vsteel].launching_order_details
SET
ID_LO=#idlo,
ID_CONTRACT=#contract,
ID_PHASE=#phase,
ID_ASS=#assembly,
LIST_REP_ORI=#ori,
IS_SUBDETAIL=#isSubDetail,
REF_DETAIL=#ref,
NAME=#name,
PRIORITY=#priority,
QUANTITY_INIT=#initial,
QUANTITY_REMAINING=#remaining,
FICHIER=#path,
WHERE ID=#id";
using(var connection=new MySqlConnection(...))
{
connection.Execute(sql, new {
id,
idLo=launchingOrder.ID ,
contract=contract.ID,
....,
path=fileName});
}
Without Dapper, the code is a bit more complex but still easier and safer to write than string concatenation and trying to replace characters.
using(var connection=new MySqlConnection)
using (var cmd=new MySqlCommand(query,connection))
{
cmd.Parameters.AddWithValue("#id",this.id);
...
connection.Open();
cmd.ExecuteNonQuery();
}
BTW the DbConnect class has other issues as well. Long-lived database connections are a bug that harms performance and scalability. The locks taken during a connection remain active until it closes, which results in increased blocking for all clients. This happens even in databases with multi-version concurrency like PostgreSQL.
Connections are meant to be opened as late as possible and closed immediately after use. That's why you see all samples and tutorials create connections in a using block. This ensures the connection is close immediately after use.
ADO.NET uses connection pooling to eliminate the cost of opening a new connection, by reseting existing connections. When DbConnection.Close is called, the connection is reset and placed in a connection pool.
Tutorials
Basics of ADO.NET is a short intro to ADO.NET that explains what the various classes do and how they're used.
MySQL's Tutorial: An Introduction to Connector/NET Programming shows how to use ADO.NET with MySQL.
Microsoft's documentation on ADO.NET is almost an entire book that goes in great depth, so you should probably use it only as a reference
Dapper is a micro-ORM library that makes it very easy to map object properties to parameters and results to objects. It can be used with any ADO.NET provider, including MySQL.
With Dapper, one can write code like this :
public class Dog
{
public int? Age { get; set; }
public Guid Id { get; set; }
public string Name { get; set; }
public float? Weight { get; set; }
public int IgnoredProperty { get { return 1; } }
}
var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = #Age, Id = #Id", new { Age = (int?)null, Id = guid });
And the library will map the Age and Id properties to #Age and #Id. It will also map the Age and Id columns in the results to Dog.Age and Dog.Id
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 new to .net programming and I have a problem writing intput from a form to sql server. Only one record gets written to the database, for the other records it's says "Data not written to database.". Also my cmbbox is not updated after the data is written to the database, though I run method UpdateInitialWeek().
I don't want to write 'spaghetti code' and would love my program to be a structured one. So any advice is greatly appreciated (I already know it's better to use the Entity Framework to deal with data, something I will learn eventually ;)).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Data.SqlClient;
namespace Write_to_database
{
public partial class WriteToDatabase : Form
{
SqlServer sql = new SqlServer();
public WriteToDatabase()
{
sql.OpenSqlConnection();
InitializeComponent();
this.UpdateInitialWeek();
sql.CloseSqlConnection();
}
private void btnWrite_Click(object sender, EventArgs e)
{
WriteToOutput(sql.OpenSqlConnection());
if (txtMilitaryPress.Text != "")
WriteToOutput(sql.InsertToTraining(ConvertDate(dtMilitaryPress.Value), "Military Press", txtMilitaryPress.Text.ToString(), txtRepMilitaryPress.Text.ToString(), cmbMilitaryPress.Text.ToString()));
if (txtDeadlift.Text != "")
WriteToOutput(sql.InsertToTraining(dtDeadlift.Value.ToString(), "Deadlift", txtDeadlift.Text.ToString(), txtRepDeadlift.Text.ToString(), cmbDeadlift.Text.ToString()));
if (txtBenchPress.Text != "")
WriteToOutput(sql.InsertToTraining(dtBenchPress.Value.ToString(), "Bench Press", txtBenchPress.Text.ToString(), txtRepBenchPress.Text.ToString(), cmbBenchPress.Text.ToString()));
if (txtBackSquat.Text != "")
WriteToOutput(sql.InsertToTraining(dtBackSquat.Value.ToString(), "Back Squat", txtBackSquat.Text.ToString(), txtRepBackSquat.Text.ToString(), cmbBackSquat.Text.ToString()));
this.UpdateInitialWeek();
WriteToOutput(sql.CloseSqlConnection());
}
//Write output to textbox
public void WriteToOutput(string output)
{
this.txtOutput.AppendText(output + Environment.NewLine);
}
//Convert date for sql server
public string ConvertDate(DateTime date)
{
return date.ToString("MM/dd/yyyy");
}
//Update comboboxes to set right training week
public void UpdateInitialWeek()
{
this.cmbBackSquat.Text = CheckWeek(sql.GetDataTraining("Back Squat"));
this.cmbBenchPress.Text = CheckWeek(sql.GetDataTraining("Bench Press"));
this.cmbDeadlift.Text = CheckWeek(sql.GetDataTraining("Deadlift"));
this.cmbMilitaryPress.Text = CheckWeek(sql.GetDataTraining("Military Press"));
}
//Training week +1 except for week 4 --> back to 1
public string CheckWeek(string trainingWeek)
{
int trWeek = Int32.Parse(trainingWeek);
if (trWeek == 4)
trWeek = 1;
else
trWeek += 1;
return trWeek.ToString();
}
}
public class SqlServer
{
SqlConnection con = new SqlConnection("Data Source=WINSERVER;Initial Catalog=TRAINING;Integrated Security=SSPI;");
public string OpenSqlConnection()
{
try
{
con.Open();
return "Connection to: " + "'Data Source=WINSERVER;Initial Catalog=TRAINING;Integrated Security=SSPI;'" + " successful.";
}
catch
{
return "Connection to: " + "'Data Source=WINSERVER;Initial Catalog=TRAINING;Integrated Security=SSPI;'" + " failed.";
}
}
public string CloseSqlConnection()
{
try
{
con.Close();
return "Connection to: " + "'Data Source=WINSERVER;Initial Catalog=TRAINING;Integrated Security=SSPI;'" + " successfully closed";
}
catch
{
return "Connection to: " + "'Data Source=WINSERVER;Initial Catalog=TRAINING;Integrated Security=SSPI;'" + " not closed.";
}
}
public string InsertToTraining(string date, string lift, string weight, string reps, string week)
{
try
{
using (SqlCommand command = new SqlCommand("INSERT INTO LIFT_HISTORY VALUES(#date,#lift,#weight,#reps,#week)", con))
{
command.Parameters.Add(new SqlParameter("weight", weight.ToString())); //SqlDbType.NVarChar
command.Parameters.Add(new SqlParameter("date", date.ToString()));
command.Parameters.Add(new SqlParameter("week", week.ToString()));
command.Parameters.Add(new SqlParameter("reps", reps.ToString()));
command.Parameters.Add(new SqlParameter("lift", lift.ToString()));
command.ExecuteNonQuery();
}
return "Data successfully written to database.";
}
catch
{
return "Data not written to database.";
}
}
public string GetDataTraining(string where)
{
int trainingWeek;
//using (SqlCommand command = new SqlCommand("SELECT WEEK_OF_TRAINING FROM dbo.LIFT_HISTORY WHERE [DATE] = (SELECT MAX([DATE]) FROM dbo.LIFT_HISTORY WHERE LIFT = 'Deadlift') AND LIFT = 'Deadlift')", con))
using (SqlCommand command = new SqlCommand("SELECT WEEK_OF_TRAINING FROM dbo.LIFT_HISTORY WHERE LIFT = '"+ where +"' ORDER BY [DATE] DESC", con))
{
trainingWeek = (Int32)command.ExecuteScalar();
}
return trainingWeek.ToString();
}
}
}
There are some issues with your code, but it's ok for now that you still learning, for example:
public WriteToDatabase()
{
sql.OpenSqlConnection();
InitializeComponent();
this.UpdateInitialWeek();
sql.CloseSqlConnection();
}
should be:
public void WriteToDatabase()
{
sql.OpenSqlConnection();
InitializeComponent();
this.UpdateInitialWeek();
sql.CloseSqlConnection();
}
That's because you're not returning anything, instead of that you shoud to declare the type of variable that you're returning on.
Well first of all I'd like to suggest you to use a layer-oriented coding. For example:
I'll start crating an entity class:
namespace Entities
{
public class LiftingStory
{
public string Weight { get; set; }
public string Date { get; set; }
public string Week { get; set; }
public string Reps { get; set; }
public string Lift { get; set; }
}
}
Then you start creating "Data-Access" Layer
using System.Data;
using System.Configuration;
using Entities;
namespace DataAccess
{
public class DataLiftingStory
{
public bool insertLifting(LiftingStory obj) //correction: should be LiftingStory instead of DataLiftingStory because I'm retrieving a LiftingStory objecto to be proccesed.
{
//we're creating a new connection to Database, but it will need string parameter
//you can get it directly from the connectionstring on the Web.config in this way
// ConfigurationManager.ConnectionStrings["nameParameterOfYourConnString"].ConnectionString
//instead of that I'll do it with a string for making more easier to understand
string connectionString = "Data Source=WINSERVER;Initial Catalog=TRAINING;Integrated Security=SSPI;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
//now I'll create the command
using (SqlCommand command = new SqlCommand())
{
//so now I've to say what type of command I'm making up. In your case is "Text" because you're being explicit with the query
//I suggest you to use stored procedures btw.
command.CommandType = CommandType.Text;
//now the command text will be your query
command.CommandText = "INSERT INTO LIFT_HISTORY VALUES(#date,#lift,#weight,#reps,#week)";
//now we set the parameters
command.Parameters.Add(new SqlParameter("date", obj.Date));
command.Parameters.Add(new SqlParameter("lift", obj.Lift));
command.Parameters.Add(new SqlParameter("weight", obj.Weight));
command.Parameters.Add(new SqlParameter("reps", obj.Reps));
command.Parameters.Add(new SqlParameter("week", obj.Week));
try
{
command.Connection = connection;
command.Connection.Open();
//now we're executing the query and if we get more than 0 that will means that it inserted or modified a row
//then it will return true and going out from method.
if (command.ExecuteNonQuery() > 0)
return true;
}
catch (Exception)
{
//If it fails return false
return false;
throw;
}
finally
{
//then we close the connection
command.Connection.Close();
}
//if not failed but it didn't anything, it will return false
return false;
}
}
}
Now it's the easy part Business.
using System.Web;
using Entities;
using DataAccess;
namespace Business
{
public class BusinessLiftingStory
{
public bool insertLifting(LiftingStory obj)
{
DataLiftingStory dataLifting = new DataLiftingStory();
dataLifting.insertLifting(obj);
}
}
}
So the last step is to fill the object in the "View-layer" and call the method from Business:
LiftingStory obj = new LiftingStory();
obj.Weight = string.Empty;
obj.Date = string.Empty; //put values from comboBoxes
obj.Reps = string.Empty;
obj.Lift = string.Empty;
obj.Week = string.Empty;
BusinessLiftingStory busObj = new BusinessLiftingStory();
busObj.insertLifting(obj);
Combo boxes are not refreshing data because the DataBind() method, dont forget in the moment when you want to "redraw" your comboBox you'll have to set DataSource = null then get the datasource again and then dataBind.
use a method Init() for that if you want.
private void Init()
{
cmbWeight.DataSource = null;
cmbWeight.DataSource = //new Datasource
//dont forget to set the values and text fields
cmbWeight.DataBind();
}
In that way you'll have a order in your code, , I hope it would help you.
Greetings :)
PS: sorry for the extended answer.
maybe try printing out the exception details either to your return string or console, etc.
instead of
catch
{
return "Data not written to database.";
}
..
catch( Exception ex )
{
return "Data not written to database." + ex.Message;
}
..
https://msdn.microsoft.com/en-us/library/system.exception.message(v=vs.110).aspx