Blogs

Gary's Blog

XAF 10.1 Sneak Peek

     

One of the major features that you will see as of 10.1 is the Typed Application Model.

As you will know XAF allows developers to define business class and then builds the http://documentation.devexpress.com/#Xaf/CustomDocument2580 from these declarations and generates a UI based on them. Thereafter if you want to change the UI you simply customise the Application Model using the http://documentation.devexpress.com/#Xaf/CustomDocument2582. The Application Model is therefore a cornerstone of XAF and we have re-designed it from the bottom up in 10.1.

Firstly, let’s take a look at what the Application Model is like now, prior to 10.1. It is currently defined by it’s schema and looks something like this:

<Element Name="Application">
<
Element Name="Views">
<
Element Name="ListView">
<
Element Name="Variants" >
<
Attribute Name="Current" />
<
Element Name="Variant" >
<
Attribute Name="ID" />
<
Attribute Name="Caption" />
<
Attribute Name="ViewID" />
</
Element>
</
Element>
</
Element>
</
Element>
</
Element>

This snipped declares the Variants node, as a child node of the ListView node. As you can see, in this simplified example, the only thing that attributes haveare names. There are no types declared. So, even if the ID attribute is intended to hold an integer and Caption to hold a string, there is really no difference. The untyped Application Model stores its data as a set of string. For a caption this is fine, for an integer though, it’ll first need to be converted to a string representation. When retrieving its value, it’ll need to be converted again from a string to an integer. What’s more, you need to remember that an integer is stored there, because all the Application Model stores are plain strings. What a PITA!

Now let’s take a look at how the Application Model worked under the hood. This’ll be a pretty simplified look but it’ll do for our purposes. When an application is started, the types info subsystem (see http://documentation.devexpress.com/#Xaf/CustomDocument3224) collects metadata on all the business classes declared in the application. After this, the Application Model generation is started. Firstly, the BOModel node’s child nodes are generated and filled with values from the types info subsystem. Then all the other Application Model’s nodes and their attributes are filled with data.

After this initial generation is complete, the Application Model is filled with custom data from all the Modules used in the application.

At this point we have the initial Application Model, that part which can’t be changed by the user. Then the Application Model is filled with each user’s customizations . Now, the Application Model generation is complete, and we can use our application.

Now that we know how our untyped Application Model works, we can see the disadvantages:

  1. We COMPLETELY generate ALL THE Application Model at application startup. This is slow.
  2. Memory consumption is far from being optimal. Each attribute contains a string value, not a reference. Usually, there are a lot of duplicate info. For example, a string holding the name of a business class can be found in: types info subsystem, a BOModel node’s attribute, an attribute of each of the Views declared for the class (usually, there are several Views for each business type, at least two or three) and so on.
  3. The Application Model is untyped, and working with it isn’t straightforward. Though we can use node wrappers, the process is generally bulky.

So, in general, the current Application Model, is suboptimal. :-)

But it is improved in 10.1 and here’s how. What we’ve done is to do away with the schema and we now use interfaces instead. So the example above would now be declared like so:

public interface IModelViewVariants : IModelNode
{
IModelVariants Variants { get; set; }
}

public interface IModelVariants : IModelNode,
IModelList<IModelVariant>
{
IModelVariant Current { get; set; }
}
public interface IModelVariant : IModelNode
{
int Id { get; set; }
string ViewID { get; set; }
string Caption { get; set; }
}

Now as you see, attributes not only have names, but they also have types. Also, there’s no need to use node wrappers or convert attribute values to and from string representations. That is much better, don’t you think?

The basic Application Model structure is described by the base IModelApplication interface. This interface defines the root Application node (its attributes and child nodes). Each Module can also extend the Application Model. This is done by specifying additional interfaces derived from the IModelNode interface.

So, firstly, when an application is started, we collect all the interfaces that define the Application Model. Then we compile an object implementing all these interfaces. This object represents the Application Model. At this point the Application Model doesn’t hold any actual data.

Secondly, we fill the Application Model with layers. Our new typed Application Model consists of unmerged layers. Each layer represents a separate data set. Each Module is represented by its own data layer. So, for each Module we create a separate layer and fill it with data supplied by the Module. We also create the user customizations layer and load the user customizations into it. Note that the base Application Model layer defined by the IModelApplication interface doesn’t contain any data at this point.

At this point, the Application Model is ready to be used. When, for example, the Logon Detail View needs to be invoked, the Application Model is accessed, to retrieve the Detail View’s layout. The following code snippet illustrates this:

IModelView modelView = 
Application.ModelApplication.Views[
“Logon_DetailView”];

Since the Views node hasn’t been generated yet, a Views generator is invoked which in turn invokes the BOModel generator. As a result, the Views and BOModel nodes’ child nodes are created. Note however, that the created child nodes are NOT filled with data. Then, since we asked for the “Logon_DetailView” data, this node gets populated with data on the base layer. After this, XAF displays the specified View using the data.

As you can see, the base layer of the Application Model is populated with data on-demand. In other words, if you launch an XAF application, and use only a Contact List View then only the Contact_ListView node will be populated with data. All the other View nodes of the Application Model will remain uninitialized.

A note on ASP.NET Web XAF applications. Now, if users haven’t customized the Application Model, a single Application Model instance is shared between all the users. Moreover, even if users have customized the Application Model, the common part of it is still represented by a single instance which is also shared between the users.

Now that we know how our typed Application Model works, we can outline its advantages:

  1. We never completely generate all the Application Model. It’s created on demand (kind of a lazy initialization). This is fast (or at least we hope it will be).
  2. Memory consumption is much more optimal. Each attribute holds a reference, not another value copy. So, for example, ideally, a string holding the name of a business class could only be found in the types info subsystem. In ASP.NET Web XAF applications, most of the Application Model (and sometimes ALL the Application Model) is shared between users.
  3. The Application Model is typed, and working with it becomes more straightforward. No more need for string conversions and use of additional artificial wrappers.

Okay, so let’s finish up with some before and after examples:

Defining a key:

Before:

<Element Name="Variant" KeyAttribute="ID" />
After:
[KeyProperty("Id")]
public interface IModelEditorStateRule : IEditorStateRule {
string Id { get; set; }
}

Declaring multiple child nodes:

Before:

<Element Name="ListView">
<
Element Name="Variants">
<
Element Name="Variant" Multiple="True" />

After:

public interface IModelViewVariants : IModelNode {
IModelVariants Variants { get; set; }
}
public interface IModelVariants :
IModelNode, IModelList<IModelVariant> {
}
public interface IModelVariant : IModelNode { .. }

Declaring a localisable attribute:

Before:

<Element Name="Variant">
<
Attribute Name="Caption" IsLocalized="True" />

After:

public interface IModelVariant : IModelNode {
[Localizable(true)]
string Caption { get; set; }
}

Specifying an image for an node:

Before:

<Element Name="Variant" ImageName="ModelEditor_ListView">

After:

[ImageName("ModelEditor_ListView")] 
public interface IModelVariant : IModelNode {...

Specifying the display property:

Before:

<Element Name="Variant" DisplayAttribute="Caption" />

After:

[DisplayProperty("Caption")]
public interface IModelVariant : IModelNode {...

Specifying a required attribute:

Before:

<Element Name="Variant" >
<
Attribute Name="ViewID" Required="True" />

After:

public interface IModelVariant : IModelNode {
[Required()]
string ViewID { get; set; }

Specifying a child node’s Index attribute used to order child nodes:

Before:

<Element Name="Variant" >
<
Attribute Name="Index" IsNewNode="True"/>

After:

Nothing to do here as the base IModelNode interface already declares such an attribute.

Using an enumeration to specify the possible values for an attribute displayed in a dropdown:

Before:

<Element Name="NavigationItems" >
<
Attribute Name=""
DefaultChildItemsDisplayStyle=""""
Choice=""List,LargeIcons=""""/>

After:

public interface IModelNavigationItems : IModelNode {
ItemsDisplayStyle DefaultChildItemsDisplayStyle { get; set; }

Getting data from the Application Model. Retrieving a node:

Before:

DictionaryNode viewsNode = 
Application.Model.RootNode.GetChildNode("Views");
DictionaryNode viewNode =
viewsNode.FindChildNode(
BaseViewInfoNodeWrapper.IdAttribute, viewId);

After:

IModelView modelView =
    Application.ModelApplication.Views[viewId];

Getting data from the Application Model. Retrieving an attribute value:

Before:

DictionaryNode viewNode = 
viewsNode.FindChildNode(
BaseViewInfoNodeWrapper.IdAttribute, viewId);

string myCustomFilter =
viewNode.GetAttributeValue("MyCustomFilter");

After:

IModelViewMyExtention myModelView = 
(IModelViewVariants)
Application.ModelApplication.Views[viewId];

string myCustomAttrValue = myModelView.MyCustomFilter;

 

Well that’s all for this post, until next time – happy XAF-ing! :-)

Published Feb 08 2010, 06:12 PM by Gary Short (DevExpress)
Filed under: , ,
Technorati tags: V2010 V1, XAF, Sneak Peak
Bookmark and Share

Comments

 

Mohsen Benkhellat said:

Hi,

Beside having no need anymore to store the model in the DB explicitly, I have a feeling it will help in this scenario:

A custom logon view to be able to choose database and then logon.

With current XAF, it does work but it is more like hack than a solution.

Am I right in this?

Mohsen

February 8, 2010 2:14 PM
 

dani said:

Great, looks like xaf is returning to the right track. I'm sure this will bring hope to those who lost the faith in the future of XAF.

February 8, 2010 2:32 PM
 

Nate Laff said:

So, model.xafml is gone? I"m confused.

February 8, 2010 3:19 PM
 

Roman Eremin (DevExpress) said:

We did not made any radical changes in how model is stored - just changed internal logic and interface that you use to work with model.

So xafml files will still be a way to store model.

February 8, 2010 3:56 PM
 

Nate Laff said:

So the model editor/ Different? I guess i'll need to see it in action to wrap my head around it.

February 8, 2010 4:00 PM
 

cncev said:

Is it like SharpDevelop?

February 8, 2010 11:30 PM
 

Steve Sharkey said:

Am I the only one thinking:

+1 Faster startup

+2 More efficient memory management

-1 Potentially breaking changes

The rest is under the covers stuff that whilst interesting I don't need to know afterall that is why I am considering using XAF!

February 9, 2010 3:26 AM
 

Binish Peter said:

As I understand the collecting of XAF attributes and generating BOModel and applying its customization will always be done On-Demand, so how about the customizations that change the XPDictionary?

February 9, 2010 3:28 AM
 

Marcello said:

I'm excited for this improvement, but I'm also anxious for the breaking changes. Hopefully good! :-)

February 9, 2010 4:55 AM
 

Nate Laff said:

Mohsen:

"Beside having no need anymore to store the model in the DB explicitly"

Can you explain? Thanks!

February 9, 2010 7:43 AM
 

Mohsen Benkhellat said:

Nate:

My understanding (and of course I may be wrong) is that by using the interfaces and strong types to persist and query the model confirms what Roman said some time ago that they will be using the same technology for the model just like for any other business object.

This will allow for instance to apply the same validation rules as well as use XAF views to interact with the model.

Feel free to correct me if I am wrong.

Mohsen

February 9, 2010 9:30 AM
 

Nate Laff said:

I was just trying to get what you meant :) Thanks for explaining!

February 9, 2010 9:43 AM
 

david@shannon.name said:

"by using the interfaces and strong types to persist and query the model ... they will be using the same technology for the model just like for any other business object."

Is that right?  I don't really understand this business about the model being stored in the DB.  I thought it was generated based on the code, then modified based on the xafml files.  Nothing in the DB except a reflection of the BO schema and the module versioning.

February 9, 2010 11:39 AM
 

david@shannon.name said:

Just to be clear.  I'm not saying it's wrong at all.  I really mean it when I say I don't understand the part about storing or persisting the model.  I'm also not clear yet on how we use this system to modify the model on the fly.

February 9, 2010 11:58 AM
 

Nate Laff said:

David,

THat's where I got lost, too.

Also not seeing how end-users make model changes. That's where I need to see this thing in action to really grasp it I guess.

February 9, 2010 1:31 PM
 

Mohsen Benkhellat said:

Saving the model in the DB is not new and something some of us have already implemented using a sample from DX even with before 9.1 version.

I agree that the model initially is reflected from assemblies then merged with global customizations and the result is in Model.xafml.

Futher customizations done by user in runtime are in Mode.User.xafml.

Now, the point is that if you want to load/save those files from/in DB, you just have to override a few events of XafApplication class.

The only difference is that this will be easier to do with 10.1 but by no means the only way to store the model. DX could provide as many persistence store implementations as they wish be it in Xml files, in DB or WebService.

I know it can be confusing at times but hopefully we will understand more when we actually play with it.

Of course Romain could bring a lot more precise information about this to help us understand.

Mohsen

February 9, 2010 3:09 PM
 

Mohsen Benkhellat said:

I finally found the post from Roman.

community.devexpress.com/.../292889.aspx

and here is an extract:

"If you mean changes in the model we making - now it looks very similar to DC technology - you specify model scheme by registering interface. So having that we can reuse all the data presentation power of XAF UI to edit model - you will be able to browse, say, list of views the same way you browse the list of orders. So next step - somebody may want to store particular "branch" of model tree in a database as persistent objects, not just blob with XML - and we don't expect big technical problems to allow that"

Mohsen

February 9, 2010 3:29 PM
 

Steve Sharkey said:

I'd just like to know the positioning of XAF as some of the emphasis on "the community extending and tinkering with the model" is not what suits me. I know there is always a trade off of things that the framework can't do so you need to be able to extend the frame work but if the number of these gets too large I tend to find I can build the application faster using my own "framework" application which is easier to extend than someone elses work. I understand that many are interested in this as an exercise but the message of what XAF IS should be kept clear to (prospective and actual) customers.

It is all a trade off with tools:

project = x days of work

buying tool saves y days as it helps with the first n% of the project.

Extending tool to do the last 100-n% = z days

If (x-y+z) > x then DONT BUY/USE THE TOOL

I often have found that whilst XAF used to have a high y figure, I can now do these somewhat quicker (using DX components) and it is usually counter balanced by the fact that z is also quite high. Even when the balance is pretty even the lure of a completely customised interface tips the balance against XAF.

If I didn't have DevX components otherwise then XAF would look more attractive.

I guess in the end I'd have to say I could get the time z down if I really committed myself to XAF but I am reluctant to do this because I have hit too many stumbling blocks in the past where either I struggle to achieve something that would otherwise be easy or I'm trying to remember for the nth time how to do something that is just not so intuitive (like changing graphics for a, b or c. Perhaps I should add these tips to a crib sheet for use in new applications.... Might be of use to others too....

February 10, 2010 3:39 AM
 

Robert Fuchs said:

> Perhaps I should add these tips to a crib sheet for use

> in new applications.... Might be of use to others too....

Steve, can I have it, please?

February 10, 2010 10:37 AM
More from DevExpress
Live Chat
Have a pre-sales question?
Need assistance with your evaluation?
We are here to help.
Chat is one of the many ways you can contact members of the DevExpress Team. We are available Monday-Friday between 8:30am and 5:00pm Pacific Time.
If you need additional product information, require pre-sales assistance, or want help with your order, write to us at info@devexpress.com or call us at
+1 (818) 844-3383.