My function is saving a customer data when there is a booking request. There are 2 conditions : force to save booking even if customer data is not filled yet or save booking with completed customer data.
I do the try catch and raise the exception to user in case that the program found that this customer doesn't exist to confirm that user wants to create new customer data or not. If yes, open new form and if not, force the program to save.
Inside forcing the program to save, I want to catch other exceptions if exist.
Currently, I'm doing like this.
try
{
booking = new Booking();
booking.RoomID = roomID;
booking.Tel = txtTel.Text;
booking.PersonalID = txtId.Text;
booking.Name = txtBookBy.Text;
booking.CheckinDate = dtpCheckin.Value;
booking.CheckoutDate = dtpCheckin.Value.AddDays(Convert.ToDouble(cbNight.SelectedItem));
mng.SaveBooking(booking, false);
if (MessageBox.Show("Data is saved") == DialogResult.OK)
{
this.Close();
}
}
catch (NewCustomerException ex)
{
DialogResult dialog = MessageBox.Show("Customer doesn't exist in database. Do you want to create new customer?", "Please confirm", MessageBoxButtons.YesNo);
if (dialog == DialogResult.Yes)
{
String param = txtBookBy.Text + "," + txtTel.Text + "," + txtId.Text;
CustomerForm oForm = new CustomerForm(param);
oForm.Show();
}
else if (dialog == DialogResult.No)
{
try
{
mng.SaveBooking(booking, true);
}
catch (Exception ex1)
{
MessageBox.Show(ex1.Message);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
You never use exceptions to control the flow of your program.
The mng.SaveBooking(booking, false); should return true/false to signal the calling code that a customer doesn't exist.
Better to write a method that gives you back just this information and then use that info to write the following code
try
{
// Check if customer already registered
if(mng.CustomerExists(CustomerKey))
{
mng.SaveBooking(booking, false);
if (MessageBox.Show("Data is saved") == DialogResult.OK)
{
this.Close();
}
}
else
{
DialogResult dialog = MessageBox.Show("Customer doesn't exist in database. " +
"Do you want to create new customer?", "Please confirm", MessageBoxButtons.YesNo);
if (dialog == DialogResult.Yes)
{
.....
}
else
{
.....
}
}
}
catch(Exception ex)
{
... somthing unexpected here
}
Make separate try catch block so that every try catch block follow single responsibility. In catch block you can manage the statement for which it throw exception and you can use that manage statement result in the next try catch block.
try {
booking = new Booking();
booking.RoomID = roomID;
booking.Tel = txtTel.Text;
booking.PersonalID = txtId.Text;
booking.Name = txtBookBy.Text;
booking.CheckinDate = dtpCheckin.Value;
booking.CheckoutDate = dtpCheckin.Value.AddDays(Convert.ToDouble(cbNight.SelectedItem));
mng.SaveBooking(booking, false);
if (MessageBox.Show("Data is saved") == DialogResult.OK) {
this.Close();
}
}
catch (NewCustomerException ex) {
DialogResult dialog = MessageBox.Show("Customer doesn't exist in database. Do you want to create new customer?", "Please confirm", MessageBoxButtons.YesNo);
}
if (dialog == DialogResult.Yes) {
String param = txtBookBy.Text + "," + txtTel.Text + "," + txtId.Text;
CustomerForm oForm = new CustomerForm(param);
oForm.Show();
}
else if (dialog == DialogResult.No) {
try {
mng.SaveBooking(booking, true);
}
catch (Exception ex1) {
MessageBox.Show(ex1.Message);
}
}
}
Related
I was working on my windows form program, and i saw that the login function (linked to a simple button) freeze my application. I searched on internet and i found how to create a task, but i'm not sure about how it works ...
That's my login function, how can i correctly translate it into a task?
string sURL = url + "/login";
string result = null;
await Task.Run(() =>
{
try
{
result = Web_api.MakeRequest("POST", sURL); //return null if there is some error
}
catch(Exception ex)
{
Debug.WriteLine("[frmLogin] --> result: " + result);
}
});
if(result != null)
{
try
{
Login_response accepted = JsonConvert.DeserializeObject<Login_response>(result);
Debug.WriteLine("[frm_Login] --> accepted: " + accepted);
if (accepted.login)
{
//throw new Exception();
Debug.WriteLine("[frm_login]: result " + result);
frmMain frm = new frmMain(); //calling the new form
frm.Show(); //new form is show-up
this.Hide(); //log-in form hide
frm.FormClosed += Frm_FormClosed; //close the form
}
}
//if server is down, or the id or password is wrong
catch (Exception ex)
{
lblLoginError.Visible = true; //pop-up the error label
pbLogin.Visible = false; //hide the progress-bar
this.Style = MetroFramework.MetroColorStyle.Red; //changing the color of the form
Debug.WriteLine("Exception: " + ex);
}
}
else
{
lblLoginError.Visible = true; //pop-up the error label
pbLogin.Visible = false; //hide the progress-bar
this.Style = MetroFramework.MetroColorStyle.Red; //changing the color of the form
}
EDIT: i provided a real (and working) soluction and i followed all the suggestion in the comments ... do you think this could be acceptable?
Execute any potentially long-running code on a background thread using a Task:
private async void btnLogin_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(txtUser.Text.Trim()) || string.IsNullOrEmpty(txtPassword.Text.Trim()))
{
MessageBox.Show("You must insert a valid user/password format", "Login Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
//Progress bar start
pbLogin.Visible = true; // BUT THIS PROGRESS BAR I STACK DUE TO STATIC DEFINITON OF LOGIN
//Getting ID + Password
User.username = txtUser.Text;
User.password = txtPassword.Text;
string sURL = Web_api.url + "/login";
try
{
Login_response accepted = await Task.Run(() =>
{
//the following code gets executed on a background thread...
string result = Web_api.MakeRequest("POST", sURL);
Login_response accepted = JsonConvert.DeserializeObject<Login_response>(result);
Debug.WriteLine("[frm_Login] --> accepted: " + accepted);
return accepted;
});
//...and here you are back on the UI thread once the task has completed
if (accepted.login)
{
//throw new Exception();
Debug.WriteLine("[frm_login]: result " + result);
frmMain frm = new frmMain(); //calling the new form
frm.Show(); //new form is show-up
this.Hide(); //log-in form hide
frm.FormClosed += Frm_FormClosed; //close the form
}
}
//if server is down, or the id or password is wrong
catch (Exception ex)
{
lblLoginError.Visible = true; //pop-up the error label
pbLogin.Visible = false; //hide the progress-bar
this.Style = MetroFramework.MetroColorStyle.Red; //changing the color of the form
Debug.WriteLine("Exception: " + ex);
}
}
Event handlers always return void. They are an exception to the rule that says that an async method always should return a Task or a Task<T>.
You can create an async void method. It is actually the correct way to implement async callbacks for events such as button click.
First, let's make an asynchronous login method :
public async Task LoginAsync()
{
try
{
var stream = await _http.GetStreamAsync($"{baseUrl}/login");
var response = await JsonSerializer.DeserializeAsync<LoginResponse>(stream);
if (!response.login)
{
throw new BusinessException<LoginError>(LoginError.LoginDenied);
}
}
catch (HttpRequestException ex)
{
throw new BusinessException<LoginError>(LoginError.LoginFailed, ex);
}
}
Now we implement an asynchronous button callback:
private async void btnLogin_Click(object sender, EventArgs e)
{
try
{
await authentication.LoginAsync().ConfigureAwait(false);
// handle login success here
}
catch (BusinessException<LoginError> ex) when (ex.Error == LoginError.LoginDenied)
{
// handle login denied here
}
catch (BusinessException<LoginError> ex) when (ex.Error == LoginError.LoginFailed)
{
// handle connection failed here
}
}
If you want the LoginAsync() method to do some UI operations (for instance showing a form), you will need to use ConfigureAwait(true). Otherwise, there is a risk that part of the method is executed on a different thread and UI operations will fail.
I came across the following problem:
I have a DataGridView with 5 Column where the first 3 are ComboBoxColumns. So i want that a User has to select the first ComboBox so that on the second will be loaded the data (SQL Query with value of first ComboBox). An so on also for the third one which is only working when the first two are set.
Now i have the problem. If I check if selected Index change via the code below. It gives an error because on the second ComboBox i dont need .SubString(). So i wanted to check the names of the ComboBoxes and make some if branches to check wether is the first, second or third ComboBox. But all of my tries give me an empty return.
Is there a possibility to solve my problem?
private void datagridview1_detailed_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cb = e.Control as ComboBox;
if (cb != null)
{
cb.SelectedIndexChanged += new EventHandler(selectionchange);
}
}
void selectionchange(object sender, EventArgs e)
{
try
{
ComboBox cb = (ComboBox)sender;
//To Test the output
Console.WriteLine("CB_NAME:" + ((ComboBox)sender).Tag);
Console.WriteLine("CB_NAME:" + ((ComboBox)sender).Name);
Console.WriteLine("CB_NAME:" + cb.Name);
if (cb.Name == "datagridview1_value1")
{
order_id = cb.Text.Substring(0, 6);
fill_second_combo();
}
else if (cb.Name == "datagridview1_value2")
{
fill_third_combo();
}
}
catch {
Console.WriteLine("Error");
}
}
Fill second ComboBox Code:
private void fill_second_combo()
{
string rcs = db_conn.connection();
using (var OraConn = new OracleConnection(rcs))
{
using (var OraCmd = OraConn.CreateCommand())
{
try
{
OraConn.Open();
OraCmd.BindByName = true;
OraCmd.CommandText = "select * from xxx where order_id" + order_id +";
OracleDataReader OraDataReader = OraCmd.ExecuteReader();
if (OraDataReader.Read() == false)
{
MessageBox.Show("No data!", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
using (var OraDat = new OracleDataAdapter(OraCmd))
{
using (var combo2_table = new DataTable())
{
OraDat.Fill(combo2_table );
foreach (DataRow combo2_row in combo2_table .Rows)
{
foreach (DataColumn combo2 in combo2_table .Columns)
{
if (combo2_row[combo2 ] != null)
{
if (combo2.ColumnName == "Second Value")
{
secondComboBox.Items.Add(combo2_row[combo2.ColumnName].ToString());
}
}
}
}
}
}
}
}
catch (OracleException ex)
{
switch (ex.Number)
{
case 1:
MessageBox.Show("F1 -DB Error", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case 12560:
MessageBox.Show("F2 - DB Error", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
default:
MessageBox.Show("F3 - DB Error: " + ex.Message.ToString(), "Fehlermeldung", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
OraConn.Dispose();
}
}
}
}
All of the Test-Outputs are empty in my case.
Our application requires that a witness must authenticate before a logged in user can perform enrollment operations (Enroll and Delete).
This is not an issue for Enrolling as I can add a check (IsWitnessApproved) to an enrollment_OnStartEnroll method I.E. before the Capture method is called and fired.
However, this is not possible for Deletion as I don't have access to a point where the enrollment_OnDelete method hasn't fired.
I haven't been able to get a response to this issue from Digital Persona so I'm now looking at work-arounds.
I'm exploring if its possible to open up a new form (WitnessApproval) inside the enrollment_OnDelete method, approve the witness in the form (btnConfirmWitness_Click) and then come back into the method and continue on with the deletion?
enrollment_OnDelete method:
private void enrollment_OnDelete(DPCtlUruNet.EnrollmentControl enrollmentControl, Constants.ResultCode result, int fingerPosition)
{
if (!witnessApproved)
{
WitnessApproval witnessApproval = new WitnessApproval();
witnessApproval.Show();
}
else
{
int fingerMask = GetFingerMask(fingerPosition);
if (enrollmentControl.Reader != null)
{
try
{
// Delete from database
new EnrollmentDAL().DeleteEnrolledFingerprint(Settings.Default.Username, fingerMask, txt_WitnessName.Text);
MessageBox.Show("Fingerprint deleted.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information);
pbFingerprint.Image = null;
pbFingerprint.Visible = false;
btnCancel.Visible = false;
witnessApproved = false;
txt_WitnessName.Text = String.Empty;
txt_WitnessPassword.Text = String.Empty;
}
catch (Exception ex)
{
MessageBox.Show("There was a problem deleting the fingerprint.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Error);
new Util().LogError(ex);
}
}
else
{
MessageBox.Show("No Reader Connected.", "Message", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
_sender.Fmds.Remove(fingerPosition);
}
}
Selected WitnessApproval methods:
private void btnConfirmWitness_Click(object sender, EventArgs e)
{
lbl_Validation.Visible = false;
if (txt_WitnessName.Text == String.Empty)
{
SetMessage("Please enter a Witness.");
return;
}
if (txt_WitnessPassword.Text == String.Empty)
{
SetMessage("Please enter a Password.");
return;
}
if (txt_WitnessName.Text == Settings.Default.Username)
{
SetMessage("User and witness cannot be the same.");
return;
}
bool IsValidate = Membership.ValidateUser(txt_WitnessName.Text, txt_WitnessPassword.Text);
Settings.Default.WitnessName = txt_WitnessName.Text;
Settings.Default.WitnessPassword = txt_WitnessPassword.Text;
if (IsValidate)
{
this.Close();
// Allow enrollment operations
}
else
{
SetMessage("Witness credentials invalid.");
}
}
private void btnCancelWitness_Click(object sender, EventArgs e)
{
this.Close();
// DO NOT Allow enrollment operations
witnessCancelled = true;
}
private void SetMessage(string message)
{
lbl_Validation.Visible = true;
lbl_Validation.Text = message;
}
How to open form inside method, submit button and then come back to original method and continue?
There is ShowDialog method for this purposes.
Here is usage example from MSDN:
public void ShowMyDialogBox()
{
Form2 testDialog = new Form2();
// Show testDialog as a modal dialog and determine if DialogResult = OK.
if (testDialog.ShowDialog(this) == DialogResult.OK)
{
// Read the contents of testDialog's TextBox.
this.txtResult.Text = testDialog.TextBox1.Text;
}
else
{
this.txtResult.Text = "Cancelled";
}
testDialog.Dispose();
}
In your case, Form2 is WitnessApproval.
In WitnessApproval Form button handlers you will also need to set DialogResult to true when the witness is approved or to false when user cancelled operation.
i want to implement a transaction in my code which is separated by functions.
There is one function in which all other functions are called. I want to make this function execute the function calls in a transaction (All or None).
Some of the function calls are queries while some are routine code for C# and Crystal report.
Can Some one help with this?
This function is working perfectly.
But if 2 users are accessing same database and executing this function at same time may get Cuncurrency problem.
the function code is attached.
public bool Generate_PDF(decimal wo_id)
{
if (Fill_WO_Report_Table(wo_id) == false) // this function has queries
return false;
try
{
string exception;
cryRpt = new ReportDocument();
cryRpt.Load(Application.StartupPath + "\\CrRpt_WO_Report.rpt");
DB_Interface.CrystalReport_Login(ref cryRpt, out exception);
string Temp_file_path = #"c:\Reports\WO_Temp.pdf";
cryRpt.ExportToDisk(ExportFormatType.PortableDocFormat, Temp_file_path);
string File_name = str_WO_File_Name + ".pdf";
// option to print at which location
//DialogResult dlg = MessageBox.Show("Select Option For Print :\n\nYes - Print To Default Path\nNo - Print To Custom Path\nCancel - Do Not Print",
// "Print GRN", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Information);
SaveToDialog frm = new SaveToDialog(ref File_name);
DialogResult dlg = frm.ShowDialog();
File_name = frm.GetString();
frm.Dispose();
frm = null;
if (dlg == DialogResult.Yes)
{
if (Convert.ToString(MMS_Vars.PathReport_WO) == "")
throw new Exception("Default Path Not Available.");
string Full_File_name = Convert.ToString(MMS_Vars.PathReport_WO) + "\\" + File_name;
cryRpt.ExportToDisk(ExportFormatType.PortableDocFormat, Full_File_name);
System.Diagnostics.Process.Start(Full_File_name);
}
else if (dlg == DialogResult.No)
{
SaveFileDialog obj_saveFileDialog = new SaveFileDialog();
obj_saveFileDialog.InitialDirectory = Convert.ToString(MMS_Vars.PathReport_WO);
//obj_saveFileDialog.RestoreDirectory = true;
obj_saveFileDialog.FileName = File_name; //set file name to
obj_saveFileDialog.Filter = "PDF Files|*.pdf";
DialogResult result = obj_saveFileDialog.ShowDialog();
if (result == DialogResult.OK && obj_saveFileDialog.FileName != "")
{
cryRpt.ExportToDisk(ExportFormatType.PortableDocFormat, obj_saveFileDialog.FileName);
System.Diagnostics.Process.Start(obj_saveFileDialog.FileName);
}
else if (result == DialogResult.Cancel)
{
obj_saveFileDialog.Dispose();
cryRpt.Close();
cryRpt.Dispose();
return false;
}
obj_saveFileDialog.Dispose();
}
else if (dlg == DialogResult.Cancel)
{
cryRpt.Close();
cryRpt.Dispose();
return false;
}
cryRpt.Close();
cryRpt.Dispose();
// Drop Report tables
if (Drop_WO_Report_Table() == false) // this function has queries
return false;
return true;
}
catch (Exception exe)
{
MessageBox.Show(exe.Message, "WO Report", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}
You haven't posted any of the functions you do on your db. Are there any writes, etc? From your posted code, adding a transaction will probably not solve your problem. A simple solution can be to add a lock around the function so only one pdf is created at a time:
private static object locker = new object();
public bool Generate_PDF(decimal wo_id)
{
lock(locker){
if (Fill_WO_Report_Table(wo_id) == false) // this function has queries
return false;
........
........
return true;
}
catch (Exception exe)
{
MessageBox.Show(exe.Message, "WO Report", MessageBoxButtons.OK, MessageBoxIcon.Error);
return false;
}
}//END LOCK
}
I have to validate the textboxes to check if they are a number and if they are in the database. Only problem is I can only seem to get it to check for validity or if they are numeric. How can change this to get both validations?
it validates to make sure there is something in the textboxes:
if (string.IsNullOrEmpty(employeeIDTextBox.Text) && (string.IsNullOrEmpty(JobIDTextBox.Text)))
Then it looks to see if the value is numeric, then if it does it checks to see if the person exists, then sets the values
else if (string.IsNullOrEmpty(JobIDTextBox.Text))
{
if (!Int32.TryParse(JobIDTextBox.Text, out number1))
{
using (dbConn)
{
ReportGrid newGrid = new ReportGrid();
if (newGrid.isValidEmp(Int32TryParseSafe(employeeIDTextBox.Text)))
{
newGrid.startDate = startingdateTimePicker.Value;
newGrid.endDate = endingdateTimePicker.Value;
newGrid.EmployeeID = Int32TryParseSafe(employeeIDTextBox.Text);
newGrid.JobID = Int32TryParseSafe(JobIDTextBox.Text);
newGrid.ShowDialog();
}
else
{
MessageBox.Show("No ID found for the employee.", "Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
Then it does the same for the other textbox
else if (string.IsNullOrEmpty(employeeIDTextBox.Text))
{
if (!Int32.TryParse(emplyeeIDLabel.Text, out number2))
{
using (dbConn)
{
ReportGrid newGrid = new ReportGrid();
if (newGrid.isValidJob(Int32TryParseSafe(JobIDTextBox.Text)))
{
newGrid.startDate = startingdateTimePicker.Value;
newGrid.endDate = endingdateTimePicker.Value;
newGrid.EmployeeID = Int32TryParseSafe(employeeIDTextBox.Text);
newGrid.JobID = Int32TryParseSafe(JobIDTextBox.Text);
newGrid.ShowDialog();
}
else
{
MessageBox.Show("No ID found for that job.", "Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
else
MessageBox.Show("Must be a number.");
}
Here is the whole code
try
{
if (string.IsNullOrEmpty(employeeIDTextBox.Text) && (string.IsNullOrEmpty(JobIDTextBox.Text)))
{
MessageBox.Show("Please enter a EmployeeID or JobID.", "Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else if (string.IsNullOrEmpty(JobIDTextBox.Text))
{
if (!Int32.TryParse(JobIDTextBox.Text, out number1))
{
using (dbConn)
{
ReportGrid newGrid = new ReportGrid();
if (newGrid.isValidEmp(Int32TryParseSafe(employeeIDTextBox.Text)))
{
newGrid.startDate = startingdateTimePicker.Value;
newGrid.endDate = endingdateTimePicker.Value;
newGrid.EmployeeID = Int32TryParseSafe(employeeIDTextBox.Text);
newGrid.JobID = Int32TryParseSafe(JobIDTextBox.Text);
newGrid.ShowDialog();
}
else
{
MessageBox.Show("No ID found for the employee.", "Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
else
MessageBox.Show("Must be a number.");
if (startingdateTimePicker.Value > endingdateTimePicker.Value)
{
MessageBox.Show("Starting data can not be after than ending date.", "Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
else if (string.IsNullOrEmpty(employeeIDTextBox.Text))
{
if (!Int32.TryParse(emplyeeIDLabel.Text, out number2))
{
using (dbConn)
{
ReportGrid newGrid = new ReportGrid();
if (newGrid.isValidJob(Int32TryParseSafe(JobIDTextBox.Text)))
{
newGrid.startDate = startingdateTimePicker.Value;
newGrid.endDate = endingdateTimePicker.Value;
newGrid.EmployeeID = Int32TryParseSafe(employeeIDTextBox.Text);
newGrid.JobID = Int32TryParseSafe(JobIDTextBox.Text);
newGrid.ShowDialog();
}
else
{
MessageBox.Show("No ID found for that job.", "Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
else
MessageBox.Show("Must be a number.");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
I could be mistaken but is this line possibly the problem?
if (!Int32.TryParse(emplyeeIDLabel.Text, out number2))
The exclamation point reverses the bool so if the text successfully parses as a number the TryParse function returns true but by using the exclamation the if statement resolves to false. Therefore you are sending the code to else statement which states that it is not a number.
Also, try using "Return" to avoid nested ifs.
if (string.IsNullOrEmpty(employeeIDTextBox.Text) && (string.IsNullOrEmpty(JobIDTextBox.Text)))
{
MessageBox.Show("Please enter a EmployeeID or JobID.", "Alert", MessageBoxButtons.OK, MessageBoxIcon.Warning);
Return;
}
There's no need for an else at this point because the if the if statement resolves to true you will return from the method.
Nested if statements are frequently necessary but they should be avoided when they can to make code clearer to read for maintenance.