I will explain what XAF is just for the sake of the consistency of this article, XAF is a low code application framework for line of business applications that runs on NET framework (windows forms and web forms) and in dotnet (windows forms, Blazor and Web API)
XAF is laser focus on productivity, DevExpress team has created several modules that encapsulate design patterns and common tasks needed on L.O.B apps.
The starting point in XAF is to provide a domain model using an ORMs like XPO or Entity framework and then XAF will create an application for you using the target platform of choice.
It’s a common misunderstanding that you need to use and ORM in order to provide a domain model to XAF
DevExpress team has created ObjectSpace abstraction so XAF can be extended to use different data access technologies ( you can read more about it here https://docs.devexpress.com/eXpressAppFramework/DevExpress.ExpressApp.BaseObjectSpace)
Out of the box XAF provide 3 branches of object spaces as show is the graph below.
XPObjectSpace: this is the object space that allows you to use XPO as a data access technology.
EfCoreObjectSpace: this is the object space that allows you to use Microsoft Entity Framework as a data access technology.
NonPersistenObjectSpace: this object space is interesting as it provides the domain model needed for XAF to generate the views and interact with the data is not attached to an ORM technology so it’s up to us to provide the data, also this type of object space can be used in combination with XPObjectSpace and EfCoreObjectSpace
When querying external data sources, you also need to solve the problem of filtering and sorting data in order to provide a full solution, for that reason DevExpress team provide us with the DynamicCollection class, that is a proxy collection that allows you to filter and sort an original collection without changing it.
Now that we know the parts involved in presenting data in a XAF application, we can define the required flow.
- Provide XAF a domain model: https://github.com/DevExpress-Examples/XAF_Non-Persistent-Objects-Filtering-Demo/blob/22.2.5%2B/CS/XPO/NonPersistentFiltering/NonPersistentFiltering.Module/BusinessObjects/Article.cs
[DefaultClassOptions] [DefaultProperty(nameof(Article.Title))] [DevExpress.ExpressApp.ConditionalAppearance.Appearance("", Enabled = false, TargetItems = "*")] [DevExpress.ExpressApp.DC.DomainComponent] public class Article : NonPersistentObjectBase { internal Article() { }
- Wire the application setup completed event in the agnostic module: https://github.com/DevExpress-Examples/XAF_Non-Persistent-Objects-Filtering-Demo/blob/22.2.5%2B/CS/XPO/NonPersistentFiltering/NonPersistentFiltering.Module/Module.cs
public override void Setup(XafApplication application) { base.Setup(application); // Manage various aspects of the application UI and behavior at the module level. application.SetupComplete += Application_SetupComplete; }
- Wire the application object space created event.
private void Application_SetupComplete(object sender, EventArgs e) { Application.ObjectSpaceCreated += Application_ObjectSpaceCreated; } private void Application_ObjectSpaceCreated(object sender, ObjectSpaceCreatedEventArgs e) { var npos = e.ObjectSpace as NonPersistentObjectSpace; if (npos != null) { new ArticleAdapter(npos); new ContactAdapter(npos); } }
- Write the ObjectSpaceObjectsGetting event of each object space created. https://github.com/DevExpress-Examples/XAF_Non-Persistent-Objects-Filtering-Demo/blob/95e5eb82c0d1834721a315c9438918ba5fb01c42/CS/XPO/NonPersistentFiltering/NonPersistentFiltering.Module/BusinessObjects/Article.cs#L44
public ArticleAdapter(NonPersistentObjectSpace npos) { this.objectSpace = npos; objectSpace.ObjectsGetting += ObjectSpace_ObjectsGetting; }
- On the ObjectSpaceObjectsGetting event, create a dynamic collection that will be filled on demand by wiring the FetchObjects event of the dynamic collection. https://github.com/DevExpress-Examples/XAF_Non-Persistent-Objects-Filtering-Demo/blob/95e5eb82c0d1834721a315c9438918ba5fb01c42/CS/XPO/NonPersistentFiltering/NonPersistentFiltering.Module/BusinessObjects/Article.cs#L44
private void ObjectSpace_ObjectsGetting(object sender, ObjectsGettingEventArgs e) { if(e.ObjectType == typeof(Article)) { var collection = new DynamicCollection(objectSpace, e.ObjectType, e.Criteria, e.Sorting, e.InTransaction); collection.FetchObjects += DynamicCollection_FetchObjects; e.Objects = collection; } } private void DynamicCollection_FetchObjects(object sender, FetchObjectsEventArgs e) { if(e.ObjectType == typeof(Article)) { e.Objects = articles; e.ShapeData = true; } }
Full source code here
In conclusion the ObjectSpace abstraction ensures that different data access technologies can be employed, while the DynamicCollection class allows for seamless filtering and sorting of data from external sources. By following the outlined steps, developers can create robust, adaptable, and efficient applications with XAF, ultimately saving time and effort while maximizing application performance.