CslaDataSource with DataPortalResult Async list methods

CslaDataSource with DataPortalResult Async list methods

Old forum URL: forums.lhotka.net/forums/t/11140.aspx


jamie.clayton posted on Thursday, February 09, 2012

I've got an application with multiple UI's connected.  I've just been through an exercise to upgrade all the List methods to include Asycn for retrieving lists for ViewModels in WPF.

VB Example

        ''' <summary>
        ''' Asynchronous retrieval of all the software in the system.        
        ''' </summary>
        ''' <param name="handler"></param>
        ''' <remarks>Designed for use with ViewModels(of CSLA.BusinessObject)</remarks>
        Public Shared Sub GetAllAsync(ByVal handler As EventHandler(Of DataPortalResult(Of SoftwareList)))
            Dim dp As New DataPortal(Of SoftwareList)()
            AddHandler dp.FetchCompleted, handler
            dp.BeginFetch(New SoftwareCriteria())
        End Sub

WPF View Model

Namespace ViewModels
    Public Class SoftwareListViewModel
        Inherits Csla.Wpf.ViewModel(Of VIP.BL.SoftwareList)
 
        Private Shared ReadOnly logger As NLog.Logger = NLog.LogManager.GetCurrentClassLogger()
 
        Sub New()
            MyBase.New()
            If System.ComponentModel.DesignerProperties.GetIsInDesignMode(MeThen
                Return
            End If
 
            LoadAsync()
        End Sub
 
        ''' <summary>
        ''' Report any ansync errors back to the user. Provides a full path of the issue to user/logging mechanism.
        ''' </summary>
        ''' <param name="sender"></param>
        ''' <param name="e"></param>
        ''' <remarks>Improvement over constructor based lamba method, as we can report explicit code location back to the user/logger.</remarks>
        Private Sub Property_Changed(sender As Object, e As System.ComponentModel.PropertyChangedEventArgsHandles Me.PropertyChanged
            If e.PropertyName = "Error" AndAlso [Error] IsNot Nothing Then
                Const cVIEWMODEL_ERROR As String = "{0} error: {1}"
                Const cVIEWMODEL_ERROR_TITLE As String = "Data error"
                Dim Current As StackFrame = New StackFrame(0)
                Dim stackPath As String = String.Format("{0}.{1}", Current.GetMethod().ReflectedType.FullName, Current.GetMethod().Name)
                Dim message As String = String.Format(cVIEWMODEL_ERROR, stackPath, [Error].Message)
                logger.ErrorException(message, [Error])
                MessageBox.Show(message, cVIEWMODEL_ERROR_TITLE, MessageBoxButton.OK)
            End If
        End Sub
 
        Private Sub Load()
            '   Fine if you want to populate a async list, but it you then want to bind a value in that list we really want to know
            '   When the list is populated and then we want to select an item in that list.
            '   LL: For a simple UI behaviour, this seems a little more complex than expected.
            Dim parameters As Object() = PopulateBoMethodParameters()
 
            'DoRefresh("GetAll", parameters)
            DoRefresh("GetAll")
        End Sub
 
        Private Sub LoadAsync()
            '   Fine if you want to populate a async list, but it you then want to bind a value in that list we really want to know
            '   When the list is populated and then we want to select an item in that list.
            '   LL: For a simple UI behaviour, this seems a little more complex than expected.
            Dim parameters As Object() = PopulateBoMethodParameters()
 
            'BeginRefresh("GetAll", parameters)
            BeginRefresh("GetAllAsyncWithAllEntry")
        End Sub
 
        Private Shared Function PopulateBoMethodParameters() As Object()
            Dim parameters(0) As Object
            'parameters(0) = Application.Globals.UserID
            'Return parameters
            Return Nothing
        End Function
    End Class
End Namespace

WPF Form XAML & Code

<CollectionViewSource x:Key="SoftwareListVM_Source"
                              d:DesignSource="{d:DesignInstance vm:SoftwareListViewModel, CreateList=True}" />
        <CollectionViewSource x:Key="SoftwareListModelSource"
                              Source="{Binding Path=Model, Source={StaticResource SoftwareListVM_Source}}" />
Public Sub LoadSoftwareList()         PopulateCollectionView("SoftwareListVM_Source"New ViewModels.SoftwareListViewModel)     End Sub

The performance improvement for the WPF forms that are drop down list heavy is fantastic! So now I'm trying to get my head around doing this on web pages that use CSLADataSource.

ASPX example

<csla:CslaDataSource ID="SoftwareCslaDataSource" runat="server" TypeName="VIP.BL.SoftwareList, VIP.BL">
</csla:CslaDataSource>
    Private Sub SoftwareCslaDataSource_SelectObject(ByVal sender As ObjectByVal e As Csla.Web.SelectObjectArgsHandles SoftwareCslaDataSource.SelectObject
        If Me.rptlstSoftware.Visible Then
            e.BusinessObject = VIP.BL.SoftwareList.GetAll()
        Else
            e.BusinessObject = Nothing
        End If
    End Sub

Can any one tell me how I can load the CslaDataSource on the web page Asynchronously. I can't seem to find an example or an obvious method to complete this.

RockfordLhotka replied on Thursday, February 09, 2012

You can't use the async data portal in ASP.NET.

There are numerous reasons this is problematic, mostly having to do with the way ASP.NET itself is designed to work and use threads.

This may change in .NET 4.5 with the new async/await keywords, because they allow your code to function as though it were synchronous, even though behind the scenes it does asynchronous work.

Even then however, using async will probably cause worse performance on a web server than the simpler sync counterparts.

jamie.clayton replied on Thursday, February 09, 2012

Thanks for the quick reply Rocky.

RockfordLhotka
You can't use the async data portal in ASP.NET.

This is good to know.

RockfordLhotka
however, using async will probably cause worse performance on a web server than the simpler sync counterparts

In my case I have a report criteria web page that includes every list in a database as potential report criteria.  So I'm loading 14 lists synchronously.One of these lists alone take 5 seconds to load, so I'm close to that 6 second web standard before users get frustrated. 

Essentially I'm looking at trying to apply the "animated loading circle" UX approach from WPF to ASPX. Since the Async data portal is not practical, I thought I might need to go down a PageAsyncTask or an jQuery path. Does anyone have any recommendations on a good technology match for this scenario?

RockfordLhotka replied on Thursday, February 09, 2012

I can provide a more "robust" answer that might help you :)

It is possible to use the async data portal in ASP.NET if you fully understand the ramifications.

First, you must understand how ASP.NET uses threads so you don't starve the server of threads. I won't cover the details here - it is a well known reality that ASP.NET uses the thread pool, as does the data portal (and many other async technologies), so they compete for that pool of threads. Take up too many, and inbound web requests from other users can be blocked waiting for a thread to become available.

Second, you must understand how ASP.NET renders pages. It expects to run on one thread, and when that thread completes the request is done - even if there are outstanding "background" threads running. Those threads just complete and the results of their work is lost. So you must ensure that you implement proper thread synchronization code to block the main request thread from completing until you know that all background threads are complete. This is probably done with ManualReset or other thread synchronization objects from System.Threading.

If you understand those two points, it is absolutely possible to fire off multiple async data portal requests as part of your page processing, and they will run on background threads (from the thread pool) to interact with your data access code. When each async request completes, the callback for that request will run, just like it does in a smart client setting.

What I don't know, is on what thread the callback will run. The data portal relies on a .NET synchronization context to get the callback to run on the "UI thread" in Windows Forms, WPF, Silverlight, etc. I think that ASP.NET has a synchronization context too, but I'm not sure.

If ASP.NET does NOT have a synchronization context, the callback code will run on the background thread. That's probably good, because your callback handler can signal a threading primitive so the main request thread knows the task is complete. You'd probably have a threading primitive for each data portal call, so you can do a WaitAll on all those primitives to block the primary thread until they are done.

If ASP.NET does have a synchronization context, the callback code will run on the primary request thread. This would be more complex, because it means you can't block that thread with a simple WaitAll or Wait. If you do block the primary thread, it could never run to handle the callback. In this case you'd need to implement something called a SpinWait (I don't know if .NET includes one of these in System.Threading?) so the thread just spins idle while waiting for the background tasks to complete. Implementing a SpinWait is a little tricky, because you obviously don't want the primary thread to chew up 100% CPU while spinning and polling the background task completion flags...

In any case, you must ensure that the primary thread doesn't complete and render the output until all the background tasks are complete.

JonnyBee replied on Thursday, February 09, 2012

ASP.NET has it's own AspNetSynchronizationContext:

http://msdn.microsoft.com/en-us/magazine/gg598924.aspx

 

 

RockfordLhotka replied on Thursday, February 09, 2012

I kind of suspected that was the case Jonny. That tends to make things a little more complex due to the spinwait requirement.

But if there's a good spinwait implementation this should work fine.

rcollette replied on Friday, February 10, 2012

Rocky,

Is it possible to write BO code in a manner that makes it possible to run in the Silverlight/Metro clients as well as on IIS without having to write two separate sets of logic due to threading requirements?   If so, do your latest books/videos provide an example of that?

The impression I got, coming out of the 2008 book was that running my BOs with CSLA.NET Silverlight was more of file linking and recompile exercise but now as I look at some of the Silverlight examples, it appears that the code is written differently.   Maybe I'm missing something because I haven't read any of the Silverlight books yet.

Thanks,

Rich

 

 

RockfordLhotka replied on Friday, February 10, 2012

The differences are not dramatic - at least not in most classes.

You are right to mention async - the primary code differences aren't SL vs .NET - they are sync vs async.

The other code difference area relates to platform capability (does ADO.NET exist on each platform?). This can be mitigated by using a encapsulated invocation with a DAL provider model, or factory implementation or invocation - all discussed in the Using CSLA 4: Data Access book.

SL/WP7/WinRT require async in some places. .NET does not.

.NET has ADO.NET, not all other platforms have it. WinRT will have data access, but it will be async (unlike all existing data access technologies).

What does this mean?

It means that any logic that talks to a server or database might have to vary between platforms.

(as an aside - there are a LOT of other differences between platforms, but CSLA is able to transparently abstract them for you - I'm only talking about the differences we can't abstract)

In most business classes you need to use "#if !SILVERLIGHT" blocks to ensure that synchronous static factory methods, and DataPortal_XYZ methods, don't compile on SL/WP7. That's all, nothing else should be different.

The exception to this is if you have a business rule that needs to talk to a server/database. Like a rule that checks the database to see if a value already exists.

Such a rule would be implemented through the use of another business object (often a command object or read-only object). Where this gets a little tricky is that the rule needs to be an async rule on SL/WP7, it could be async or sync on Windows Forms or WPF, and it normally needs to be a sync rule in ASP.NET.

The Using CSLA 4: Creating Business Objects book discusses this specific scenario and shows how to implement such a rule to minimize the code differences between the sync/async scenarios. You end up with one set of code, with just an "if (IsAsync)" check so the rule works properly on client vs server.

Copyright (c) Marimer LLC