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.
Related
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
}
I have two connection strings, the first is to an existing database, and the second is to a different SQL Server. I need to copy the entire first database to the second one. This includes creating all the tables, constraints, and keys. I want to use the SqlConnection object and do not have access to a hard drive.
EDIT 1:
I can create the db (code below) but I can't figure out how to programmatically set up the tables and then populate them. What is the best way to approach this?
public void Copy()
{
var connectionForExisting = "REDACTED";
var connectionForNew = "REDACTED";
var targetDbName = "REDACTED";
var source = new SqlConnection(connectionForExisting);
var target = new SqlConnection(connectionForNew);
try
{
source.Open();
}
catch (Exception e)
{
source.Close();
return;
}
try
{
target.Open();
}
catch (Exception e)
{
source.Close();
target.Close();
return;
}
var myCommand = new SqlCommand(String.Format(CreateNewDatabaseScript, targetDbName), target);
try
{
myCommand.ExecuteNonQuery();
}
catch (Exception e)
{
source.Close();
target.Close();
return;
}
}
I created a Sql server database, in which I added a table named user. Then I executed this script
ALTER DATABASE [TestNotification] SET ENABLE_BROKER
I'd like to use SqlDependency class to notify a winforms application when the user table were changed.
namespace Watcher
{
public partial class Form1 : Form
{
private int changeCount = 0;
private const string statusMessage = "{0} changes have occurred.";
private DataSet dataToWatch = null;
private SqlConnection connection = null;
private SqlCommand command = null;
public Form1()
{
InitializeComponent();
button1.Enabled = CanRequestNotifications();
this.FormClosed += Form1_FormClosed;
}
void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
SqlDependency.Stop(GetConnectionString());
if (connection != null)
{
connection.Close();
}
}
private bool CanRequestNotifications()
{
// In order to use the callback feature of the
// SqlDependency, the application must have
// the SqlClientPermission permission.
try
{
SqlClientPermission perm =
new SqlClientPermission(
PermissionState.Unrestricted);
perm.Demand();
return true;
}
catch
{
return false;
}
}
private void button1_Click(object sender, EventArgs e)
{
changeCount = 0;
label1.Text = String.Format(statusMessage, changeCount);
//SqlDependency.Stop(GetConnectionString());
SqlDependency.Start(GetConnectionString());
if (connection == null)
{
connection = new SqlConnection(GetConnectionString());
}
if (command == null)
{
command = new SqlCommand(GetSQL(), connection);
}
if (dataToWatch == null)
{
dataToWatch = new DataSet();
}
GetData();
}
private string GetConnectionString()
{
return #"Data Source=BILOG-PRT-12\SQLEXPRESS; Initial Catalog=TestNotification;Integrated Security=True";
}
private string GetSQL()
{
return "Select [id],[nom],[prenom],[age] from [dbo].[user]";
}
private void dependency_OnChange(object sender, SqlNotificationEventArgs e)
{
MessageBox.Show("modification Occurred");
ISynchronizeInvoke i = (ISynchronizeInvoke)this;
if (i.InvokeRequired)
{
OnChangeEventHandler tempDelegate =new OnChangeEventHandler(dependency_OnChange);
object[] args = { sender, e };
i.BeginInvoke(tempDelegate, args);
return;
}
SqlDependency dependency = (SqlDependency)sender;
dependency.OnChange -= dependency_OnChange;
++changeCount;
label1.Text = String.Format(statusMessage, changeCount);
GetData();
}
private void GetData()
{
//dataToWatch.Clear();
//command.Notification = null;
SqlDependency dependency = new SqlDependency(command);
if (connection.State != ConnectionState.Open) connection.Open();
using (var dr = command.ExecuteReader())
{
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
}
}
}
}
All the broker service are running :
I launched the application, Then I clicked into the button and finally I go the Sql Server management studio And I inserted a new row. The problem is that the message box in the application is not shown so the c# application is not notified by SQL Server!!!
So I need to know :
Why this happens?
Which step I forget ?
How can I resolve this issue?
There's quite a few limitations with SqlDependency. To quote one relevant issue:
The projected columns in the SELECT statement must be explicitly stated, and table names must be qualified with two-part names.Notice that this means that all tables referenced in the statement must be in the same database.
(see https://msdn.microsoft.com/library/ms181122.aspx for the full list)
You have to explicitly use two-part name (e.g. dbo.user instead of just user).
Also, you need to execute the command. It doesn't just start working automagically :) Adding a simple using (var dr = command.ExecuteReader()) {} should be enough.
I'm sure this question has been asked many times, and I dug through a few of the similar ones but couldn't find one that really flushed it out as much as I'd have liked.
I have an application that uses a database helper class to connect and retrieve records from a database. I was considering rewriting it and wanted to know what the best way to do it would be.
Here is roughly how it's set up now (Note: This is already in place, and there are thousands of lines of this stuff).
DatabaseHelper.CS
private SqlConnection conn;
public DatabaseHelper()
{
// Create database connection
conn = new System.Data.SqlClient.SqlConnection();
SqlConnectionStringBuilder connection = new SqlConnectionStringBuilder();
connection.ConnectTimeout = 150; // Microsft fix for timeout error (known bug)
connection.MinPoolSize = 20; // Microsft fix for timeout error (known bug)
connection.DataSource = Properties.Settings.Default.DBString;
connection.InitialCatalog = Properties.Settings.Default.DBInitCatalog;
connection.IntegratedSecurity = true;
if (conn.State != ConnectionState.Connecting)
{
conn.ConnectionString = connection.ConnectionString;
}
}
public bool Open()
{
if (this.IsOpen()) // IsOpen is just a method that checks connectionstate.
{ return true; }
else
{
try
{
conn.Open();
return true;
}
catch (System.Data.SqlClient.SqlException ex)
{
// omitted for post
}
}
return false;
}
public bool Close()
{
if (!this.IsOpen())
{ return true; }
try
{
conn.Close();
return true;
}
catch (System.Data.SqlClient.SqlException ex)
{
// omitted for post
}
return false;
}
public List<string> GetTeamLeaders(string team)
{
List<string> leaders = new List<string>();
string query = "Select Leader FROM Teams WHERE Team = #team_vc";
try
{
using (SqlCommand cmd = new SqlCommand(query, conn))
{
cmd.Parameters.Add("#team_vc", SqlDbType.NVarChar).Value = team;
using (SqlDataReader sdr = cmd.ExecuteReader())
{
int column = sdr.GetOrdinal("Leader");
while (sdr.Read())
{
leaders.Add(sdr[column].ToString());
}
}
}
}
catch (Exception ex)
{
// omitted for post
}
return leaders;
}
private string GetTeamAbbrev(string team)
{
string abbrev= "";
string query = "SELECT Abbrev FROM Teams where Team = #team_vc";
using (SqlCommand cmd = new SqlCommand(query, conn))
{
cmd.Parameters.Add("#team_vc", SqlDbType.NVarChar).Value = team;
try
{
abbrev= Convert.ToString(cmd.ExecuteScalar());
}
catch (Exception ex)
{
// omitted for post
}
}
return (string.IsNullOrEmpty(location)) ? "None" : abbrev;
}
MainApp.CS
private DatabaseHelper dbHelper;
public MainApp()
{
InitializeComponent();
dbHelper= new DatabaseHelper(); // Instantiate database controller
}
private void someButton_Click(object sender, EventArgs e)
{
List<string> teamLeaders = new List<string>();
if (dbHelper.Open())
{
teamLeaders = dbConn.GetTeamLeaders(textboxTeam.Text);
dbHelper.Close();
}
else
{
return;
}
// all the code to use results
}
private void someOtherButton_Click(object sender, EventArgs e)
{
List abbreviation = string.Empty;
if (dbHelper.Open())
{
abbreviation = dbConn.GetTeamLeaders(textboxTeam.Text);
dbHelper.Close();
}
else
{
return;
}
// all the code to use results
}
Now I'm sure there are some very serious issues with how this is setup, but for me my biggest complaints are always having to open and close the connection.
My first move was to just move the open and close inside the DatabaseHelper methods, so each method (i.e. GetTeamLeaders) would call open and close in itself. But the problem was if it did actually fail to open it was really hard to feed it back up to the main program, which would try to run with whatever value the variable contained when it was made. I was thinking I would almost need an "out" bool that would tag along to see if the query completed, and could check make and check that anytime I used I needed to get something from the database, but I'm sure that has issues to.
Another big problem with this approach is anytime I want to make a call from another form, I have to either make another instance of the helper on that form, or pass a reference to the main one. (Currently my way around this is to retrieve all the information I would need beforehand in the MainApp and then just pass that to the new form). I'm not sure if when I rewrite this there's a good static way to set it up so that I can call it from anywhere.
So is there anything here worth keeping or does it all need to be stripped down and built back from scratch?
I am getting this unhandled exception error shown: See screen shot.
I am getting this error only when I run with Ctrl+ F5 and not in F5(debug mode). Not sure if this is helpful, my computer is a windows 7- 64bit and running a 32 bit build
According to this discussion: How can I get WinForms to stop silently ignoring unhandled exceptions?, adding Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException) it wll cause Windows to ignore the error.
EDIT:
frmPlant_Load Event
public partial class frmPlant : Form
{
DatabaseConnection _DbConnection = new DatabaseConnection();
string conString = ConfigurationManager.ConnectionStrings["RVESTConnString"].ConnectionString;
SQLQueries _SQlQueries = new SQLQueries();
DataSet ds;
SQLiteDataAdapter da;
static DataTable gdt;
int gSelectedPlant;
string gSelectedPlantName = "";
bool ignoreSelChg = false;
bool DataDirty = false;
public frmPlant()
{
InitializeComponent();
}
public frmPlant(int sSelectedPlant)
{
InitializeComponent();
}
private void frmPlant_Load(object sender, EventArgs e)
{
ds = FillData();
gdt = ds.Tables[0];
bindingSource1.DataSource = gdt;
dataGridView1.DataSource = bindingSource1;
gSelectedPlant = StaticClass.GlobalValue;
dataGridView1.AutoGenerateColumns = true;
dataGridView1.Columns["PlantId"].Visible = false;
dataGridView1.Columns["NSSS_Design"].Width = 70;
}
private DataSet FillData()
{
ignoreSelChg = true;
SQLiteConnection con = new SQLiteConnection(conString);
DataSet dPlant;
try
{
con.Open();
SQLiteCommand cmd = new SQLiteCommand("select * from Plant", con);
da = new SQLiteDataAdapter("select * from Plant", con);
dPlant = new DataSet();
da.Fill(dPlant, "plant");
}
catch (Exception ex)
{
throw ex;
}
finally
{
con.Close();
}
return dPlant;
}
I should also add another concern: When I say continue here in the dialog, it works fine but leaves a background process running. I have to manually go and kill it in the task manager
Question: Suppose I add this line in the Program.cs, will it ignore ANY- even genuine errors which need to be fixed?
More Code:
This dialog pops up on button click on the second screen- Initial Setup screen . The first being a splash screen. Initial setup takes me to the Plant form
Here's the code for the initial setup screen
public partial class frmInitialSetUp : Form
{
public frmInitialSetUp()
{
InitializeComponent();
}
private void btnOK_Click(object sender, EventArgs e)
{
Program.fPlant = new frmPlant();
Program.fPlant.Show();
this.Hide();
}
private void frmInitialSetUp_Load(object sender, EventArgs e)
{
Program.LoadAllForms();
}
}
}
Program.cs
static public void LoadAllForms()
{
try
{
Program.fInitialSetUp = new frmInitialSetUp();
Program.fPlant = new frmPlant();
Program.frm*** = new frm***();
Program.frm*** = new frm***();
Program.frm*** = new frm***();
}
catch (Exception ex)
{
throw ex;
}
}
On button click on the
Enclosed the frmload in a try { } catch(unhandledexception ex) {} and ran it in debug mode
This time the debugger caught it. It was a small problem with datagridview columns