One of the unbeaten strengths of the SmartComponent Library is the implementation of Business Entities and the tooling provided for developing and testing these.

In most situations static Business Entity classes designed and generated by the Business Entity Designer are the best choice – as they provide a high degree of flexibility and customizability to developers. However, customers have reported use-cases for dynamically rendered Business Entities, especially for simple tables with no requirement for custom business logic (e.g. validation or calculated fields).

We therefore support dynamic Business Entities as well.

Configuration

Dynamic Business Entities require a definition of the ProDataset schema and Data Sources just like static business entities do. The configuration is provided to the Dynamic Business entities through the Consultingwerk.OERA.DynamicBusinessEntity.IDynamicBusinessEntityRepository service implementation.

The default implementation is provided through the DynamicBusinessEntityRepository service. This implementation receives the configuration from one or multiple JSON documents describing the model of the dynamic business entity. The file names for the JSON documents are received from the IConfigurationProvider service (e.g. .applicationsettings or .restapplicationsettings file) as a comma delimited list in the DynamicBusinessEntityDefinitions attribute:

"DynamicBusinessEntityDefinitions": "DynamicBusinessEntities/Sports2000.json"

Using Dynamic Business Entities

Dynamic Business Entities are rendered through the Consultingwerk.OERA.DynamicBusinessEntity.DynamicBusinessEntity class. Their usage does not differ from the usage of any other Business Entity. Dynamic Business Entities can be accessed through

Dynamic Business Entities are accessed by appending the identifier of the Dynamic Business Entity (e.g. DynamicCustomerBusinessEntity) to the name of the Consultingwerk.OERA.DynamicBusinessEntity.DynamicBusinessEntity renderer delimited by the “#” or “|” character. Through this method, the identifier of the Dynamic Business Entity is passed as the argument to the constructor of the DynamicBusinessEntity class.

DEFINE VARIABLE oEntity  AS IBusinessEntity NO-UNDO .
DEFINE VARIABLE hDataset AS HANDLE          NO-UNDO .

oEntity = CAST (ServiceManager:GetBusinessService ("Consultingwerk.OERA.DynamicBusinessEntity.DynamicBusinessEntity#DynamicEmployeeBusinessEntity"),
                IBusinessEntity) .

oEntity:FetchData (NEW FetchDataRequest ("*", 
                                         "for each eEmployee where eEmployee.EmpNum = 20"),
                   OUTPUT DATASET-HANDLE hDataset) .

or

ServiceInterface:FetchData ("Consultingwerk.OERA.DynamicBusinessEntity.DynamicBusinessEntity#DynamicEmployeeBusinessEntity"),
                            NEW FetchDataRequest ("*", 
                                                  "for each eEmployee where eEmployee.EmpNum = 20"),
                            OUTPUT DATASET-HANDLE hDataset)

For the RESTful access, URI’s are defined just like URI’s are defined for static Business Entities. Details can be found in this document further below.

Due to the dynamic nature of dynamic Business Entities, the usage through DatasetModels is not supported.

Definition of the Business Entity Model

Then the default repository for the models of the dynamic business entities is used, a JSON document describes the schema and the data source of the Dynamic Business Entities.

In the top-level of the JSON Document, each single Dynamic Business Entity is described by a JSON document which is referenced by a unique identifier. The following sample JSON contains a single model for an entity named “DynamicEmployeeBusinessEntity”:

{
    "DynamicEmployeeBusinessEntity": {
        "DatasetName": "dsEmployee",
        "Schema": {
            "eEmployee": {
                "Fields": [
                    "Employee.*"
                ],
                "PrimaryUniqueIndex": [
                    "EmpNum"
                ]
            }
        },
        "DataSource": {
            "eEmployee": {
                "DatabaseTables": [
                    "Employee"
                ],
                "SourceDefaultQuery": "for each Employee"
            }
        }
    }
}

The model defines a Dataset named dsEmployee with a single temp-table eEmployee inheriting all fields from the Employee database table, defining the Primary Unique Index. The Data Source for the eEmployee temp-table is defined against a single database table and the given SourceDefaultQuery.

The schema of a temp-table can also inherit files from multiple database tables and allows you to change the names of the temp-table fields. In this example the eCustomer temp-table inherits all fields from the Customer database table and includes a field named RepFullName based on the Salesrep.RepName field as well as a field based on the Region field of the Salesrep table.

"eCustomer": {
    "Fields": [
        "Customer.*",
        {
           "RepFullName": "Salesrep.RepName"
        },
        "Salesrep.Region"
    ],
    "PrimaryUniqueIndex": [
        "CustNum"
    ]
}

In this example the schema of the dsOrder ProDataset contains multiple temp-tables with a relation:

"Schema": {
    "eOrder": {
        "Fields": [
            "Order.*"
        ],
        "PrimaryUniqueIndex": [
            "OrderNum"
        ]
    },
    "eOrderLine": {
        "Fields": [
            "OrderLine.*",
            {
                "ArtikelBezeichnung": "Item.ItemName"
            }
        ],
        "PrimaryUniqueIndex": [
            "OrderNum,LineNum"
        ]
    }
},
"Relations": {
    "OrderLineRelation": {
        "ParentTable": "eOrder",
        "ChildTable": "eOrderLine",
        "RelationFields": "OrderNum,OrderNum"
    }
}

Validation

Dynamic Business Entities support updates (Create, Delete, Update) just like static Business Entities. The following validations can be configured on the table level of the schema:

The validations are defined as part of the temp-table schema, e.g:

"Schema": {
    "eEmployee": {
        "Fields": [
            "Employee.*"
        ],
        "PrimaryUniqueIndex": [
            "EmpNum"
        ],
        "ReadOnly": false,
        "ReadOnlyFields": "EmpNum",
        "MandatoryFields": "FirstName,LastName,DeptCode,SickDaysLeft,Birthdate"
    }
}

When the SmartFramework is used, dynamic Business Entities and Data Access classes inherit from the SmartBusinessEntity and SmartDataAccess base classes inheriting the data-driven referential integrity of the SmartFramework. That means that RI rules such as delete restrict, delete cascade and foreign key validation on inserts and updates are executed as expected.

Authorization

Dynamic Business Entities are supported by the SmartRequestAuthorizationProvider implementation of the Consultingwerk.Framework.Authorization.IRequestAuthorizationProvider service.

In the definition of the Security Objects in the Smart Security Object Maintenance, the Identifier of the Dynamic Business Entity is used as the name of the security object:

RESTful Interfaces

Dynamic Business Entities are supported by the RestEntitiesWebHandler exactly like static Business Entities. RESTful interfaces are also defined in the JSON structure in the REST branch:

"REST": {
    "/DynamicCustomers": {
        "Type": "collection",
        "Tables": "eCustomer",
        "Id": "CustNum",
         "Fields": "CustNum,Name,Salesrep,RepFullName",
         "Links": "orders:/Customers/{CustNum}/Orders,salesrep:/Salesreps/{SalesRep}",
         "CanRead": true,
         "CanCreate": true
    },
    "/DynamicCustomers/{CustNum}": {
         "Type": "record",
         "Tables": "eCustomer",
         "Id": "CustNum",
         "Fields": "eCustomer.*",
         "Links": "orders:/Customers/{CustNum}/Orders,salesrep:/Salesreps/{SalesRep}",
         "CanRead": true,
         "CanUpdate": true,
         "CanDelete": true
    }
}

Note, that in the JSON document, it is not required to use the tilde as an escape character for the opening curly braces.

Sample JSON Definition file

{
    "DynamicCustomerBusinessEntity": {
        "DatasetName": "dsCustomer",
        "Schema": {
            "eCustomer": {
                "Fields": [
                    "Customer.*",
                    {
                        "RepFullName": "Salesrep.RepName"
                    },
                    "Salesrep.Region"
                ],
                "PrimaryUniqueIndex": [
                    "CustNum"
                ]
            }
        },
        "DataSource": {
            "eCustomer": {
                "DatabaseTables": [
                    "Customer",
                    "Salesrep"
                ],
                "SourceDefaultQuery": "for each Customer, first Salesrep where Salesrep.Salesrep = Customer.salesrep"
            }
        },
        "REST": {
            "/DynamicCustomers": {
                "Type": "collection",
                "Tables": "eCustomer",
                "Id": "CustNum",
                "Fields": "CustNum,Name,Salesrep,RepFullName",
                "Links": "orders:/Customers/{CustNum}/Orders,salesrep:/Salesreps/{SalesRep}",
                "CanRead": true,
                "CanCreate": true
            },
            "/DynamicCustomers/{CustNum}": {
                "Type": "record",
                "Tables": "eCustomer",
                "Id": "CustNum",
                "Fields": "eCustomer.*",
                "Links": "orders:/Customers/{CustNum}/Orders,salesrep:/Salesreps/{SalesRep}",
                "CanRead": true,
                "CanUpdate": true,
                "CanDelete": true
            }
        }
    },
    "DynamicOrderBusinessEntity": {
        "DatasetName": "dsOrder",
        "Schema": {
            "eOrder": {
                "Fields": [
                    "Order.*"
                ],
                "PrimaryUniqueIndex": [
                    "OrderNum"
                ]
            },
            "eOrderLine": {
                "Fields": [
                    "OrderLine.*",
                    {
                        "ArtikelBezeichnung": "Item.ItemName"
                    }
                ],
                "PrimaryUniqueIndex": [
                    "OrderNum,LineNum"
                ]
            }
        },
        "Relations": {
            "OrderLineRelation": {
                "ParentTable": "eOrder",
                "ChildTable": "eOrderLine",
                "RelationFields": "OrderNum,OrderNum"
            }
        },
        "DataSource": {
            "eOrder": {
                "DatabaseTables": [
                    "Order"
                ],
                "SourceDefaultQuery": "for each Order"
            },
            "eOrderLine": {
                "DatabaseTables": [
                    "OrderLine",
                    "Item"
                ],
                "SourceDefaultQuery": "for each OrderLine, first Item of OrderLine"
            }
        }
    },
    "DynamicSalesrepBusinessEntity": {
        "DatasetName": "dsSalesrep",
        "Schema": {
            "eSalesrep": {
                "Fields": [
                    "Salesrep.*"
                ],
                "PrimaryUniqueIndex": [
                    "Salesrep"
                ],
                "ReadOnly": true
            }
        },
        "DataSource": {
            "eSalesrep": {
                "DatabaseTables": [
                    "Salesrep"
                ],
                "SourceDefaultQuery": "for each Salesrep"
            }
        }
    },
    "DynamicEmployeeBusinessEntity": {
        "DatasetName": "dsEmployee",
        "Schema": {
            "eEmployee": {
                "Fields": [
                    "Employee.*"
                ],
                "PrimaryUniqueIndex": [
                    "EmpNum"
                ],
                "ReadOnly": false,
                "ReadOnlyFields": "EmpNum",
                "MandatoryFields": "FirstName,LastName,DeptCode,SickDaysLeft,Birthdate"
            }
        },
        "DataSource": {
            "eEmployee": {
                "DatabaseTables": [
                    "Employee"
                ],
                "SourceDefaultQuery": "for each Employee"
            }
        }
    }
}