Tuesday, January 29, 2013

Dynamic member aliases from Application Model

Although I promised to talk about Stephen Manderson’s Dashboard module I will skip and I will provide a discussion on dynamic member aliases. The reason is that cross data source filtering provided by the Dashboard Suite does not permit aliases for our aggregated members. I already touched dynamic members subject at calculated members creation—Pros and Cons. However today we will go in details through all steps involved in creating member aliases through XAF’s Application Model and as usual at the end of the post a sample will be available.

In the following image you can see existing implementations from our community project eXpand as discussed in calculated members creation—Pros and Cons.

image_thumb[14]

In this post we are only interested in the RuntimeCalculatedMember entry.,

Extending the model

Following our documentation to the letter (How to: Extend the Application Model) first we need to define and interface that will persist all parameters to the model.

public interface IModelRuntimeCalculatedMember : IModelMember {

    [Required]

    string AliasExpression { get; set; }

}

 

The context popup menu

You probably noticed in IModelRuntimeCalculatedMember instead of deriving from IModelNode interface as suggested in our docs we used the IModelMember. This is an already registered interface that describes the model members. So do not need to re extend our Application Model with the IModelRuntimeCalculatedMember. XAF knows how to create the popup menu with the correct entry:

 

image

 

Changing Model Editor’s behavior

 

AliasExpression

 

In IModelRuntimeCalculatedMember we marked the AliasExpression property with the RequiredAttribute because an empty alias is not valid. XAF Model Editor will notify that this is a mandatory property with an asterisk icon as shown:

 

image

Editable Type at Runtime

Since our IModelRuntimeCalculatedMember derives from IModelMember and not from IModelNode it inherits all its properties. This however, raises a conflict with XAF’s native support for design time custom members. The conflict refers to IModelMember Type property which is by design editable only in design time. As we see in the next image is marked with a ModelReadOnlyAttribute which tells Model Editor what to do.

image

In XAF everything is overridable! So to change Type property we need to create a new Type property in our IModelRuntimeCalculatedMember and mark it with the new keyword. In addition we need to create and use an AlwaysEditableCalculator  instead of the DesignerOnlyCalculator:


image

Remember the IsCustom functionality

As I already mentioned XAF has native support for runtime members only if Model Editor is at design time. This is done adding a new IModelMember and setting IsCustom to true as shown:

image

image

IModelRuntimeCalculatedMember inherits from IModelMember. This means when we create a new IModelRuntimeCalculatedMember XAF will set IsCustom to true as it does for simple IModelMember. ModuleUpdaters that can change Application Model’s values are designed to work only in the “zero” layer and here we need to change the differences made from the Model Editor designer. However XAF is designed to be extensible and our docs to solve the problems Convert Application Model Differences. The solution to this problem is to implement Implement a Node Updater following our docs to the letter:

image

NodeUpdaters are one more powerful tool provided to us by XAF an is designed to take us out of trouble. As you can see in above image it does it fairly simple.

  1. We make our module or any class a NodeUpdater by implementing the IModelNodeUpdater<T> interface.
  2. We register the new NodeUpdater by overriding the AddModelNodeUpdaters or our module.
  3. Implement our logic inside the the UpdateNode method

The Dessert

The AliasExpression property will hold basic criteria + complex criteria as well as aggregated function. Right now there is no editor associated with the property. However we can easily associate a CriteriaModelEditorControl editor as shown:

image

As we see the CriteriaModelEditorControl offers great features:

image

The Coding part 

Up to now we modeled a new Application Model member type the IModelRuntimeCalculatedMember. What remains is to write the algorithm to create that member in our TestBO object. Unfortunately we cannot use the standard place for extending our business objects as suggested by our knowledgebase. This is because the Application Model is not fully constructed at that point. However we can use any other place, as far as our algorithm is smart enough to execute just one time without consuming many resources. 

private static IEnumerable<IModelRuntimeCalculatedMember> GetCustomFields(IModelApplication model) {

    return model.BOModel.SelectMany(modelClass => modelClass.AllMembers).OfType<IModelRuntimeCalculatedMember>();

}

 

static void AddRuntimeMembers(IModelApplication model) {

    foreach (IModelRuntimeCalculatedMember modelRuntimeMember in GetCustomFields(model))

        try {

            Type classType = modelRuntimeMember.ModelClass.TypeInfo.Type;

            XPClassInfo typeInfo = _dictionary.GetClassInfo(classType);

            lock (typeInfo) {

                if (typeInfo.FindMember(modelRuntimeMember.Name) == null) {

                    new XpandCalcMemberInfo(typeInfo, modelRuntimeMember.Name, modelRuntimeMember.Type, modelRuntimeMember.AliasExpression);

                    XafTypesInfo.Instance.RefreshInfo(classType);

                }

            }

 

In fact forget about the many resources when using our frameworks, Please search our Support Center, there are answers to almost all common problems you will face! If not shoot the guys they are happy to die for you Winking smile!

Now let’s touch the unknown XpandCalcMemberInfo class:

  1. XPO allows non persistent calculated properties with the use of PersistentAliasAttribute.
  2. To create a dynamic member we simply need to instantiate an XPCustomMemberInfo derivative like the  XpandCalcMemberInfo.

public class XpandCalcMemberInfo : XPCustomMemberInfo {

    public XpandCalcMemberInfo(XPClassInfo owner, string propertyName, Type propertyType, string aliasExpression)

        : base(owner, propertyName, propertyType, null, true, false) {

        AddAttribute(new PersistentAliasAttribute(aliasExpression));

    }

 

    public override object GetValue(object theObject) {

        var xpBaseObject = ((XPBaseObject)theObject);

        return !xpBaseObject.Session.IsObjectsLoading && !xpBaseObject.Session.IsObjectsSaving

                   ? xpBaseObject.EvaluateAlias(Name)

                   : base.GetValue(theObject);

    }

 

    protected override bool CanPersist {

        get { return false; }

    }

}

 

Therefore in the constructor we added a PersistentAliasAttribute using the AddAttribute method of the XPCustomMemberInfo. In addition we had to modify the returned value of the member by overriding the GetValue method and using an approach similar with the EvaluateAlias docs,

Best place to create the dynamic members

In my opinion there is no such place and everything depends on our requirements. However I can suggest a solution we used in eXpand for many years without problems. You can do it just after login where the the user model is fully merged.

public sealed partial class DynamicMemberAliasesModule : ModuleBase {

    public override void Setup(XafApplication application) {

        base.Setup(application);

        application.LoggedOn += (sender, args) => RuntimeMemberBuilder.AddFields(application.Model);

    }

The check please!

We discussed in detail all the steps needed for dynamic member aliases. In DynamicMemberAliases.zip is a XAF solution to see everything in action.

To implement the rest of the dynamic members (RuntmeNonPersistent, RuntimeOrphanedCollection, RuntimeMember) you either

  1. Need to use core or ModelDifference module of eXpandFramework (see .
  2. Copy RuntimeMemberBuilder, all interfaces from IModelRuntimeMember.cs and extend the Application Model with the included IModelMemberEx interface.

That’s all you need, to have a few productive and happy hours with XAF Smile. I really enjoy working with such a flexible framework and I am sure the XAF team will continue to surprise us in the future!

Remember next post will talk the integration of XAF + Dashboard suite so stay tuned.

We are happy to read your feedback about anything you heard today!. Remember that your questions are the best candidates for future posts .

Until next time, happy XAF’ing!

Subscribe to XAF feed
Subscribe to community feed

DiggIt!

0 comments:

Post a Comment