XAF - Startup Performance, Application Model and Non-Persistent Objects Enhancements (Coming soon in v16.1)

In my opinion, XAF's Application Model (defined as a set of application settings or UI skeleton) is the second most important XAF feature (first being automatic database and CRUD forms generation based on your ORM data model).

Because of its importance, we consistently strive to improve the Application Model and in our upcoming release, we focused our energies on improving startup application performance and general usability for XAF developers.

Application Model Caching

With v16.1, you can speed up WinForms application startup using the EnableModelCache property. When set to true, Application Model content is cached to a file when the application is first launched and recovered on subsequent runs. This property works in a production environment when the debugger is not attached (see Debugger.IsAttached). By default, EnableModelCache is set to false. To change this property value, add the following code to the Program.cs (Program.vb) file, before the Setup method is called: winApplication.EnableModelCache = true;

As a result of these settings, Nodes Generators and Generator Updaters will be executed and the Model.Cache.xafml cache file will be created only when the application is started for the first time. Note that initial startup can take more time than normal, however, the time taken by subsequent startups will be reduced, because the Application Model content will be recovered from the cache. The cache is recreated when application module version is incremented. 


By default, the Model.Cache.xafml file is located in the application folder. You can change its path using one of the following methods:
  1. override the GetModelCacheFileLocationPath method in XafApplication descendant;
  2. use the ModelCacheLocation key in the configuration file (App.config).

The Model.Cache.xafml file name is specified by the ModelStoreBase.ModelCacheDefaultName constant. If your application is localized, separate cache files are created for each language (e.g. Model.Cache_de.xafml).

    

Frequently Asked Questions

Q: How is this different from the existing ModelAssembly.dll-based cache?
A: Unlike the existing ModelAssembly file-based cache (which is technically an assembly providing Application Model object implementation based on the IModelXXX interfaces), the new model cache file contains the entire model structure in XAFML format.

Q: Is this cached file an equivalent of merging of the platform agnostic model and WinForms/ASP.NET models?  Does this include user modifications?
A: No, this Model.Cache.xafml only contains an unchangeable Application Model layer. Administrative (Model.XAFML) and user-specific (e.g. Model.User.XAFML) model differences are not cached (they are meant to be modified once the app is deployed).

Q: Is generation of this cache file somehow connected with database schema changes?
A: No.

Q: Can this cached file be supplied with the installer?
A: Absolutely. You can generate this file by running your app without the debugger attached and then copy it into your installation program. This manual process may be unnecessary in many cases because the cache file will still be automatically generated after the released application is executed for the first time.

Q: What about this feature for ASP.NET?
A: By default, the EnableModelCache property has no effect on an ASP.NET application since a shared application model is usually generated once for all web clients. If you wish, you can manually activate the creation of this cache file by overriding the GetModelCacheFileLocationPath method of your WebApplication descendant.


Application Model Database Storage Enhancements

As you know, XAF apps can store user-defined application customizations (layouts, selected skin, etc.) in an application database with the use of the ModelDifferenceDbStore class introduced several releases ago. In this release, we've enhanced functionality in the following ways:

  • Design-time customizations are now always loaded from the Model.xafml file stored in the file system to simplify debugging.
    In other words, for administrative model differences we have returned to the schema we used prior to introduction of ModelDifferenceDbStore. Only user-specific model differences are loaded from the database by default. When required, you can uncomment the XafApplication.CreateCustomModelDifferenceStore event subscription manually. Note that Model.xafml file content will be loaded to the database once the application starts. Further changes to this file will be ignored if the database record already exists for shared model differences. To reload settings from Model.xafml, enable the administrative UI and use the Import Shared Model Difference Action (or delete the Shared Model Difference record and restart).

  • ModelDifferenceDbStore can now be used when the Security System is disabled.
    Currently, if the Security System is enabled, the SecuritySystem.CurrentUserId value is used as the identifier.  The System.Security.Principal.WindowsIdentity.GetCurrent().Name value is used as a user identifier (passed to the IModelDifference.UserId property) when the Security System is disabled. So, you can enable ModelDifferenceDbStore for WinForms applications with the disabled Security System using the approach described here. However, we do not recommended that you enable ModelDifferenceDbStore for unsecured ASP.NET applications because the UserID will be the same for all users. Shared model differences are supported for both WinForms and ASP.NET when the Security System is disabled. 

  • XML settings are now validated before being persisted.
    This is a small usability improvement to simplify error debugging when using ModelDifferenceDbStore and when the administrative UI for editing model differences is enabled in the application as described here. Application administrators will not be able to save invalid XML, e.g. with unclosed tags.

    


Non-Persistent Objects Enhancements

We continue incorporating non-persistent objects support implemented in our most recent releases with the help of the NonPersistentObjectSpace and NonPersistentObjectSpaceProvider classes.

With this release, you can display persistent objects in a non-persistent object view with much less code. This is possible with the help of the AdditionalObjectSpaces property in the NonPersistentObjectSpace class. It is best to illustrate this improvement with a short code snippet:

...
[DomainComponent, DefaultClassOptions]
public class NonPersistentObject {
    // ... 
    public string Name { get; set; }
    public Person Owner { get; set; }
...
public class AdditionalPersonObjectSpaceController : WindowController {
    private IObjectSpace additionalObjectSpace;
    protected override void OnActivated() {
        base.OnActivated();
        Application.ObjectSpaceCreated += Application_ObjectSpaceCreated;
        additionalObjectSpace = Application.CreateObjectSpace(typeof(Person));
    }
private void Application_ObjectSpaceCreated(Object sender, ObjectSpaceCreatedEventArgs e) {
        if (e.ObjectSpace is NonPersistentObjectSpace) {
            ((NonPersistentObjectSpace)e.ObjectSpace).AdditionalObjectSpaces.Add(additionalObjectSpace);//!!!
        }
    }
...


Once XAF v16.1 is released, refer to the "How to: Show Persistent Objects in a Non-Persistent Object's View" article in the online documentation for a full tutorial and additional explanations. Also notice the new ORM-agnostic DevExpress.ExpressApp.Data.Key attribute we introduced to mark unique key properties of an object type.

    

Another improvement involves showing a non-persistent Detail View: Separately (e.g., from a navigation control or in a popup window) or Embedded (inside a DashboardView item). With XAF v16.1, you can handle the new ObjectByKeyGetting event of the NonPersistentObjectSpace class to provide a non-persistent object instance manually. As an example, consider a scenario when a navigation item pointing to a non-persistent class DetailView was added under the NavigationItems node and its ObjectKey parameter was specified in the Model Editor as shown below:

    

You can now add the following Controller to handle this scenario:

...
public class NonPersistentObjectsController : WindowController {
    protected override void OnActivated() {
        base.OnActivated();
        Application.ObjectSpaceCreated += Application_ObjectSpaceCreated;
    }
    private void Application_ObjectSpaceCreated(object sender, ObjectSpaceCreatedEventArgs e) {
        NonPersistentObjectSpace nonPersistentObjectSpace = e.ObjectSpace as NonPersistentObjectSpace;
        if(nonPersistentObjectSpace != null) {
            nonPersistentObjectSpace.ObjectByKeyGetting += nonPersistentObjectSpace_ObjectByKeyGetting;
        }
    }
    private void nonPersistentObjectSpace_ObjectByKeyGetting(object sender, ObjectByKeyGettingEventArgs e) {
        if(e.ObjectType.IsAssignableFrom(typeof(NonPersistentObject))) {
            if(((int)e.Key) == 138) {
                NonPersistentObject obj138 = new NonPersistentObject();
                obj138.Name = "Sample Object";
                e.Object = obj138;
            }
        }
    }
...


If you want to create a non-persistent object automatically for each Detail View, you do not need to specify an ObjectKey in the Model Editor. Leave the ObjectKey value empty and create the following View Controller, which will create a non-persistent object

...
public class NonPersistentObjectActivatorController : ViewController<DetailView> {
    protected override void OnActivated() {
        base.OnActivated();
        if(ObjectSpace is NonPersistentObjectSpace) {
            View.CurrentObject = View.ObjectTypeInfo.CreateInstance();
        }
    }
}
...


On a final note, let me add that custom calculated fields are now supported as well. In other words, you can define custom fields  for non-persistent types in the Model Editor with the Expression attribute like NonPersistentProperty1 + NonPersistentProperty2.


Several types have changed their host assemblies without changes in namespaces

This is just a formal notification as this change should not be "breaking" or affect you at all in the majority of cases. Refer to the Several types have been moved to the DevExpress.Persistent.Base and DevExpress.Persistent.BaseImpl assemblies from the Charts, ConditionalAppearance, KPI and StateMachine modules in XAF v16.1 KB Article for more details.

10 comment(s)
Kim_Schmidt

Great news! I'm really looking forward to the new model cache feature.

24 May, 2016
Dennis (DevExpress Support)

@Kim: You're welcome to try this feature with the beta bits we've just provided in the Download Manager (www.devexpress.com/.../downloads) and tell us what you think. Thanks!

24 May, 2016
Alex Miller

Hi Dennis,

The speed increase is amazing. I wish we could have that benefit when debugging. Any chance we'll be able to use that feature with a debugger attached?

I'm a little worried about manual modifications. Is there any way we can modify the XAML file reading/writing? Perhaps if we could load it from the resources?

About providing the file with the installer, I was hoping we would save the original file creation. Any chance you could check if it's there before generating it?

Thanks.

Alex

24 May, 2016
Konstantin B (DevExpress)

@Alexandre:

> Any chance we'll be able to use that feature with a debugger attached?

Model caching makes no sense when debugging. Modules' versions increment each time you make any changes and rebuild, and the cache will be recreated on each run.

>Is there any way we can modify the XAML file reading/writing?

Currently, there is no such an option. We will consider adding it in future updates.

> About providing the file with the installer, I was hoping we would save the original file creation.

Simply run your application and close it before using the "xcopy" deployment method or packing the installer.

25 May, 2016
Alex Miller

@Konstantin

It makes sense the cache would be re-created when we make changes to XAF modules. What if we make changes to other satellite DLLs? The Model and cache should not be affected. Saving 1/100 loading time is better than nothing.

Perhaps I'm not understanding the cache properly. What would make the cache need to change in a production environment?

Can we move this to a trackable ticket? Manually monitoring these comments is inefficient.

Thanks,

Alex

25 May, 2016
Konstantin B (DevExpress)

@Alexandre: I have created the Support Center ticket on your behalf - T384030.

25 May, 2016
Jim Horvath

Is the app model caching just for WinForms, or does it help startup times for Web apps too?

22 June, 2016
Dennis (DevExpress Support)

@Jim: From this blog post:

"Q: What about this feature for ASP.NET?

A: By default, the EnableModelCache property has no effect on an ASP.NET application since a shared application model is usually generated once for all web clients. If you wish, you can manually activate the creation of this cache file by overriding the GetModelCacheFileLocationPath method of your WebApplication descendant."

22 June, 2016
Jim Horvath

@Dennis:  Q:  Would doing so (enabling the model cache) reduce startup time for my ASP.NET web app pool?  My startup time (granted, on a shared server) is currently over 1 minute.  That's a long time for me to hold my breath.  :)

23 June, 2016
Dennis (DevExpress Support)

@Jim: Yes. Please inform us of your results via the Support Center. In addition, you may want to look at the blogs.iis.net/.../now-available-the-iis-7-5-application-warm-up-module feature.

23 June, 2016

Please login or register to post comments.