Derived attributes & associations

Derived attributes has been in MDriven for ages. To an SQL-guy derived attributes resemble calculated fields. Derived attributes are just like calculated fields that subscribe to all the values that it is calculated from. So whenever data, seen by the expression, is changed the derived attribute is marked as out of date. And the next time you or your UI tries to read it, the attribute is re-evaluated.

The key thing with derived attributes is that it is NOT calculated each time you access the attribute. If it were performance would suffer. It is calculated (or derived) as few times as theoretical possibly; only when read the first time after a change of anything that the derivation expression looks at.

The concept of derivation relies on the concept of subscription – everything in your domain layer has the ability to signal whenever it is changed (the publisher pattern), and subscribers subscribe to publishers to detect these changes. Although this is a different story I must mention that publishing event to catch changes is a key difference between the ECO approach to implementing a domain layer and the POCO way (plain old c#-objects) like NHibernate and some other frameworks use.

ModelOrder+Customer.png

Having a model as the one above I can create a derived attribute on the Order that calculates the total shipping cost by checking the products ordered and the customer’s country in OCL:

self.OrderItems->FilterOnType(ProductThatNeedsShipping).Weight->sum

* self.Customer.Country.CostToShipHerePerKilogram

I do this by creating a new Attribute, set the AttributeMode to Derived and fill in the DerivationOCL.

Derived attributes can be used in other derivation expressions so I can make a derived TotalCost without repeating the definition of the Shipping cost:

self.OrderItems.Product.Price->Sum() + self.ShippingCost

The expression of derived attributes can be expressed with C# / VB.NET / Delphi.NET code instead of in OCL (MDriven for Visual Studio). You do this by setting the AttributeMode to Derived as before but leave the DerivationOCL property empty. Generate code and inspect the Order.eco.cs file. At the bottom of the file you will see these stubs:

Code Snippet

/// <summary>This method is called when ShippingCost needs to be calculated</summary> 
partial void ShippingCostDerive(ref string res);
 /// <summary>This method is called when TotalCost needs to be calculated</summary>
 partial void TotalCostDerive(ref bool res);

You never change the files named something.eco.cs – instead, you implement these partial methods in the file something.cs (or vb or delphi). So in our case, we open up the Order.cs file and enter:

Code Snippet

partial void ShippingCostDerive(ref double res)
 {
   res=0;
   double totWeight=0;
   foreach(OrderItem i in OrderItems)
   {
       if (i is ProductThatNeedsShipping)
         {
           totWeight += (i as ProductThatNeedsShipping).Weight;
       }
   }
   if (Customer!=null && Customer.Country!=null)
   res = totWeight * Customer.Country.CostToShipHerePerKilogram;
 }

Exception

Please note that because of how time flows, you can't subscribe to time, like DateTime.Now. If it could, it would always need updating and lead to an infinite update loop.

What to remember

All you need to remember is that derived attributes can be defined in code or in the model with OCL. Derived attributes are efficient and always return the correct up to date result. Associations can also be derived the same way as attributes.

In the example model above the Shipping cost is out of date if you add a new order line, if you change the customer, if the customer changes country, if an already picked ProductThatNeedsShipping gets an updated weight etc – it covers every and any change that affects the calculation as long as that change is part of your domain layer.

I hope that you see the positive effect this will have on your UI implementations – Showing the ShippingCost and see it update as you change anything it depends on by one central definition far away from the UI.

Using derived attributes and associations clean up your code and consolidates central definitions to single points and thus greatly reduce maintenance costs and efforts.

See also

Derived settable attributes

Derived settable associations

C# and the AutoSubscribeService

In versions of the product ECO, you needed to explicitly explain to ECO what ECO should subscribe to in order to stay updated if anything changes. But as of the introduction of the IAutoSubscriptionService this is no longer necessary.

The IAutoSubscribeService deserves a chapter of its own but here is a brief overview:

The caller of your code derivation has a subscriber and starts an Auto subscription session. An Auto subscription session is notified about all access to the domain layer as long as the session is in effect. All access is inspected and builds up the description of what ECO needs to subscribe to in order to know when to mark the attribute out of date. The session is ended after your code derivation returns.

Conceptually this is what happens:

Code Snippet

using (this.AsIObject().ServiceProvider.GetEcoService<IAutoSubscriptionService>().StartSubscribe(subscriber))
 {
   double res;
   this.ShippingCostDerive(ref res)
 }
This page was edited 16 days ago on 04/02/2024. What links here