Does PRISM have a performance issue?

A developer in the PRISM forum was noting that the Shell (main app) loaded a control faster than a view (injected into a region) could load the very same control.   As THIS DEMO will verify the user control loaded much faster within the shell (bottom) and took up to five seconds to load within a module's region (top).   Once I refactored his code you will see that not only does the control load very fast but also the application (patience will be required for the demo code it is very slow).

His comment follows:

I'm working on a simple demo application using the compoiste Application Library for WPF. I have an issue in that when I inject a user control into a region in the shell there is a considerable lag in the control rendering and being visible. When I include the same control in the shell (not in a referenced IModule) it appears instantly. I understand that I must take a performance hit at the point when I load controls into my shell from other modules but the delay is quite extreme. The control itself is a dataGrid (WPFToolkit) with around 2000 rows - there is no database work being done to generate the data for the grid.

Is there any reason Im getting such poor performance? The lag in the control rendering is around 5 seconds - a complete show stopper for using prism if it cannot do something so simple. more

Source code provided by developer demonstrating the issue: CALDemoOld.zip (2.13 mb)
Refactored source code using best Practices and Patterns (P&P): CALDemo.zip (3.50 mb)

There were some inherent design flaws that introduced this issue; which have nothing to do with PRISM (as shown by the demo link above).

The following control is the topic of this discussion - note how in the code-behind that we are populating TestEntities with 1000 elements and then applying it to the data context.


Figure A. 

Below is the XAML for the Shell - note that it simply references the Control.   When the Shell is loaded the code (above) will execute effectively populating the control within the Shell.


Figure B. 

As you can see below in the sequence diagram the CreateShell process (which actually does a Shell.Show()) will execute, and display the control long before the InitializeModules() command fires (which will place the same control within the MainRegion above).   So this explains the increased performance for the Shell's load of the control


Figure C. 

So what about the lengthy delay?   If you were to put a break point after InitializeModules you would find that the Shell's control is visible and populated while the Regions control is still not visible - our processing logic within the controls code-behind is bogging down the UI thread. 

What we need to do is pull the Data Access Layer (populating 1000 elements) out of the presentation layer and have it process asynchronously on a separate thread.   In the case of my refactored code I update the model which the MainRegions control is databound to.

Applying the Model-View-Presenter (MVP) pattern against the control we now only have to have the following code to populate our model (which the view is bound to).  The PresenterBase subscribes to an event raised in the Bootstrapper when modules are initialized so that it can start the asych process of populating the model.TestEntities property (handled by OnProcessEvent below).  Since model.TestEntities is an observableCollection WPF will automatically be notified as the collection changes - which is why we pass it in as a parameter after every 15 elements we allow event processing to continue which effectively will refresh our Grid display every 15 elements (more on this below).


Figure D.

Below is our refactored BootStrapper which launches a new thread to handle the data processing


Figure E.

Note how on line 17 of Figure D above that we are using Dependency Injection (DI) to get our data access layer.   In the Services folder we implement IEntityDataService using stubbed data - later the actual data layer could eaily replace the "stub" by simply updating the modules Initialize method to reflect the new implementation - in the case of this demo we are using EntityDataStub.

public void Initialize()
{
    container
        .RegisterType<
IMyControl, MyControl>()
        .RegisterType<
IEntityDataService, EntityDataStub>()
        .RegisterType<
IMyControlPresentationModel, MyControlPresentationModel>(
        new ContainerControlledLifetimeManager()); // Singleton

Notice on line 21 below we are borrowing a feature from System.Windows.Forms.   The Application.DoEvents() method permits other events to process.  In the absence of this the below process is hogging up the CPU cycles and will not give other events a chance to process until it is completed - on lengthy delays your form could "stop responding".  As a result of this command our grid will refresh every 15 elements which will be reflected by the behavior of the scrollbar.

Architectural note:  This demo uses a combination of the Model-View-Presenter (MVP) pattern and the Model-View-ViewModel (MVVM) pattern, aka Presentation Model and Application Model.    By having multiple views share the same model (see Martin Fowlers Presentation Model) you can effectively share the same data without having to have a lot of complex logic to maintain state.   Each view can update the model and the other views will be notified via the observer pattern (INotifyPropertyChanged).

Trying to use MVVM alone has introduced the limitations that Martin Fowler discussed in THIS ARTICLE (paragraph above Figure 11).   As he suggest, it was the limitations that introduced the need for MVP.   Combining them gives us the best of both worlds.  

 


Tags: , ,
Categories: CompositeWPF


Actions: E-mail | Permalink |  Grammar/Typo/Better way? Please let me know