2

I have a method that uses ODBC to go out and get some data in relation to a database however I need to change my method to async so that it doesn't freeze the form but still loads the data I see there is a ReadAsync method on the ODBC reader but how does one use that.

public decimal GetFreeStockFromSage(string productCode)
{
        string sageDsn = ConfigurationManager.AppSettings["SageDSN"];
        string sageUsername = ConfigurationManager.AppSettings["SageUsername"];
        string sagePassword = ConfigurationManager.AppSettings["SagePassword"];
        decimal retDecimal =(decimal) 0.00;
        //using (var connection = new OdbcConnection("DSN=SageLine50v24;Uid=Manager;Pwd=;"))
        using (var connection = new OdbcConnection(String.Format("DSN={0};Uid={1};Pwd={2};", sageDsn, sageUsername, sagePassword)))
        {
            string sql = string.Format("SELECT 'STOCK_CODE' ,'Description',QTY_IN_STOCK - QTY_ALLOCATED AS 'FreeStock'  FROM 'STOCK' where STOCK_CODE = '{0}'", productCode);
            using (var command = new OdbcCommand(sql, connection))
            {
                connection.Open();

                using (var reader = command.ExecuteReader())
                {

                    while (reader.ReadAsync())
                    {
                          retDecimal = Convert.ToDecimal(reader["FreeStock"]);                          

                    }

                }
            }

            return retDecimal;
        }
    }           
Dave
  • 233
  • 2
  • 12
  • `OdbcCommand` returns an `OdbcDataReader` and no version of it *I* can find has a `ReadAsync` method. What framework are you targetting? – Damien_The_Unbeliever Apr 09 '19 at 08:00
  • Now, this alone will not "unfreeze" your form, that depends on how you deal with it higher up the call chain. Make sure you fully understand the concept of async and why your current code freezes the form before you rewrite everything. – Lasse V. Karlsen Apr 09 '19 at 08:00
  • @Damien_The_Unbeliever I just tried declaring a `OdbcDataReader` variable in LINQPad, and it gives me intellisense for `ReadAsync`, it should be inherited from `DbDataReader`, the base class, if I'm not mistaken. – Lasse V. Karlsen Apr 09 '19 at 08:01
  • @LasseVågsætherKarlsen - ah, so it'll be fake-async anyway and not help with unfreezing. – Damien_The_Unbeliever Apr 09 '19 at 08:01
  • Let me check what ILSpy from LINQPad tells me about the implementation. – Lasse V. Karlsen Apr 09 '19 at 08:02
  • Yes, this is fake async, ie. sync wrapped in async. It'll block just as much as the "non-async" code, which in this case is the same thing. I see that they added the methods as virtual but this is just shoddy work if you ask me. They should've made the asyncness be a part of an interface that individual implementations could opt-in to. – Lasse V. Karlsen Apr 09 '19 at 08:02
  • @LasseVågsætherKarlsen Yeah that was the impression I got when I was doing testing it still blocked the UI any idea chaps of making the method a bit better so it didnt block the UI. – Dave Apr 09 '19 at 08:20
  • 1
    You will have to run it on a different thread basically. You could see if BackgroundWorker will be enough for you. – Lasse V. Karlsen Apr 09 '19 at 08:23

1 Answers1

0

If ReadAsync is not truly asynchronous, you'll need to use a separate thread.

The simplest thing would be to just put the whole method call into a Task:

decimal d = await Task.Run(() => GetFreeStockFromSage(myProductCode));

Making GetFreeStockFromSage() asynchronous is also an option, but it's not going to be perfect if ReadAsync isn't truly asynchronous. You'd need to use async methods for anything IO-bound, such as SqlConnection.OpenAsync() or SqlCommand.ExecuteReaderAsync() (assuming these are actually truly asynchronous). You'd then need to move the while(reader.Read()) loop into its own task so that it doesn't block the UI thread.

Patrick Tucci
  • 1,824
  • 1
  • 16
  • 22