Question

Private Sub LoadData(Of T)(ByVal query As ObjectQuery(Of T), 
    ByRef result As IEnumerable(Of T))
  If Connection.State = ConnectionState.Open Then
    result = query.ToArray
  Else
    AddHandler Connection.StateChange,
      Sub(sender As Object, e As StateChangeEventArgs)
        LoadData(query, result)
      End Sub
  End If
End Sub

In the above code I am trying to recurse the LoadData function when the connection is unavailable, I want to defer the loading to when it becomes available.

The problem is the above code leads to a compiler error, since a ByRef param cannot be used in lambda expressions.

Any idea of how to do this the right way?

Was it helpful?

Solution

You cannot use a ByRef parameter in a lambda because it could be pointing to a location on the stack which no longer exists once the lambda execuetes. All you have to do is use a more "permanent" storage location. You can pass in an object with a property of IEnumerable(Of T) that you can set in order to assign the result.

A probably better option is to pass in a delegate (Action<IEnumerable<T>>) that accepts the result and performs whatever action the caller requires with the result. Here's an example in C#:

void LoadData<T>(ObjectQuery<T> query, Action<IEnumerable<T>> action)
{
    if (Connection.State == ConnectionState.Open)
        action(query.ToArray());
    else
    {
        // create a lambda to handle the next state change
        StateChangeEventHandler lambda = null;
        lambda = (sender, e) =>
        {
            // only perform our action on transition to Open state
            if (Connection.State == ConnectionState.Open)
            {
                // unsubscribe when we're done
                Connection.StateChange -= lambda;
                action(query.ToArray());
            }
        }
        // subscribe to connection state changes
        Connection.StateChange += lambda;
    }
}

And you would invoke LoadData like this:

LoadData(query, results => listBox.DataSource = results);

Note the nuances of my implementation. For example, it does not call itself within the event handler because that will cause it to resubscribe to the event if the handler is ever called with a state other than Open. It also unsubscribes from the event once the connection opens. I'm not sure how this would translate to VB, but in C# this is a 3-step process. First you must declare a variable to hold the lambda and set its value to null. Then you create the lambda, which can now reference itself to unsubscribe. And finally you can use the lambda to subscribe to the event.

OTHER TIPS

You've got a problem in that your calling thread has no idea if the variable has been populated by the LoadData() call

In thi case you need to do something like:

  • Block execution until the load completes
  • Raise an event when the load completes
  • Set a publicly visible field on your loader to indicate load status

One (possible) compromise would be to return a custom object instead of an IEnumerable

The custom object could immediately attempt to load the data and keep re-trying until success. If the result set of the custom object is read before a load has occured, block the thread until the load completes, otherwise return the result set

In this scenario, you get a benefit if there's a delay between the Load occuring and the data being used - your program can continue on until it needs the data. Whether this i useful or not depends entirely on what you're using it for.

More information on blocking execution: It depends a little on how you become aware that the connection is back up but something like:

Public Sub LoadData(Of T)(ByVal query As ObjectQuery(Of T), ByRef result As IEnumerable(Of T))
  While Not Connection.State = ConnectionState.Open
    Threading.Thread.Sleep(100) 'Pick a decent value for this delay based on how likely it is the connection will be available quickly
  End While
  result = 'Use the connection to get your data
End Sub

Is there any reason you're doing this as a sub with ByRef parameters as opposed to a function? You're only "returning" one object so I don't quite see the benefit. Not that it would make a huge difference functionally but it would be more readable...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top