Serializing Aysnchronous SQLCommand - c#

I have the code below which works fine when the Session state is InProc. However when the Session state is Sql Server, HandleCallback never gets called. How do I change the code so HandleCallBack gets called?
private void TAdata(object sender, EventArgs e)
{
if (((Form)sender).DialogResult == DialogResult.No)
{
return;
}
if (Changed)
{
MessageBox.Show(this.ParentForm, "Save Payroll Changes First", "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
else
{
SqlConnection dbconnAS = new SqlConnection(strDBconnAS);
{
try
{
AsyncCallback callback = new AsyncCallback(HandleCallback);
using (SqlCommand SQLcmd = new SqlCommand("dbo.KronosTaData", dbconnAS))
{
SQLcmd.CommandType = CommandType.StoredProcedure;
dbconnAS.Open();
Changed = true;
SQLcmd.BeginExecuteNonQuery(callback, SQLcmd);
strResult = "";
ExportProgress.Visible = true;
ExportProgress.Value = 0;
ExportProgress.Maximum = 120;
ExportTimer.Start();
}
}
catch (Exception ex)
{
Changed = false;
strResult = ex.Message;
if (dbconnAS != null)
{
dbconnAS.Close();
}
}
}
}
}
private void HandleCallback(IAsyncResult result)
{
try
{
using (SqlCommand SQLcmd = (SqlCommand)result.AsyncState)
{
int rowCount = SQLcmd.EndExecuteNonQuery(result);
strResult = "OK";
SQLcmd.Connection.Close();
}
}
catch (Exception ex)
{
strResult = ex.Message;
}
}
private void ExportTimer_Tick(object sender, EventArgs e)
{
//Timer Exists on UI thread
if (strResult == "")
{
if (cmdKronos.Enabled) cmdKronos.Enabled = false;
if (ExportProgress.Value > ExportProgress.Maximum - 10) ExportProgress.Maximum += 10;
ExportProgress.Value += 1;
}
else if (strResult == "OK")
{
Changed = false;
cmdKronos.Enabled = true;
ExportProgress.Visible = false;
ExportTimer.Stop();
MessageBox.Show(ParentForm, "Kronos data succesfully imported", "Data Import", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
Changed = false;
cmdKronos.Enabled = true;
ExportProgress.Visible = false;
ExportTimer.Stop();
MessageBox.Show(ParentForm, Text, "Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
}

You are disposing the command as soon as you've finished starting it:
using (SqlCommand SQLcmd = new SqlCommand("dbo.KronosTaData", dbconnAS))
{
//...
SQLcmd.BeginExecuteNonQuery(callback, SQLcmd);
//...
}
that will abort everything - so indeed: it will never complete. Basically; using doesn't play nicely with Begin*/End*, so don't do that. You might find it much easier to do this using async/await, by the way (via ExecuteNonQueryAsync).
You also probably want to close and dispose the connection somewhere; again, async/await would make this much easier to get right.

The solution is to declare the variable strResult as static.
See Visual Webgui Variable Scope

Related

How to know when SQL query has finished

I´m trying to create a logic to update massively the column "Prices" from table "Products".
This logic is made, but in my C# code I send the command to the MS Access DB, and immediately I want to show the new results.
The problem is that the update query takes a few seconds to finish, and my C# instantaneously asks for the Select from Products, showing no changes.
The main question is how I can know when the update has finished to show the new results in the grid.
Here my code:
private void btnAceptar_Click(object sender, EventArgs e)
{
string operator;
int code = Convert.ToInt32(cmbcode .SelectedValue);
int material = Convert.ToInt32(cmbMaterial.SelectedValue);
int productType= Convert.ToInt32(cmbproductType.SelectedValue);
if (cmboperating.SelectedIndex == 0){ operator= "+"; }
else if (cmboperating.SelectedIndex == 1) { operator= "-"; }
else if (cmboperating.SelectedIndex == 2) { operator= "*"; }
else { operator= "/"; }
if(txtValoroperating.Text != "") { txtValoroperating.Text = (txtValoroperating.Text).Replace(",", "."); }
string operating = txtValoroperating.Text;
if(rdPrecioDeLista.Checked == true & cbTodasPiezas.Checked == true)
{
//Here is my problem
oProductsDAL.modifyPricesMassively(operating, operator);
txtValoroperating.Text = null;
fillGridProducts();
}
}
And oProductsDAL.modifyPricesMassively(operating, operator) does this:
public bool modifyPricesMassively(operating, operator)
{
if (operating!= "" & operator!= "")
{
return conexion.executeMethod("UPDATE Piezas SET Precio = Precio " +operator+" " +operating);
}
else
{
return false;
}
}
conexion.executeMethod does this:
public bool conexion.executeMethod(string strComando)
{
try
{
OleDbCommand Comando = new OleDbCommand();
Comando.CommandText = strComando;
Comando.Connection = this.establecerConexion();
Conexion.Open();
Comando.ExecuteNonQuery();
Conexion.Close();
return true;
}
catch (Exception ex)
{
MessageBox.Show("No se pudo establecer conexion con la base de datos" +ex);
return false;
}
}
I wish to show a progress bar or a gif loading icon.

c# BeginInvoke doesn't work

I have this code
private delegate void InvokeDelegate();
private void OpenFormNewNote(object sender, FileSystemEventArgs e)
{
if(loop == 2)
{
string service = null;
if (currentServiceName != null)
{
service = currentServiceName.Replace(" ", "");
}
NewNotePanel newNote = new NewNotePanel(e.FullPath, service, listOfService, Path, MyConn, ipAddress, imgFolder, Utente_id);
newNote.TopMost = true;
watcher.EnableRaisingEvents = false;
var result = newNote.ShowDialog();
if(result == DialogResult.OK || result == DialogResult.Cancel)
{
watcher.EnableRaisingEvents = true;
if(result == DialogResult.OK)
{
this.BeginInvoke(new InvokeDelegate(Refresh));
}
}
loop = 0;
}
else
{
loop++;
}
}
And this is the Refresh() Function:
public void Refresh()
{
noteContainer.Controls.Clear();
page = 0;
try
{
string Query = "SELECT a, v, b, cFROM note Where Servizio_ID = " + asd+ " AND Visibile = 1 order by ID desc limit 15 OFFSET " + (pageIndex * page) + " ;";
MySqlCommand MyCommand = new MySqlCommand(Query, MyConn);
MySqlDataReader MyReader;
if (MyConn.State == ConnectionState.Open)
{
MyReader = MyCommand.ExecuteReader();// Here our query will be executed and data saved into the database.
while (MyReader.Read())
{
CreateNotePreview(MyReader.GetString("a"), MyReader.GetString("b"), MyReader.GetString("c"), MyReader.GetString("d"));
}
MyReader.Close();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Since OpenFormNewNote is called when the "Change" event of FileSystemWatcher is triggered.. of course it runs on a separated thread. Instead Refresh() does some UI stuff and it can't be called directly from OpenFormNewNote() otherwise it gives a Cross-Thread exception. So I tried with delegate but when the codeflow arrives on the BeginInvoke part.. Nothing happens and the Refresh function is not called.
What should I do?

textbox and dropdown values inserted should store in database using c# asp.net

I got a tab container which has 4 tabs in it. In one of the tab named ADD TASK I got few fields like
(Task Name: --txtbox
Client Name:--drpdwn
Begin Date:--txtbox wid calendar
Due Date:--txtbox wid calendar
Description:--txtbox
Assign To:--drpdown
Status:--drpdown
% Complete:--drpdown)
and an ADD and CANCEL button in the end.
On running the project and inserting the values to those above mentioned fields i will click the add button and after clicking the button the values should store in my DATABASE. i have table named TASK in my DB already.
Please help me with the back end code.
here is my code
protected void BtnAdd_Click(object sender, EventArgs e)
{
MTMSDTO objc = new MTMSDTO();
int Flag = 0;
objc.TaskName = Session["TaskName"].ToString();
objc.ClientName = DrpClientName.SelectedItem.Text;
objc.BeginDate = Convert.ToDateTime(TxtBeginDate.Text);
objc.DueDate = Convert.ToDateTime(TxtDueDate.Text);
objc.Description = Session["Description"].ToString();
objc.AssignTo = DrpAssignTo.SelectedItem.Text;
objc.Status = DrpStatus.SelectedItem.Text;
objc.PercentageComplete = Convert.ToInt32(DrpPercentageComplete.Text);
int X = obj.InsertTask(objc);
{
if (X >= 0)
{
Flag = 1;
}
else
{
Flag = 0;
}
}
if (Flag == 1)
{
LblSuccess.Visible = true;
LblSuccess.Text = "Data Added Successfully";
Panel2.Visible = false;
}
else
{
LblErr.Visible = true;
LblErr.Text = "Failed To Add Data!!!";
}
}
im using layered architecture and i have this code on my ACCESS file of DAL CLASS
public int InsertTask(MTMSDTO M)
{
DBAccess db = new DBAccess();
SqlParameter objParam = new SqlParameter("#TaskID", M.TaskID);
objParam.Direction = ParameterDirection.Output;
db.Parameters.Add(new SqlParameter("#TaskName", M.TaskName));
db.Parameters.Add(new SqlParameter("#ClientName", M.ClientName));
db.Parameters.Add(new SqlParameter("#BeginDate", M.BeginDate));
db.Parameters.Add(new SqlParameter("#DueDate", M.DueDate));
db.Parameters.Add(new SqlParameter("#Description", M.Description));
db.Parameters.Add(new SqlParameter("#AssignTo", M.AssignTo));
db.Parameters.Add(new SqlParameter("#Status", M.Status));
db.Parameters.Add(new SqlParameter("#PercentageComplete", M.PercentageComplete));
db.Parameters.Add(objParam);
int retval = db.ExecuteNonQuery("InsertTask");
if (retval >= 1)
{
return int.Parse(objParam.Value.ToString());
}
else
{
return -1;
}
}
the code is edited now but im getting error as "object reference not set to an instance of an object. " for the line (objc.TaskName = Session["TaskName"].ToString();) which is in BtnAdd_Cick.
Shouldn't your BtnAdd_Click function be something like this instead? You don't currently seem to be calling the InsertTask() function.
protected void BtnAdd_Click(object sender, EventArgs e) {
MTMSDTO m = new MTMSDTO();
m.TaskName = TxtTaskName.Text;
m.ClientName = DrpClientName.Text;
m.BeginDate = TxtBeginDate.Text;
m.DueDate = TxtDueDate.Text;
m.Description = TxtDescription.Text;
m.AssignTo = DrpAssignTo.Text;
m.Status = DrpStatus.Text;
m.PercentageComplete = DrpPercentageComplete.Text;
InsertTask(m);
}
get all values in back end and pass to this function
public bool InsertRecord(string strTableName, string strColumn_Name, string strValues)
{
SqlConnection OBJCONNECTION;
StringBuilder strbQuery;
SqlCommand cmd;
try
{
OBJCONNECTION= new SqlConnection();
OBJCONNECTION.ConnectionString = ConfigurationManager.ConnectionStrings["Basic_ADO"].ConnectionString;//get connection string from web.config file
OBJCONNECTION=
strbQuery = new StringBuilder();
strbQuery.Append("INSERT INTO ");
strbQuery.Append(strTableName);
strbQuery.Append("(" + strColumn_Name + ")");
//strbQuery.Append(" VALUES");
strbQuery.Append("(" + strValues + ")");
cmd = new SqlCommand(strbQuery.ToString(), OBJCONNECTION);
cmd.ExecuteNonQuery();
return true;
}
catch (Exception ex) { throw ex; }
finally { strbQuery = null; cmd = null;OBJCONNECTION.close();}
}

goto in c# and its usage

I have a subroutine. It comapares whether values are empty then doing something. For example, if they are empty, then warnings are raised.
The code works fine. But when value are not empty, the warnings are still pop out. Please help me to correct the logic.
Thanks.
private void btnNew_Click(object sender, EventArgs e)
{
try
{
if (txtbox1.Text.ToString().Trim() == string.Empty)
{
goto Msg1;
}
if (txtbox2.Text.ToString().Trim() == string.Empty)
{
goto Msg2;
}
DataRow dr = mydataSet.Tables[0].NewRow();
dr["Descript"] = txtbox1.Text;
dr["Abbr"] = txtbox2.Text;
dr["SortOrder"] = Convert.ToDecimal(numericOrder.Value);
if (SortOrders.Contains((decimal)dr["SortOrder"]))
{
goto Msg3;
}
mydataSet.Tables[0].Rows.Add(dr);
dgv.DataSource = mydataSet.Tables[0];
Msg1:
MessageBox.Show("Description is required.");
Msg2:
MessageBox.Show("Abbr is required.");
Msg3:
MessageBox.Show("Please select another one, this one is already used.");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
From the above code, you see. if txtbox1 has some value, the program still displays Msg1. I want to avoid it.
Because labels are just labels, and code after them is executed sequentially.
Why can't you do just this:
try
{
if (txtbox1.Text.ToString().Trim() == string.Empty)
{
MessageBox.Show("Description is required.");
return;
}
if (txtbox2.Text.ToString().Trim() == string.Empty)
{
MessageBox.Show("Abbr is required.");
return;
}
DataRow dr = mydataSet.Tables[0].NewRow();
dr["Descript"] = txtbox1.Text;
dr["Abbr"] = txtbox2.Text;
dr["SortOrder"] = Convert.ToDecimal(numericOrder.Value);
if (SortOrders.Contains((decimal)dr["SortOrder"]))
{
MessageBox.Show("Please select another one, this one is already used.");
return;
}
mydataSet.Tables[0].Rows.Add(dr);
dgv.DataSource = mydataSet.Tables[0];
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
It's so much more readable.
Restructure your code to avoid goto - it is a relic and not much use in a properly object oriented codebase.
Returning from the method, throwing exceptions or building an errors dictionary are all better options than using goto.
For example, you can have a List<string> errors which you add to when you get an error condition.
If it is empty, no errors were encountered, if it isn't, there were.
This is a good case were goto is the wrong way to go. Use something like this instead.
private void btnNew_Click(object sender, EventArgs e)
{
try
{
bool error = false;
if (txtbox1.Text.ToString().Trim() == string.Empty)
{
MessageBox.Show("Description is required.");
error = true;
}
if (txtbox2.Text.ToString().Trim() == string.Empty)
{
MessageBox.Show("Abbr is required.");
error = true;
}
if (SortOrders.Contains(Convert.ToDecimal(numericOrder.Value)
{
MessageBox.Show("Please select another one, this one is already used.");
error = true;
}
if(error)
return;
DataRow dr = mydataSet.Tables[0].NewRow();
dr["Descript"] = txtbox1.Text;
dr["Abbr"] = txtbox2.Text;
dr["SortOrder"] = Convert.ToDecimal(numericOrder.Value);
mydataSet.Tables[0].Rows.Add(dr);
dgv.DataSource = mydataSet.Tables[0];
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Edit
Just figured that my code didn't actually do the same as his first sample since it only displayed the first error no matter how many that occured. Updated my sample to accomodate for that.
I've always been taught to avoid goto like the plague, and it's something I've followed for years. I've never even considered it to be an option when writing code.
Thinking about it though, I did read an article a few years ago (which I can't find now) which said you could credibly use gotos only if you used it to jump down code, and not up: a rule that is stuck to here.
Check here for more info: Does anyone still use [goto] in C# and if so why?
There are better ways of using goto statement, for instacne using "return" (when used in the middle of a method), "break" and "continue". Have you ever used one of these?
private void btnNew_Click(object sender, EventArgs e)
{
try
{
var description = txtbox1.Text.Trim();
if (string.IsNullOrEmpty(description))
{
MessageBox.Show("Description is required.");
return;
}
var abbr = txtbox2.Text.Trim();
if (string.IsNullOrEmpty(abbr))
{
MessageBox.Show("Abbr is required.");
return;
}
var numericOrderValue = Convert.ToDecimal(numericOrder.Value);
if (SortOrders.Contains(numericOrderValue)
{
MessageBox.Show("Please select another one, this one is already used.");
return;
}
DataRow dr = mydataSet.Tables[0].NewRow();
dr["Descript"] = description;
dr["Abbr"] = abbr;
dr["SortOrder"] = numericOrderValue;
mydataSet.Tables[0].Rows.Add(dr);
dgv.DataSource = mydataSet.Tables[0];
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void btnNew_Click(object sender, EventArgs e)
{
try
{
if (txtbox1.Text.ToString().Trim() == string.Empty)
{
MessageBox.Show("Description is required.");
}
if (txtbox2.Text.ToString().Trim() == string.Empty)
{
MessageBox.Show("Abbr is required.");
}
DataRow dr = mydataSet.Tables[0].NewRow();
dr["Descript"] = txtbox1.Text;
dr["Abbr"] = txtbox2.Text;
dr["SortOrder"] = Convert.ToDecimal(numericOrder.Value);
if (SortOrders.Contains((decimal)dr["SortOrder"]))
{
MessageBox.Show("Please select another one, this one is already used.");
}
mydataSet.Tables[0].Rows.Add(dr);
dgv.DataSource = mydataSet.Tables[0];
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Try this. It works.

Handling errors while inside a button

I have a windows form that involes filling out textboxes with information and then clicking connect. I have error messages that pops up if any of the textboxes are empty but when I hit OK the program just continues and I end up getting run-time errors because there's insufficient information, and the program crashes. What I want is for the program to go back to the point before I hit "Connect" whenever any textbox is not filled out correctly.
This is the code:
private void cmdConnect_Click(object sender, EventArgs e)
{
if (cmdConnect.Text == "Connect")
{
if (txtGroup.Text == "")
{
txtGroup.Text = "_Group01";
}
if (txtItemID.Text == "")
{
MessageBox.Show("Please enter ItemID.", "Connect Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
switch (cboServer.Text)
{
case "":
MessageBox.Show("Please select and OPC server", "Connect Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
case "RSLinx Remote OPC Server":
if (txtMachine.Text == "")
{
MessageBox.Show("Please enter a machine name for remote connection", "Connect Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
else
{
oOpcServer.Connect(cboServer.Text, txtMachine.Text);
}
break;
case "RSLinx OPC Server":
oOpcServer.Connect(cboServer.Text);
break;
default:
if (txtMachine.Text == "")
{
oOpcServer.Connect(cboServer.Text);
}
else
{
oOpcServer.Connect(cboServer.Text, txtMachine.Text);
}
break;
}
oOpcGroup = oOpcServer.OPCGroups.Add(txtGroup.Text);
oOpcGroup.IsSubscribed = true;
oOpcGroup.IsActive = false;
oOpcGroup.UpdateRate = 1000;
ClHandle = 1;
oOpcGroup.OPCItems.DefaultAccessPath = txtAccessPath.Text;
oOpcGroup.OPCItems.AddItem(txtItemID.Text, ClHandle);
cmdItemWrite.Enabled = true;
cmdItemRead.Enabled = true;
cmdSyncWrite.Enabled = true;
cmdSyncRead.Enabled = true;
cmdAsyncWrite.Enabled = true;
cmdAsyncRead.Enabled = true;
cmdAdvise.Enabled = true;
txtSubValue.Enabled = true;
cboServer.Enabled = false;
txtMachine.Enabled = false;
txtGroup.Enabled = false;
txtAccessPath.Enabled = false;
txtItemID.Enabled = false;
cmdConnect.Text = "Disconnect";
}
else
{
oOpcServer.OPCGroups.RemoveAll();
oOpcGroup = null;
oOpcServer.Disconnect();
cmdConnect.Text = "Connect";
cmdItemWrite.Enabled = false;
cmdItemRead.Enabled = false;
cmdSyncWrite.Enabled = false;
cmdSyncRead.Enabled = false;
cmdAsyncWrite.Enabled = false;
cmdAsyncRead.Enabled = false;
cmdAdvise.Enabled = false;
txtSubValue.Enabled = false;
cboServer.Enabled = true;
txtMachine.Enabled = true;
txtGroup.Enabled = true;
txtAccessPath.Enabled = true;
txtItemID.Enabled = true;
}
oOpcGroup.DataChange += new RsiOPCAuto.DIOPCGroupEvent_DataChangeEventHandler(oOpcGroup_DataChange);
}
Adding a return statment after each message box would do the trick and cause the method to exit without doing the work at end.
Simplest solution, as Dervall mentioned is adding return statements after each MessageBox.Show call. But more elegant solution is using validation and error provider to highlight incorrect input data prior to executing connect logic.
Anyway, here is some thoughts on refactoring your code.
private void cmdConnect_Click(object sender, EventArgs e)
{
if (cmdConnect.Text == "Disconnect")
{
Disconnect();
SetControlsToDisconnectedState();
return;
}
if (String.IsNullOrWhiteSpace(txtGroup.Text))
txtGroup.Text = "_Group01";
if (String.IsNullOrWhiteSpace(txtItemID.Text))
{
ShowErrorMessage("Connect Error", "Please enter ItemID.");
return;
}
if (String.IsNullOrWhiteSpace(cboServer.Text))
{
ShowErrorMessage("Connect Error", "Please select and OPC server");
return;
}
Connect(cboServer.Text, txtMachine.Text);
DoSomethingWithGroup(txtGroup.Text, txtAccessPath.Text, txtItemID.Text);
SetControlsToConnectedState();
}
What changed:
It's more readable, when you verify which text on button, then which it don't have
Method ShowErrorMessage does exactly what it says
Verify text with IsNullOrWhiteSpace because it could be full of white spaces
Control state changing moved to separate code
Connecting/Disconnecting now separated from UI
Here other methods:
private void ShowErrorMessage(string title, string message)
{
MessageBox.Show(message, title, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
private void SetControlsToConnectedState()
{
UpdateControls(true);
}
private void SetControlsToDisconnectedState()
{
UpdateControls(false);
}
private void UpdateControls(bool isConnected)
{
cmdConnect.Text = isConnected ? "Disconnect" : "Connect";
cmdItemWrite.Enabled = isConnected;
cmdItemRead.Enabled = isConnected;
cmdSyncWrite.Enabled = isConnected;
cmdSyncRead.Enabled = isConnected;
cmdAsyncWrite.Enabled = isConnected;
cmdAsyncRead.Enabled = isConnected;
cmdAdvise.Enabled = isConnected;
txtSubValue.Enabled = isConnected;
cboServer.Enabled = !isConnected;
txtMachine.Enabled = !isConnected;
txtGroup.Enabled = !isConnected;
txtAccessPath.Enabled = !isConnected;
txtItemID.Enabled = !isConnected;
}
private void Disconnect()
{
oOpcServer.OPCGroups.RemoveAll();
oOpcGroup = null;
oOpcServer.Disconnect();
}
private void Connect(string serverName, string machineName)
{
switch (serverName)
{
case "RSLinx Remote OPC Server":
if (String.IsNullOrWhiteSpace(machineName))
{
ShowErrorMessage("Connect Error", "Please enter a machine name for remote connection");
return;
}
oOpcServer.Connect(serverName, machineName);
break;
case "RSLinx OPC Server":
oOpcServer.Connect(serverName);
break;
default:
if (String.IsNullOrWhiteSpace(machineName))
oOpcServer.Connect(serverName);
else
oOpcServer.Connect(serverName, machineName);
break;
}
}
private void DoSomethingWithGroup(string groupName, string accessPath, string itemID)
{
oOpcGroup = oOpcServer.OPCGroups.Add(groupName);
oOpcGroup.IsSubscribed = true;
oOpcGroup.IsActive = false;
oOpcGroup.UpdateRate = 1000;
ClHandle = 1;
oOpcGroup.OPCItems.DefaultAccessPath = accessPath;
oOpcGroup.OPCItems.AddItem(itemID, ClHandle);
oOpcGroup.DataChange += new RsiOPCAuto.DIOPCGroupEvent_DataChangeEventHandler(oOpcGroup_DataChange);
}

Categories

Resources