0

I am developing windows form application, I have carefully written the program and it is working absolutely fine on the Touch Screen POS system with 2 GB Ram and SSD (Windows 10).

I am facing a very strange issue when using it on a relatively fast computer (16Gb RAM, i7 10th gen). Let me be a little descriptive about it.

Basically, I have designed an application to handle restaurant billing, which has a simple flow as below

  1. Add Items to Cart -> 2) Place Order -> 3) Pay for Order -> 4) Print Receipt.

The issue is on the 3rd step when I make payment, it takes 1 second to insert (update) into the database and the same for reading it again to be printed on the receipt. on a fast computer, it is being processed as data has not yet been inserted.

I have two forms executing separate functions

  1. POS form - this will handle cart items and save invoice details into the database (without payment info) after saving the invoice it will popup Pay Form as Dialog and will wait for Dialog Result to print a receipt.

CODE ON POS FORM

//AFTER INSERTING ALL ITEMS AND INVOICE INTO DATABASE
Pay p = new Pay(label_order_no.Text);
if (p.ShowDialog() == DialogResult.OK)
{
    try
    {
        //dt_cart is DataTable containing cart items - already inserted into database.
        if (dt_cart.Rows.Count > 0)
        {
            if (MessageBox.Show("DO YOU WANT TO PRINT BILL?", "CONFIRM BILL PRINT", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                //tw.getData is just simplified method(for this question) with select MS ACCESS sql statement which will return value from column "BALANCE" of selected order from database.
                // BALANCE - COLUMN NAME IN DB, INVOICES - TABLE NAME IN DB, ORDER_ID IS PRIMARY KEY
                if (tw.getData("BALANCE", "INVOICES", "WHERE ORDER_ID = " + label_order_no.Text) != "")
                {
                    print_bill(); // THIS WILL PRINT BILL, BUT AMOUNTS WILL NOT BE DISPLAYED ON BILL AS IT IS TAKING TIME TO GET UPDATED
                }
                else
                {
                    Thread.Sleep(3000); // IF THREAD IS PAUSED FOR 2 OR 3 SECONDS BILL IS GETTING PRINTED PROPERLY. IT IS NOT POSSIBLE TO STOP FOR 3 SECONDS AS RETAIL COUNTERS ARE VERY CROWDED AND WORKERS ARE OPERATING BILLING VERY FAST WITH SHORTCUT KEYS.
                    print_bill();
                }
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
  1. Pay Form - this will handle payment method, amount, due, etc, and will UPDATE into the existing database (Single ROW, only 4-5 columns will be updated). After updating the database this form will return the dialog result of OK to let the POS form know that database has been updated and printing receipt is a go.

CODE ON PAY FORM

//PAID AND COMPLETED ARE BOOLEAN VALUES SET AS FALSE BY DEFAULT
//IF CUSTOMER IS PAYING FULL AMOUNT THEN PAID AND COMPLETED WILL BE SET TO TRUE
if (paid == false)
{
    //FOR PARTIAL PAYMENT OR CREDIT SALE
    if (MessageBox.Show("PAYMENT INCOMPLETE, ARE YOU SURE TO PROCEED?", "CONFIRM", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
    {
        //tw.insertData will execute non query (insert or update) and will return "SUCCESS" as string or error message
        string updateInvoice = tw.insertData("UPDATE INVOICES SET CASH_PAID = " + total_cash_paid.ToString() + ", CARD_PAID = " + tb_card_partial.Text + ", UPI_PAID = " + tb_upi_partial.Text + ", BALANCE = " + tb_due.Text + ", PAID = " + paid + ", COMPLETED = " + completed + " WHERE ORDER_ID = " + label_order_id.Text);
        if (updateInvoice == "SUCCESS")
        {
            MessageBox.Show("PAYMENT SUCCESSFUL!", "SUCCESS", MessageBoxButtons.OK, MessageBoxIcon.Information);
            this.DialogResult = DialogResult.OK;
            //AFTER UPDATE SUCCESSFUL FORM WILL RETURN DIALOG RESULT OK TO POS FORM FOR PRINTING RECIEPT
        }
        else
        {
            MessageBox.Show("ERROR - " + updateInvoice, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }
}
else
{
    //FOR COMPLETED PAYMENT PAID AND COMPLETED WILL BE SET TO TRUE
    //tw.insertData will execute non query (insert or update) and will return "SUCCESS" as string or error message
    string updateInvoice = tw.insertData("UPDATE INVOICES SET CASH_PAID = " + total_cash_paid.ToString() + ", CARD_PAID = " + tb_card_partial.Text + ", UPI_PAID = " + tb_upi_partial.Text + ", BALANCE = " + tb_due.Text + ", PAID = " + paid + ", COMPLETED = " + completed + " WHERE ORDER_ID = " + label_order_id.Text);
    if (updateInvoice == "SUCCESS")
    {
        MessageBox.Show("PAYMENT SUCCESSFUL!", "SUCCESS", MessageBoxButtons.OK, MessageBoxIcon.Information);
        this.DialogResult = DialogResult.OK; 
        //AFTER UPDATE SUCCESSFUL FORM WILL RETURN DIALOG RESULT OK TO POS FORM FOR PRINTING RECIEPT
    }
    else
    {
        MessageBox.Show("ERROR - " + updateInvoice, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

PAY FORM SCREENSHOT

By analyzing the above codes you can clearly see that I am not calling the print_bill() method unless the Pay form is returning "OK" as a dialog result. only after updating data in the database.

The receipt looks like below, the left one is printed instantly, the right one is printed with a 3-sec pause.

VIEW RECEIPTS

The computer with lower ram and speed takes its time to load UI, hence it is working properly there, but on faster computers, I am facing this issue, how can it be solved? Thanks in advance!

Notes:

  1. I have simplified insert and update and select methods for this question only, I am calling proper OleDbCommand with monitored DB connections, and catching exceptions.
  2. It will also help to know more about optimizing MS ACCESS DB insert/update speed.
  3. Attached DB Table for reference See Image SEE INVOICES TABLE

UPDATE

tw.insertData(string query);

is a method with following code

public string insertData(string query)
{
    if (connection_mode == "access")
    {
        try
        {
            if (conn.State == ConnectionState.Closed)
            {
                conn.Open();
            }
            OleDbCommand cmd = new OleDbCommand(query, conn);
            cmd.ExecuteNonQuery();
            cmd.Dispose();
            return "SUCCESS";
        }
        catch (Exception ex)
        {
            return ex.Message;
        }
        finally
        {
            if (conn.State == ConnectionState.Open)
            {
                conn.Close();
            }
        }
    }
    else
    {
        try
        {
            if (con.State == ConnectionState.Closed)
            {
                con.Open();
            }
            SqlCommand cmd = new SqlCommand(query, con);
            cmd.ExecuteNonQuery();
            cmd.Dispose();
            return "SUCCESS";
        }
        catch (Exception ex)
        {
            return ex.Message;
        }
        finally
        {
            if (conn.State == ConnectionState.Open)
            {
                conn.Close();
            }
        }
    }
}
  • I suspect that you are using a different driver on the two machines that account for the speed difference. Look at drivers at connectionstrings.com : https://www.connectionstrings.com/access/ – jdweng Dec 21 '21 at 17:55
  • 1
    ... and use parameters to avoid sql injection. – LarsTech Dec 21 '21 at 17:57
  • 2
    One way to optimize Access is not to use Access. Use a proper database server, such as Postgres, SQL Server or MySQL – Charlieface Dec 21 '21 at 20:56
  • @jdweng - I am pretty sure that the same driver is used on both machines (I have tried more than one machine) Both have x64 OS (windows version might be different based on windows updates), x64 version of MS OFFICE 2010 Pro, VC Runtime, and .NET version is same for both machines. – Susheel Kasab Dec 22 '21 at 06:12
  • @LarsTech - As said in notes, modified code for this question only, I have followed everything properly for production purposes. – Susheel Kasab Dec 22 '21 at 06:12
  • @Charlieface - SQL server version for large retail stores is already developed, but small hotels with a single counter can't afford the license cost for that. – Susheel Kasab Dec 22 '21 at 06:12
  • SQL Server Express goes up to 10Gb databases. Postgres and MySql are both free. Regarding your issue: you do have `Thread.Sleep(3000);` there for some reason, is that not your issue? – Charlieface Dec 22 '21 at 09:13
  • What type machine are you using? You may need to update the kernel of machine or the motherboard drivers. – jdweng Dec 22 '21 at 10:36
  • @Charlieface I am currently using `Thread.Sleep(3000)` as a solution, but I don't want the system to wait for 3 seconds that is what my question is about. Please read the comment written after 'Thread.Sleep(3000)' in code – Susheel Kasab Dec 22 '21 at 11:59
  • 1
    This all makes no sense: how can the insert get "processed" after the select if it was executed first? What does `tw.insertData` actually do, is it asynchronous or something? – Charlieface Dec 22 '21 at 12:51
  • @Charlieface has the point. It is not the Access engine, that delays anything (it is extremely fast), neither C# or the ACEDB driver, it is your code. Probably, it opens and closes the connection to the database _asynchronous or something_ multiple times. Open _one_ connection and keep it for the full payment session. That said, if you have an _SQL Server_ version running, why not use the free _LocalDb_ version of this and keep one code base? – Gustav Dec 27 '21 at 09:29
  • @Charlieface I have updated the question to let you know what this method actually does. It is not asynchronous. – Susheel Kasab Dec 28 '21 at 17:43
  • @Gustav - I am also using the SQL server version where this problem does not appear with the same application. I have also tried using a single connection to execute the entire payment transaction, but it does not matter, the issue stays. Anyway, my payment transaction is a single UPDATE query that updates data into a single row in 5 Columns, No other transaction Is done at the time of this update query, as Saving Invoice details are executed in the earlier step. Cannot use SQL server version everywhere because of the low configuration of POS system (Touch screen billing computer) & budget. – Susheel Kasab Dec 28 '21 at 17:49
  • As said, try using that same open connection. There is no reason to repeatedly open and close a connection to an Access database. – Gustav Dec 28 '21 at 18:08
  • Again: the delay is caused by `Thread.Sleep(3000);` which you have not demonstrated as being necessary. Why do you need to "wait" for anything, why do you think the data is not saved? Once you execute the command then the data is saved, this applies both to ACE Engine and SQL Server. And as I said earlier, you can use SQL Server Express or LocalDB, or Postgres or MySql, all of which are free and pretty low footprint (can even be run on a low-end laptop if necessary). – Charlieface Dec 28 '21 at 20:19
  • @Charlieface already told in earlier comments, I am using `Thread.Sleep(3000)` as the current solution to pause the app for 3 seconds until data gets saved into the database, then calling print function to read data from DB. If I did not use `Thread.Sleep()` then the select query is getting empty data. Please see the receipt images. the left one is without a sleep timer, the right one is with a sleep timer. – Susheel Kasab Dec 30 '21 at 14:23
  • OK try this one: Go to "Administrative Tools" -> "ODBC Data Source Administrator" 32 or 64-bit depending on your application -> "User DSN" -> "MS Access Database" -> "Configure" -> "Advanced" -> "ImplicitCommitSync" -> Value of `Yes` -> OK, OK, close, etc. Now it should commit the data immediately without delay. Not sure if you need to restart your PC – Charlieface Dec 30 '21 at 14:43
  • @Charlieface, thank you! this really helped! Plus I have made some changes in code to optimize connection open and close only for a specific types of results (like keeping a single connection open for fetching data inside a DataReader loop) this helped to optimize reaction speed as well as my original issue is sorted! Thank you! – Susheel Kasab Dec 31 '21 at 08:35
  • The performance is dramatically increased, attaching both types of apps if someone wants to check. (One with connection open close on every query, second is optimized for connection open close and "ImplicitCommitSync" to true. [link](https://drive.google.com/drive/folders/12wKPeWRpuo67tcUDia0V7vRtieJgkh0h?usp=sharing) You may need Access Connectivity Engine 2010 x64 and Regional Date format "dd-mm-yyyy" to run this app. p.s. Attached app will not work (license expires) after 3 days from today... – Susheel Kasab Dec 31 '21 at 08:50
  • Could close this as a duplicate of https://stackoverflow.com/questions/4562973/whats-the-difference-between-jet-oledbtransaction-commit-mode-and-jet-oledbus (i've already voted so can't vote again). By the way, you might be able to do this through the connection string `ImplicitCommitSync=Yes;` – Charlieface Dec 31 '21 at 10:28

0 Answers0