Hi guys,
Most of our code uses sync processing, but i have recently created an object that calls a Build method, in this Build method, the code loops through some children objects and builds them in a List property against the object. This works perfectly if i do it sync'ly, but its takes a while to complete, so thought running each child async would speed things up.
I have wrote the following code, but I hold the Identity object in the ApplicationContext and this seems to be blank when the sync threads run, and i need this for each child object as i read certain values in there.
I have wrote the following code so you can run the Build method, so code runs sync'ly, or you can call BuildAsync, to run it async'ly. If you run the Async method, then it should raise the BuildCompleted event.
The following code doesnt use the DataPortal.BeginExecute code as i am not sure if and how i can use this for this. If it is better to use this, then please let me know, the DataPortal may fix my ApplicationContext/ClientContext problem??
Any suggestions are welcome, i have done some async method before, but i have always created the Identity object (in ApplicationContext) in the sync thread.
Here is my example code against the parent object
Public Class ParentObject
Private mBuildCount As Integer
Private WithEvents mReport As ChildObject
Public Overrides Sub BuildAsync()
If Not IsAsyncRunning Then
IsAsyncRunning = True
' get my child objects, sync'ly
Dim mBuiltReports As List(Of ChildObjects) = GetReports()
' set buildcount = 0
mBuildCount = 0
' loop through each report and start building
Dim lLoopCount As Integer = 0
While lLoopCount < mBuiltReports.Count
' set to mReport so we can call the reports BuildCompleted event
mReport = mBuiltReports.Values(lLoopCount)
mReport.BuildAsync()
lLoopCount += 1
End While
End If
End Sub
Private Sub Report_BuildCompleted(ByVal sender As Object, ByVal e As ComponentModel.AsyncCompletedEventArgs) Handles mReport.BuildCompleted
Dim lReport As IBaseReport = DirectCast(sender, IBaseReport)
Dim lBuildCount As Integer
mBuiltReports(lReport.ReportId) = lReport
' lock this object to update this value
SyncLock Me
mBuildCount += 1
lBuildCount = mBuildCount
End SyncLock
' all reports are built so raise build completed event
If lBuildCount = mBuiltReports.Count Then
OnBuildCompleted(Me, New ComponentModel.AsyncCompletedEventArgs(Nothing, False, Nothing))
End If
End Sub
End Class
Public Class ChildObject
Private Delegate Sub BuildDelegate()
Public IsAsyncRunning As Boolean
Public Overridable Sub BuildAsync() Implements IBaseReport.BuildAsync
If Not IsAsyncRunning Then
IsAsyncRunning = True
Dim lBuildDelegate As New BuildDelegate(AddressOf Build)
Dim lCallback As New AsyncCallback(AddressOf BuildCallBack)
Dim lAsync As AsyncOperation = AsyncOperationManager.CreateOperation(Nothing)
lBuildDelegate.BeginInvoke(lCallback, lAsync)
End If
End Sub
Private Sub BuildCallBack(ByVal ar As IAsyncResult)
' Retrieve the delegate.
Dim lDelegate As BuildDelegate = CType(ar.AsyncState, BuildDelegate)
Dim lAsync As AsyncOperation = CType(ar.AsyncState, AsyncOperation)
' Call EndInvoke to retrieve the results.
lDelegate.EndInvoke(ar)
' raise event to say method is completed
Dim lCompletedArgs As New AsyncCompletedEventArgs(Nothing, False, Nothing)
' call sub to raise event
OnBuildCompleted(Me, lCompletedArgs)
' set as not running
IsAsyncRunning = False
End Sub
Public Event BuildCompleted As AsyncCompletedEventHandler Implements IBaseReport.BuildCompleted
Protected Overridable Sub OnBuildCompleted(ByVal sender As Object, ByVal e As AsyncCompletedEventArgs)
RaiseEvent BuildCompleted(Me, e)
End Sub
Public Sub Build()
' This method loops through the child objects and calls there BuildAsync method
' so that they are built
For Each lChild As ChildObject In Me.Children
lChild.BuildAsync()
Next
End Sub
End Class
Hi,
If your async method does a DataPortal call to get data7build object then Iæd recommend to use the async dataportal.
Second option is to use the Csla.Threading.BackgroundWorker. This background worker
is wrapper on top of System.ComponentModel.BackgroundWorker that will make sure to
transfer ApplicationContext and Principal to the background thread.
The csla.threading.backgroundworker sounds perfect, I guess u can fire events on completion etc? Would u happen to have some basic sample code or point me to some?
Works just the same as System.ComponentModel.BackgroundWorker
Basically:
DoWork -this code runs on a background thread
RunWorkerCompleted - the callback on the "main" thread. Always check for Error before you access the Object.
Start processing by calling BackgroundWorker.RunWorkerAsync(object parameter).
There is also a demo project in Samples\Net\cs\BackgroundWorkerDemo, the samples download for Csla 4.1
Jonny You seem to know a lot about asynchronous processing, so my last question should be simple for you In the function that calls BackgroundWorker.RunWorkerAsync, what is the best way to wait for the callback event? At the moment I am doing threading.wait which doesn't seem right to me. Sry if this is a stupid question
Hmmm, I'd probably add another level of "abstraction" and have another Backgroundworker on top.
You use BackgroundWorkers to offload the fetch from the UI thread and you do NOT want to have Thread.Wait or other tricks on that thread.
So I'd rather have a background worker - to offload from the UI thread and start the other background workers in DoWork. And use a WaitHandle to halt execution (exit DoWork) until the last backgroundworker has completed. You could even support Cancelling of the async operations.
http://msdn.microsoft.com/en-us/library/system.threading.waithandle.aspx
WaitHandle's is also used in the unit testing of Csla so look at the unit tests.
This is going to be a lot easier with the new async/await keywords.
Copyright (c) Marimer LLC