iStock_000010874966XSmall I write this because if I ran into this, someone else will. Now first off it wasn’t Linq that failed, but it looked like it and here is my story of failure I found in a WPF background worker threading code which can happen in other areas as well.

The perennial advice to all people in the MSDN forums as well as others is never, never, never  write to a GUI control in a thread or the BackgroundWorker’s do work event. All GUI work must be done on a the GUI. I very well knew that…but the reverse (don’t read from a GUI in a different thread)  is also true and that bit me. Let me explain.

Anecdotal Evidence

Imagine my surprise when I created a WPF project at a new job and started getting this exception in the DoWork code of my BackgroundWorker when the first use of a Linq query’s delayed execution point was accessed :

The calling thread cannot access this object because a different thread owns it.

The code threw an exception, on line 16 below, where I was loading data from after the Linq call.  I began to think, does this have something to do with the Linq DataContext?

void bcLoad_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        List<string> Results = new List<string>();

        DatabaseDataContext dbc = new DatabaseDataContext();

        var data = dbc.SystemData
                      .Where(ac => ac.Account_id == tbAccount.Text)
                      .Where(ac => ac.TimeStamp == 0)
                      .Where(ac => ac.Category_id == tbCategory.Text)
                      .Where(ac => (int)ac.Category_seq_nbr == int.Parse(tbSequenceNumber.Text))
                      .Select(ac => ac.Unit_Code);

        Results.Add("Unit Code: " + data.First());

        e.Result = Results;
    }
    catch (Exception ex)
    {
        lbxData.Dispatcher.BeginInvoke( new Action(() => lbxData.Items.Add( "Exception Caught: " + ex.Message )));
    } 
}

No the problem was within the setup of the Lambdas for the Linq query. All the highlighted lines above is where the actual problem originates and not on the final highlighted line.

The problem was that I was accessing Gui controls data, and not changing; that was the nuance. For in my mind that was ok, it was a passive read action and not a direct writing one. Obviously not.

Note: If you have come to this blog experiencing this problem but for the writing of items to a control, one method to solve it is to use the Dispatcher off the control on any thread not just BackgroundWorker. That code is shown in my exception catch blog above. That line is perfectly fine to do and is not another issue. The lbxData is a Listbox on the main Xaml and because of the immediacy of the exception, I write

Resolution

Since I was already using the plumbing of the DoWorkEventArgs, it seemed a natural choice to pass in the data using that object. I changed the call to pass in a Dictionary of values to extract the data as such:

bcLoad.RunWorkerAsync(new Dictionary<string, string>() 
                        { 
                            { "AccountID",      tbAccount.Text }, 
                            { "CategoryID",     tbCategory.Text },
                            { "SequenceNumber", tbSequenceNumber.Text }
                        });

Then to consume the Dictionary as such:

void bcLoad_DoWork(object sender, DoWorkEventArgs e)
{
    try
    {
        List<string> Results = new List<string>();

        Dictionary<string, string> UserInputs = e.Argument as Dictionary<string, string>;

        if (UserInputs != null)
        {

        DatabaseContext dbc = new DatabaseContext();

        var data = dbc.SystemData
                      .Where(ac => ac.Account_ID == UserInputs["AccountID"])
                      .Where(ac => ac.TimeStamp == 0)
                      .Where(ac => ac.Category_id == UserInputs["CategoryID"])
                      .Where(ac => (int)ac.Category_seq_nbr == int.Parse(UserInputs["SequenceNumber"]))
                      .Select(ac => acc.UnitCode);

        Results.Add("Unit Code: " + data.First());

        e.Result = Results; // Pass the results to the completed events to process them accordingly.
        }
    }
    catch (Exception ex)
    {
        lbxData.Dispatcher.BeginInvoke( new Action(() => lbxData.Items.Add( "Exception Caught: " + ex.Message )));
    }

}

I simply convert the object property of Argument to a Dictionary, as highlighted, and go do the work. One doesn’t have to use a Dictionary. One can pass in any object off of the Argument property. Hope This Helps

Share