Inherited Constraints
by
, 2-Oct-2009 at 09:00 AM (5199 Views)
Today we set out to answer the age old question of:
Should a child be judged by the sins of their parent?
In my previous article Using Expressions and Properties in Constraints we talked about how constraints are inherited. In addition to any constraints that may be applied directly in a main Data Dictionary Object (DDO) that main DDO will also acquire the constraints from its parent DDOs. If this is not clear, you might want to review that article before proceeding.
We will now discuss how a DDO can take advantage of inherited constraints and how when desired a DDO can take advantage of disabling this inheritance.
Consider the following requirement. We want to change our order entry view so that you can only select active customers for an order. We can do this by constraining on Customer.Status as follows:
Try adding this to Order.vw in the Visual DataFlex Order Entry sample workspace. You will get the behavior you want for new orders. Create a new order and attempt to select customer 8. Do this by typing 8 and pressing the Find GE key (F9) or the Find GE Toolbar button (=). You will see that customer 8 is skipped and customer 9 is selected. Customer 8 is filtered (Customer.Status="N") and the next customer is found. No surprises here, this is exactly what you’d expect. Now try finding some orders. Clear your view, navigate to the order number form and start pressing the Find GT (F8) key or the Find GT button. Watch the order numbers as they go by. You will see that order number 108 is skipped. It turns out that order number 108’s customer is customer 8. Since the order header DDO inherits constraints from its parent Customer DD and the customer constraint fails, this order is filtered. (Note that in this example you should not use the prompt button to find records. The lookup objects have their own DDO structures so you will not see this filtering).Code:Object Vendor_DD is a Vendor_DataDictionary End_Object Object Invt_DD is a Invt_DataDictionary Set DDO_Server to Vendor_DD End_Object Object Customer_DD is a Customer_DataDictionary Procedure OnConstrain Constrain Customer.Status eq "Y" End_Procedure End_Object Object SalesP_DD is a Salesp_DataDictionary End_Object // SalesP_DD Object OrderHea_DD is a OrderHea_DataDictionary Set DDO_Server to Customer_DD Set DDO_Server to SalesP_DD End_Object // OrderHea_DD Object OrderDtl_DD is a OrderDtl_DataDictionary Set DDO_Server to OrderHea_DD Set DDO_Server to Invt_DD Set Constrain_File to OrderHea.File_Number End_Object // OrderDtl_DD Set Main_DD to OrderHea_DD Set Server to OrderHea_DD
Is this filtering of orders based on a customer constraint desired? Maybe it is and maybe it’s not. Let’s pick another example where this is clearly less desirable. Assume you've been told to only allow the creation of order-detail lines if the unit price of the inventory item is greater than $100. We’d do that as follows:
If you run this view you will see that existing orders no longer display all of the detail records. Any detail record that has a parent inventory item with a unit cost that is less than $100 is filtered. This means that the detail items displayed will not add up to the displayed order total. While internally everything is fine and this is doing exactly what it should, you probably don’t want this behavior.Code:Object Vendor_DD is a Vendor_DataDictionary End_Object Object Invt_DD is a Invt_DataDictionary Set DDO_Server to Vendor_DD Procedure OnConstrain Constrain Invt.Unit_Price gt 100 End_Procedure End_Object Object Customer_DD is a Customer_DataDictionary Procedure OnConstrain Constrain Customer.Status eq "Y" End_Procedure End_Object Object SalesP_DD is a Salesp_DataDictionary End_Object Object OrderHea_DD is a OrderHea_DataDictionary Set DDO_Server to Customer_DD Set DDO_Server to SalesP_DD End_Object Object OrderDtl_DD is a OrderDtl_DataDictionary Set DDO_Server to OrderHea_DD Set DDO_Server to Invt_DD Set Constrain_File to OrderHea.File_Number End_Object
In both of the above examples, we are having an issue because a parent constraint is inherited by the main DDO. We can stop this inheritance by setting the pbInheritConstraint property of the main DDO to false (when we refer to the "main DDO" we are talking about the DDO that performs the find). When you do this constraints are not inherited, which means that DDOs and their parent DDOs can apply different filters.
In our example all order-detail items will be displayed but you can now only add new detail items that have a inventory item unit cost above $100. What happens if you try to edit an order detail line? You can change the inventory item but you can only change it to an item with the appropriate unit cost. This new behavior feels right for order detail. With orders you can now find any existing order for any customer but you can only create new orders for active customers. In the case of this customer filter you may or may not want constraints to be inherited. This can be controlled on a DDO by DDO basis by setting pbInheritConstraints.Code:Object Vendor_DD is a Vendor_DataDictionary End_Object Object Invt_DD is a Invt_DataDictionary Set DDO_Server to Vendor_DD Procedure OnConstrain Constrain Invt.Unit_Price gt 100 End_Procedure End_Object Object Customer_DD is a Customer_DataDictionary Procedure OnConstrain Constrain Customer.Status eq "Y" End_Procedure End_Object Object SalesP_DD is a Salesp_DataDictionary End_Object Object OrderHea_DD is a OrderHea_DataDictionary Set DDO_Server to Customer_DD Set DDO_Server to SalesP_DD Set pbInheritConstraints to False End_Object Object OrderDtl_DD is a OrderDtl_DataDictionary Set DDO_Server to OrderHea_DD Set DDO_Server to Invt_DD Set Constrain_File to OrderHea.File_Number Set pbInheritConstraints to False End_Object
There is another, arguably more straight forward way, to inherit parent constraints. Rather than counting on the parent DD to supply the constraint you can add it directly to the main DDO’s OnConstrain event. There is one catch to this. If you are applying a parent filter you must use the Constrain As construct. It will take the format of:
For example:Code:Constrain Main_Table_Name As (Parent constraint expression or whatever you want)
Here we are not inheriting constraints (so we don’t care what the Customer DDO does) but we are creating a constraint on OrderHea that just happens to be based on information found in the parent.Code:Object OrderHea_DD is a OrderHea_DataDictionary Set DDO_Server to Customer_DD Set DDO_Server to SalesP_DD Set pbInheritConstraints to False Procedure OnConstrain Constrain OrderHea as (Customer.Status = "Y") End_Procedure End_Object
As we know, Constrain As can be used for any type of expression. This makes the parent constraint just another type of Constrain As filter. If you’ve read the article about Constraints and Performance you might be thinking that the use of Constrain As here is a bad idea because it cannot be optimized. That’s true but it turns out it does not matter. When a constraint is inherited from a parent DDO, it cannot be optimized anyway. If you think about what we are doing, this makes sense. There is no way you can optimize the finding of your main DD record based on a filtered value of a related parent column. You are not using parent’s index so there is no way to optimize it.
To summarize:
The default behavior of DDOs is to inherit their constraints from their parent DDOs. This can include the constraints that the parents might have inherited from their parent DDOs and so on. This normally works well but it can create conditions where the filtering of a record based on a parent DDO is not desired. Also, if you have very large DDO structures with many different constraints this inheritance can become difficult to manage.
Parent constraint inheritance can be disabled by setting pbInheritConstraints to false in your main DDO.
If inherited constraints are disabled you can still apply a parent filter by using Constrain As in the main DDO. There is no performance difference between using an inherited parent constraint and using a Constrain As in the main DDO.
Disabling inherited constraints with pbInheritConstraints and the use of parent filters with Constrain As are advanced features that can come in handy in the right situation.