Best way to handle error catching - c#

I am creating an app which does some SQL server config, which part of a bigger system
There is a config table in the database of the system as follows:
CREATE TABLE Config
(
ConfigItem NVARCHAR(255) PRIMARY KEY NOT NULL,
ConfigValue NVARCHAR(255) NOT NULL
)
INSERT INTO Config
VALUES
('LinkedServerName','MYLINKEDSERVER'),
('DatabaseName','APPLICATIONDATABASE')
My app is a Windows form with two textboxes and a button. The form also has an initially blank label which is used to display error messages to the user.
In the first text box, the value for the linked server name is shown, in the second, the value for the database is shown. Both are shown on form load.
On clicking the submit button, the two values are updated in the database based on what is in the text boxes.
I have the following code to populate the two textboxes with current values at form load:
private void Form1_Load(object sender, EventArgs e)
{
// populate the textboxes
txtLinkedServer.Text = GetConfigValue("LinkedServerName");
txtDatabase.Text = GetConfigValue("DatabaseName");
}
private string GetConfigValue(string ConfigItem)
{
// get the value for the given config item from the database
using (SqlConnection conn = new SqlConnection(connectionString))
{
DataTable dt = new DataTable();
SqlCommand com = new SqlCommand();
com.CommandText = "SELECT ConfigValue FROM Config WHERE ConfigItem = #ConfigItem";
com.Parameters.AddWithValue("ConfigItem", ConfigItem);
com.Connection = conn;
try
{
conn.Open();
dt.Load(com.ExecuteReader());
if (dt.Rows.Count == 0)
{
return "Error retrieving " + ConfigItem + " name from config table";
}
else
{
return dt.Rows[0]["ConfigValue"].ToString();
}
}
catch
{
return "Error in GetConfigValueMethod when retrieving " + ConfigItem;
}
finally
{
conn.Close();
}
}
}
If there is a problem with retrieving the config data (caught by the catch block in GetConfigValue) I want the label to show the string returned from GetConfigValue.
What is the best / neatest way to do this? I was thinking
private void Form1_Load(object sender, EventArgs e)
{
string message;
// populate the textboxes
try
{
message = GetConfigValue("LinkedServerName");
txtLinkedServer.Text = message
}
catch
{
lblFeedback.Text = message;
}
// do the same for the database here
}
however, I cannot do that as I get
Use of unassigned local variable 'Message'
Or am i best to change the GetConfigValue method so that it throws it's own exception in the catch block rather than returning a string and catching that in the Load method as follows;
private string GetConfigValue(string ConfigItem)
{
// get the value for the given config item from the database
using (SqlConnection conn = new SqlConnection(connectionString))
{
// same code here
try
{
// same code here
}
catch
{
Throw new Exception ("Error in GetConfigValueMethod when retrieving " + ConfigItem);
}
finally
{
conn.Close();
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
// populate the textboxes
try
{
txtLinkedServer.Text = GetConfigValue("LinkedServerName");
}
catch (Exception e)
{
lblFeedback.Text = e.Message;
}
// do the same for the database here
}
Or some other way completely?

Looking at your second example, if that's the result you want, then it looks like you just need to replace
catch
{
lblFeedback.Text = message;
}
in your first example with
catch (Exception e)
{
lblFeedback.Text = e.Message;
}
from your second example.

As error message says you tried to use unassigned variable 'message' and because of that you were getting that error.
Try this:
private void Form1_Load(object sender, EventArgs e)
{
string message = String.Empty;
// populate the textboxes
try
{
message = GetConfigValue("LinkedServerName");
txtLinkedServer.Text = message
}
catch (Exception ex)
{
if (!String.IsNullOrEmpty(message))
lblFeedback.Text = message;
else
lblFeedback.Text = ex.Message;
}
// do the same for the database here
}

Related

Receiving "Index Out of Range Exception" when trying to pull data from a database

I'm new to backend development and trying to pull quotes for a testimonial page. Currently my code is resulting in the following error message on my site:
System.IndexOutOfRangeException: Blurb at
System.Data.ProviderBase.FieldNameLookup.GetOrdinal(String fieldName) at System.Data.SqlClient.SqlDataReader.GetOrdinal(String name) at System.Data.SqlClient.SqlDataReader.get_Item(String name) at
testimonial_Default.Page_Load(Object sender, EventArgs e) in e:\UserFiles\testimonial\testimonial.aspx.cs:line 49
Here is the code that's resulting in this
protected void Page_Load(object sender, EventArgs e)
{
//BlurbID = Session["Blurb"];
Page.Title = "Testimonials";
try
{
sqlConnectionStr.Open();
SqlCommand getBlurb = new SqlCommand(#"SELECT b.BlurbText, b.BlurbID
FROM TestimonialBlurb b ", sqlConnectionStr);
getBlurb.Parameters.Add("#BlurbID", SqlDbType.Int).Value = 1;
getBlurb.Parameters.Add("#BlurbText", SqlDbType.VarChar, 255).Value = "This is a Blurb";
using (SqlDataReader blurbReader = getBlurb.ExecuteReader())
{
while (blurbReader.Read())
{
blurbPH.Controls.Add(new Literal
{
Text = blurbReader["BlurbText"].ToString() + "<strong>" + blurbReader["Blurb"].ToString() + "</strong>"
});
if (blurbPH.Controls.Count == 0)
{
Response.Write("There are currently no testimonials available.");
}
}
}
}
catch (Exception ex)
{
blurbPH.Controls.Add(new Literal
{
Text = ex.ToString()
});
}
finally
{
sqlConnectionStr.Close();
}
}
}
I'm thinking I need perhaps some sort of DECLARE statement at the beginning of my SQL query -- if anyone could point me in the right direction I would greatly appreciate it.
This is your sql query:
SELECT b.BlurbText, b.BlurbID
FROM TestimonialBlurb b
So you have two columns BlurbText and BlurbID. But you ask for column Blurb:
blurbReader["Blurb"].ToString()
So maybe you want to replace this with BlurbID. Apart from that, why you add two sql-parameters at all if you have a select query without parameters?

how to update image in a different form c#

Beginner here, I'm trying to update the image and the image's name from a different form and it's not updating. Also it doesn't give any errors. Is there something wrong in the code?
Form 2. this is where i update
private void btnStockEdit_Click_1(object sender, EventArgs e)
{
try
{
sqlCon.Open();
string qry = "Update SMStocksTb Set SmStockImgName=#SmStockImgName,SmStockImage=#SmStockImage where SmStockId=#SmStockId";
SqlCommand cmd = new SqlCommand(qry, sqlCon);
cmd.Parameters.AddWithValue("#SmStockId", SmStockId);
cmd.Parameters.AddWithValue("#SmStockImgName", txtUPImgName.Text);
cmd.Parameters.AddWithValue("#SmStockImage", Savephoto());
cmd.ExecuteNonQuery();
sqlCon.Close();
MessageBox.Show("Update Successfully","Updated",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private byte[] Savephoto()
{
MemoryStream ms = new MemoryStream();
pbxUpdateImg.Image.Save(ms, pbxUpdateImg.Image.RawFormat);
return ms.GetBuffer();
}
**Form1 ** this is where i open form 2 after selecting a row in datagrid
private void btnStockEdit_Click(object sender, EventArgs e)
{
SMStockUpdateForm cfrmStockUpdateForm = new SMStockUpdateForm();
try
{
if (StockListDG.CurrentRow.Index != -1)
{
SmStockId = Convert.ToInt32(StockListDG.CurrentRow.Cells[0].Value.ToString());
cfrmStockUpdateForm.txtUPImgName.Text = StockListDG.CurrentRow.Cells[11].Value.ToString();
byte[] ImageArray = (byte[])StockListDG.CurrentRow.Cells[12].Value;
if (ImageArray.Length == 0)
cfrmStockUpdateForm.pbxUpdateImg.Image = DefaultImage;
else
{
ImageByteArray = ImageArray;
cfrmStockUpdateForm.pbxUpdateImg.Image = Image.FromStream(new MemoryStream(ImageArray));
}
}
}
catch (Exception ex)
{
}
cfrmStockUpdateForm.ShowDialog(this);
if (isWindowOpen == false)
{
this.ParentForm.Opacity = 100;
}
}
You should learn how to send data between two forms. From form1 you should only send the name of image and in form two should bind it according to its name you received from form1

No row at position (-1) - SQL Server when trying to save DataTable changes

I have a Windows Forms data entry applet for entering data into a small SQL Server database. I keep seeing this error when trying to save my new record after clicking AddNewItem button on the binding navigator component.
My code on clicking the save button on the binding navigator looks like this:
private void btnSave_Click(object sender, EventArgs e)
{
try
{
this.Validate();
int currentPosition = this.witsStatusDBDataSet.TestCase.Rows.Count - 1;
WitsStatusDBDataSet.TestCaseRow row = (WitsStatusDBDataSet.TestCaseRow)witsStatusDBDataSet.TestCase.Rows[currentPosition];
row.AcceptChanges();
witsStatusDBDataSet.TestCase.AcceptChanges();
this.testCaseBindingSource.EndEdit();
int current = witsStatusDBDataSet.TestCase.Rows.Count - 1;
testCaseTableAdapter.Update(this.witsStatusDBDataSet.TestCase.Rows[current]);
WitsStatusDBEntry.WitsStatusDBDataSetTableAdapters.TableAdapterManager manager = new TableAdapterManager();
manager.UpdateAll(witsStatusDBDataSet);
SysTimer = new System.Timers.Timer(2500);
statusLabel1.Text = "Updated successfully.";
SysTimer.Start();
}
catch(Exception exc)
{
string msg = exc.Message + " : " + exc.StackTrace;
Clipboard.SetText(msg);
MessageBox.Show(msg);
}
}
If I enter the data manually in SQL Server Mgmt Studio, the binding navigator successfully loads it and I can use Move Next and Move Previous successfully.
But if I have a brand-new database that has just been deployed, with no records, I get this error.
I checked StackOverflow for similar issues, but nothing seemed to be the same situation.
I re-coded the method, based on mason's comment. Here is the working code:
private void btnSave_Click(object sender, EventArgs e)
{
try
{
WitsStatusDBDataSet.TestCaseRow row = null;
this.Validate();
int currentPosition = this.witsStatusDBDataSet.TestCase.Rows.Count - 1;
if(currentPosition == -1)
{
row = AddRowToDataTable(testCase: witsStatusDBDataSet.TestCase);
}
if (row == null)
{
currentPosition = this.witsStatusDBDataSet.TestCase.Rows.Count - 1;
}
row.AcceptChanges();
witsStatusDBDataSet.TestCase.AcceptChanges();
this.testCaseBindingSource.EndEdit();
testCaseTableAdapter.Update(row);
testCaseTableAdapter.InsertCase(row.Title, row.IsAutomated, row.Description, row.State, row.Area, row.Iteration, row.Priority,
row.Severity, row.Owner, row.CreatedDate, row.ModifiedDate, row.TFS_Case_ID, row.TFSInstance);
WitsStatusDBEntry.WitsStatusDBDataSetTableAdapters.TableAdapterManager manager = new TableAdapterManager();
manager.Connection = new SqlConnection(#"Data Source=.\SQLEXPRESS;Initial Catalog=WitsStatusDB;Integrated Security=True");
manager.UpdateAll(witsStatusDBDataSet);
SysTimer = new System.Timers.Timer(2500);
statusLabel1.Text = "Updated successfully.";
SysTimer.Start();
}
catch(Exception exc)
{
string msg = exc.Message + " : " + exc.StackTrace;
Clipboard.SetText(msg);
MessageBox.Show(msg);
}
}
Notice I had to call "AddRowToDataDable()" - that method simply transfers the contents of the Windows Form to the TestCaseRow object.

Splitting data access and catching data to form

In my project I'm trying to write code that will be nice to understand.
I currently split my data access functions in a seperate class.
What I'm trying to achieve however, is to catch the errors back to my form. I am not getting this currently and I was wondering why.
In my form I have the following code:
private void btn_Save_ItemClick(object sender, ItemClickEventArgs e)
{
if (dal.updatePerson(ObjectAfterSaving))
{
MessageBox.Show("Updated!");
}
else
{
MessageBox.Show("error");
};
}
In my dal object (derived from the DataAccess_Person class), I have the following method:
public bool updatePerson(Person p)
{
conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["Database"].ConnectionString);
SqlCommand command = new SqlCommand(#"UPDATE Person
SET PersonName = #PersonName
WHERE PersonID = #PersonID", conn);
command.Parameters.Add("#PersonName", SqlDbType.VarChar).Value = p.Name
{
try
{
if (conn.State == ConnectionState.Closed)
{
conn.Open();
}
int a = command.ExecuteNonQuery();
conn.Close();
if (a > 0)
{
return true;
}
else
{
return false;
}
}
catch (SqlException ex)
{
ex.ToString();
return false;
}
}
}
My question is: let's say if my method falls in the catch. Will my front end (form) show it (Sql Exception for example) ? Or will i just get 'error' ? And If I will just get error, how I can improve my code to show the Exception instead of error?
A simple way is to remove the try catch from your DAL and add it to the form. For example:
private void btn_Save_ItemClick(object sender, ItemClickEventArgs e)
{
var result = "Success";
try
{
dal.updatePerson(ObjectAfterSaving);
}
catch (SqlException sqlEx)
{
result = sqlEx.Message;
}
catch (Exception ex)
{
result = ex.Message;
}
MessageBox.Show(result);
}
Just note that there's a lot of ways you can do this. My preference is to not include DAL specific exception types in my UI. Instead I may return a custom result type that has an errorcode and message and let my UI display that or generate a custom message based on the error code.
You‘ll just get „error“ in case of a SqlException. All other exceptions will crash your program if you don‘t have a global exception handler. If you want to show the error message you could introduce an out variable for the error message:
bool successful = MyMethod(out string errorMessage)
if (!successful)
{
MessageBox.Show(errorMessage);
}
public bool MyMethod(out string errorMessage)
{
errorMessage = "";
try
{
// do some stuff
return true;
}
catch(Exception ex)
{
errorMessage = ex.Message;
return false;
}
}

How to handle SQL Exception for Logon failure

I have a windows form that presents a combo box for the user to select a geographical region and then sets SQL Connections based on the selection and executes a SQL Command. There is always a good chance the user doesn't have access to the SQL Server. I set up a try/catch and display the error message to the user but don't really want to break and I'm new to VS C# and am asking for guidance on how to pass control to a point the user can adjust by making a different selection.
Would it be reasonable to pass execution back to the form load? If yes, how do I do that? If no, how should it be handled?
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex > 0)
{
List<String> distinctTableList = AttributeMap.DistinctTablesList(comboBox1.SelectedItem.ToString());
lbTableNames.DataSource = distinctTableList;
}
}
public static List<String> DistinctTablesList(String environment)
{
List<String> tables = new List<string>();
var config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
AppSettingsSection appSettingSection = (AppSettingsSection)config.GetSection("cbSettings");
SqlConnection sqlcon = new SqlConnection(appSettingSection.Settings[environment].Value);
using (sqlcon)
{
StringBuilder errorMessages = new StringBuilder();
using (sqlcon)
{
try
{
sqlcon.Open();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
}
public partial class frmClassBuilder : Form
{
private List<AttributeMap> attributeMapList;
private CacheClassFactory cacheFactory;
public frmClassBuilder()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
List<String> environmentList = AttributeMap.EnvironmentList();
comboBox1.DataSource = environmentList;
}
=============================================================
using (sqlcon)
{
try
{
sqlcon.Open();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
}
}
I have used the following approach in one of enterprise desktop clients and it is still used.
Assuming you have combobox_SelectedIndexChanged() method, it should look like this:
public void combobox_SelectedIndexChanged()
{
string selectedCountry = "country"; //build actual connection string as you do now.
string connectionString = string.Format("Data Source={0};Initial Catalog=Detrack;Integrated Security=True;", selectedCountry);
var sqlCon = new SqlConnection(connectionString);
using (sqlCon)
{
// Disable some controls
try
{
sqlCon.Open();
}
catch (SqlException ex)
{
MessageBox.Show(ex.Message);
// Disable "OK"/"Next" button
return;
}
finally
{
///Enable controls
}
sqlCon.Close();
// "OK"/"Next" button
}
}
In this method, you check if connection can be opened,
If it can't:
display error message
disable controls that allow user to continue interaction, until correct selection is made
If it can, just close connection and go forth to the next part of the code, where you actually use the connection.
You'll also require some sort of "Checking connection" message displayed to the user and blocking his interaction while connection is being checked.

Categories

Resources