4

I have a simple heartbeat method for my DB2/400:

public bool CheckConnection()
        {
            try
            {
                using (OleDbConnection db = new OleDbConnection( this.conString ))
                {
                    OleDbCommand cmd = new OleDbCommand();
                    cmd.CommandText = "select 1 from sysibm.sysdummy1";
                    cmd.Connection = db;
                    db.Open();
                    cmd.ExecuteReader();
                    db.Close();
                    return true;
                }
            }
            catch (Exception)
            {
                return false;
            }
        }

I want to use this when my application runs ,and of course i dont want to hold the execution of the rest of the form.

My main method in my form:

public FrmMain()
    {
        InitializeComponent();
        PrepareFormTexts();
        PrepareFormData();
        PrepareStateClass();
        CheckDbConnection();
        //Initialize Data Access for AS400
        Dal = JDE8Dal.Instance;
        Dal.conString = Properties.Settings.Default.conAS400;
        //Initialize Icons
        picCon.Image = Properties.Resources.ledGreen;
        picStatus.Image = Properties.Resources.ledGreen;
        // Load recording from file if they exist
        PrepareRecordings(AppState.DataFileName,'|');
    }

CheckDbConnection method:

    private async Task<bool> CheckDbConnection()
    {
        return await Task.Factory .StartNew(() => Dal.CheckConnection());
    }

I think it runs fine but i get a warning

Warning 1 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

Should i ignore it? should i put async in main method?

UPDATE: After discussing below i found out that since i am using async package for .NET 4.0 i cant really make my dal methods non-blocking. the only thing i can do is to use the async/await + task.factory.startnew in order to keep my app working while dal works on the background.

Code below:

public FrmMain()
    {
        InitializeComponent();
        Load += FormLoadInit;
    }

    private async void FormLoadInit(object sender, EventArgs e)
    {
        PrepareFormTexts();
        PrepareFormData();
        PrepareStateClass();
        txtLot.Select();
        //Initialize Data Access for AS400
        Dal = JDE8Dal.Instance;
        Dal.conString = Properties.Settings.Default.conAS400;
        // Load recording from file if they exist
        PrepareRecordings(AppState.DataFileName, '|');
        bool cn = await Task.Factory.StartNew(() => Dal.CheckConnection());
        //Initialize Icons
        picCon.Image = cn ? Resources.ledGreen : Resources.ledRed;
        picStatus.Image = Properties.Resources.ledGreen;

    }
e4rthdog
  • 5,103
  • 4
  • 40
  • 89

4 Answers4

4

What the warning is telling you is that you are effectively firing and then forgetting this asynchronous task. You never actually use the results of the operation, nor do you even store the Task itself to allow any future code to rely on it's result (or even knowing when it finished).

This won't break anything, but it's not really helpful either. You may as well just not call the method. Now, in your case this is just a dummy list, so you need to ask yourself what you're trying to test. Do you want to display some dummy value on the form just as a proof of concept? If so, you're code will need a few changes to get there.

To actually use the results of the query you'll need to await it, and you can't do that from within a constructor. You'll want to move the code to the Form Load event handler and mark the handler as async so that you can await within it.

Another issue you have is that, to create your async method, you're starting a new task that will run in a thread pool and then having it perform blocking methods. The primary advantage of using the async/await model is that you don't need to do that. You should be using database querying methods that directly give you a Task<T> as their return value and don't block. You can then await on those tasks (or just return them) such that your application has no threads being blocked while you wait.

Your CheckConnection method should look something like this:

public async Task<bool> CheckConnection()
{
    try
    {
        using (OleDbConnection db = new OleDbConnection(this.conString))
        {
            OleDbCommand cmd = new OleDbCommand();
            cmd.CommandText = "select 1 from sysibm.sysdummy1";
            cmd.Connection = db;
            db.Open();
            await cmd.ExecuteReaderAsync();
            return true;
        }
    }
    catch (Exception)
    {
        return false;
    }
}

CheckDbConnection then doesn't need to exist. There's no need to wrap CheckConnection in a Task because it already returns a task.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • So you are saying that i should move the calling of the method in a load event, i got it. But next if i dont want to call the method directly from dal but let a business layer do it for me, what is the sequence of await /asyncs? i am confused as to where i need to put pair of asyncs/await .where do i have the await/async in a 3-tier approach? – e4rthdog Dec 04 '12 at 21:16
  • @e4rthdog The DAL should expose some method that returns a `Task` or `Task`. The UI can then `await` on that task to perform a non-blocking wait until the results of that operation are completed. Any method containing an `await` inside of it's body needs to have `async` in it's signature. The `async` does nothing other than tell the compiler "hey, I have an await inside of here somewhere". It's a way of ensuring backwards compatability in the event someone had named a variable/method/whatever `await`. – Servy Dec 04 '12 at 21:21
  • What if i want to use the dal method normally, should i be able to o it even if i return it as Task? Also in the specific example i just want the db checking method to opearate on the background and when it finishes to turn a picture red or green depending on the result. Meanwhile i want the form to be operable... – e4rthdog Dec 04 '12 at 21:24
  • @e4rthdog Yes you can use the method even though it returns a task instead of a boolean directly. It's perfectly acceptable for DAL methods to "chain" their async/await operators. If a complex operation uses a simpler DAL method it can mark itself as `async` and `await` the simpler method, thus it will also return a `Task`, and the code can be written as if it's sequential, rather than asynchronous (even though it's not, which is what's cool). – Servy Dec 04 '12 at 21:28
  • I tried changing my sal method to Task but i cannot use "return true" now, i dont know what i have to return. And also if i want the application to continue its operation, after the await i would have only the code that i need to operate after the result. The rest of the init should go to the calling method in order to continue immediately after the await? – e4rthdog Dec 04 '12 at 21:45
  • Yes, put all of the code that needs to wait after the `await` and the rest before it. As for the DAL method returning a `Task`, as I said in my answer, you shouldn't be using the blocking DB methods. You should be using a database method that returns a Task already. Either `return` the results up the line, or `await` it, do something with the result, and then `return` that. – Servy Dec 04 '12 at 21:47
  • Can you please add to your answer the correct dal method as it should appear to be Task? (my first code snippet) – e4rthdog Dec 04 '12 at 21:51
  • thanks, but there is no executereaderasync method in oledbcommand. Do you mean something else? – e4rthdog Dec 04 '12 at 21:59
  • NOTE: i am NOT using 4.5 framework. I am using the async package for 4.0 framework... – e4rthdog Dec 04 '12 at 22:00
  • @e4rthdog That's... unfortunate. Without async methods there's little reason to use this model in the first place, and you don't get the performance advantage of not tying up a thread pool thread. That will force you to basically go back to doing what you were doing in the OP at the DAL, and wrapping the methods in `Task.Factory.StartNew` at some point. – Servy Dec 04 '12 at 22:05
  • Need XP support but also needed an easy way of making my app NOT blocking while something happens in DAL and i wanted the simplicity of the async /await without having to go to task and continuewith...I suppose this is the best i can do e? To use async /await only for not blocking and let the dal block the background thread.. – e4rthdog Dec 04 '12 at 22:07
  • @e4rthdog Given that you can't actually use a non-blocking DB call the code snippet and last paragraph (and some of the comments) of the answer doesn't apply, but the rest of it does. You won't get a performance benefit, but you can still wrap the blocking calls in `Task.Factory.StartNew` and `await` on them. – Servy Dec 04 '12 at 22:11
  • i think i got it. Please read the update on my OP. thanks a million for your patience. Marking your answer. – e4rthdog Dec 04 '12 at 22:15
0

Constructors can't be Async (at least I always get compiler errors whenever I try. But eventhandlers can be. I think most of that initialization code belongs in the Form.Load handler anyway.

public FrmMain()
{
    InitializeComponent();
    Load+=OnLoaded;
}

private async void Onloaded(object sender, EventArgs args)
{
    PrepareFormTexts();
    PrepareFormData();
    PrepareStateClass();
    await CheckDbConnection();
    //Initialize Data Access for AS400
    Dal = JDE8Dal.Instance;
    Dal.conString = Properties.Settings.Default.conAS400;
    //Initialize Icons
    picCon.Image = Properties.Resources.ledGreen;
    picStatus.Image = Properties.Resources.ledGreen;
    // Load recording from file if they exist
    PrepareRecordings(AppState.DataFileName,'|');
}
Michael Brown
  • 9,041
  • 1
  • 28
  • 37
  • Theory says that when await line is reached , the control will be moved to the calling method and when it finishes it will resume after the line...If this is true what i really want to do is to check for db connection and at the same time to proceed with my other stuff ... – e4rthdog Dec 04 '12 at 21:22
0
public FrmMain()
{
    InitializeComponent();
    PrepareFormTexts();
    PrepareFormData();
    PrepareStateClass();
    Task delayTask = CheckDbConnection();
    //Initialize Data Access for AS400
    Dal = JDE8Dal.Instance;
    Dal.conString = Properties.Settings.Default.conAS400;
    //Initialize Icons
    picCon.Image = Properties.Resources.ledGreen;
    picStatus.Image = Properties.Resources.ledGreen;
    // Load recording from file if they exist
    PrepareRecordings(AppState.DataFileName,'|');
}
Alex Filipovici
  • 31,789
  • 6
  • 54
  • 78
  • 1
    That will make the warning go away, but it still doesn't address what the warning means, or even do anything with the task. It's practically the same as just adding a warning suppression. – Servy Dec 04 '12 at 20:12
0

In order to accomplish your scope, maybe you should consider the Reactive Extensions Framework. Below is a sample implementation based on the example from your question. You'll need to add to labels to your form (label1 and label2).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

//get Reactive Extensions and reference them in your project.
//Reactive Extensions for .NET (Rx .NET) http://msdn.microsoft.com/en-us/data/gg577610
using System.Reactive.Linq;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Load += Form1_Load;
        }

        async void Form1_Load(object sender, EventArgs e)
        {
            //do independent stuff
            PrepareSomeStuff();

            //notify the DB checking
            label1.Text = "Checking DB";

            //declare an IObservable
            IObservable<bool> startAsync = Observable.Start<bool>(() => CheckConnection()).FirstAsync<bool>();

            //do some other independent stuff
            PrepareSomeOtherStuff();

            //wait for the IObservable to return data
            bool isDbReady = await startAsync;
            label1.Text = "Ready";
        }

        private void PrepareSomeOtherStuff()
        {
            //do some other stuff
            label2.Text += "\r\nDo some other stuff";
        }

        private void PrepareSomeStuff()
        {
            //do stuff
            label2.Text = "Do stuff";
        }

        private bool CheckConnection()
        {
            //do stufff
            System.Threading.Thread.Sleep(5000);
            return true;
        }
    }
}
Alex Filipovici
  • 31,789
  • 6
  • 54
  • 78