The vehicle table also follows a standard pattern; it is a main table, similar to the Customer
or Product
tables.
The structure of the table we will create is as follows:
Table |
Label |
Table group | |
---|---|---|---|
|
Vehicle table |
Main | |
|
|
|
|
Field name |
Type |
Length |
EDT |
|
String |
20 |
|
|
String |
60 |
Name |
|
String |
10 |
|
|
Base enum |
| |
|
Base enum |
| |
|
String |
10 |
|
Before we create the table, we need to create two base enums. The first base enum is ConFMSVehicleType
, and should have the following elements:
Name |
Label |
EnumValue |
---|---|---|
Base |
Base |
0 |
Car |
Car |
1 |
Truck |
Truck |
2 |
Trailer |
Trailer |
3 |
The result should appear similar to what is shown in the following screenshot:
We now need the Status
enum—ConFMSVehicleStatus
. Create this from the following table:
Name |
Label |
EnumValue |
---|---|---|
Created |
Created |
0 |
Acquired |
Acquired |
1 |
Active |
Active |
2 |
Unavailable |
Unavailable |
3 |
Disposed |
Disposed |
4 |
Once complete, this should look similar to what is shown in the following screenshot:
Finally, we create EDTs as follows:
There is no need to set the label as this has been set on the base enum. Often, when creating generic enumerated types, we won't add a label to the base enum; we will only add it to the EDT.
First, we need to create the vehicle ID EDT, which we will do as before. This EDT will be controlled by a number sequence, so it should extend the Num
EDT.
Create the ConFMSVehicleId
EDT as shown here:
Name |
|
Label |
|
Extends |
|
Entering the Num
EDT will force StringSize to 20 and be left aligned; this is critical if we are to create IDs using a number sequence. Also, create an EDT based on the Int64
type, called ConFMSVehicleRelationType
, with the Vehicle type
label. Reuse the same label that you created before.
We now have all the EDTs we need to create our table. Typically, the table structure of a Main
table is the same as Group
(Setup) table types. We just make the TableGroup property Main
.
In this case, we are also going to enable this table to support inheritance:
ConFMSVehicleTable
table as before (it is a convention to suffix Main
tables with Table
)Vehicle details
.Main
.CreatedBy
, ModifiedBy
, CreatedDateTime
, and ModifiedDateTime
.Yes
.ConFMSVehicleId
Name
ConFMSVehicleGroupId
ConFMSVehicleTypeId
ConFMSVehicleStatusId
ConFMSServiceSchedId
VehicleId
Name
Before we continue, we can now play with this table and you can learn a bit more about relations in AX.
To ensure that everything is synchronized, it is a good idea to force AX to synchronize its data dictionary with the physical database held in SQL Server. To do this, open the AOT (Ctrl + D), right-click on the Data Dictionary node, and select Synchronize. This may take 10-20 minutes, and may ask you to continue if there are any warnings. If it does, choose to continue.
Once this completes, go back to your project window and browse the ConFMSVehicleTable
table (right-click on it and choose Open).
Press Ctrl + N to create a new record. Click through the fields, trying the drop-down options. The fields based on enums will show the appropriate elements and the system will automatically create dropdowns for the vehicle group and service tables—very powerful! This will get even better once we add the user interface.
The vehicle group and vehicle service fields are foreign keys to the respective tables, as defined in the relations on ConFMSVehicleTable
. The following screenshot shows the definition of the ConFMSVehicleGroup
relation:
The two relations are primary key relations, which has a special meaning with AX because it enables additional features over standard relations. One feature is that it can automatically create navigation methods. For this reason, we will always use primary key relations wherever possible.
The following screenshot shows the property sheet for the ConFMSVehicleGroup
relation:
Occasionally, we may have multiple relations to the same table (perhaps filtered to a subset of records in the foreign table), so we should change the name to specify this role. Most relations are one-to-many and we will configure the cardinality like this:
The default role names are normally fine. A problem occurs if we add a second relation to the same table—we will get an error stating that role names are not unique. This is because the default role names are based on the table name, not the relation name.
You can easily overcome this by changing UseDefaultRoleNames to No
and setting the RelatedRoleName and RoleName fields manually. These are normally set as follows: RelationRoleName as the foreign table name and RoleName as the foreign table name suffixed with the relation's role. Two examples of this are VehicleGroup
and VehicleGroup_VehicleTable
. These values are validated beyond being unique across this table.
We still have a few tasks on the vehicle table to complete. These are as follows:
ConFMSVehicleId
EDTWe have done this before and the process is the same as for other tables. Use the following checklist to complete the setup:
VehicleId
will be the primary key, so create an index called VehicleIdIdx
. Then add VehicleId
to it. Ensure that AllowDuplicates is No and AlternateKey is Yes.ConFMSVehicleId
EDT, enter ConFMVehicleTable
in the ReferenceTable property.VehicleId
.Overview
and Details
, as you see fit. Add the fields as appropriate. Do not add the InstanceRelationType
field; this will be added to the form later.find
and exist
methods, as follows:public static ConFMSVehicleTable find(ConFMSVehicleId _vehicleId, boolean _forUpdate = false) { ConFMSVehicleTable vehicle; vehicle.selectForUpdate(_forUpdate); if(_vehicleId != "") { select vehicle where vehicle.VehicleId == _vehicleId; } return vehicle; } public static boolean exist(ConFMSVehicleId _vehicleId) { ConFMSVehicleTable vehicle; if(_vehicleId != "") { select RecId from vehicle where vehicle.VehicleId == _vehicleId; } return (vehicle.RecId != 0); }
Inherited tables are relatively quick to create. We just need to create the basic table definition as a main table and add the additional fields each type requires.
We will create tables for the following types:
The car will require these fields and therefore the EDTs:
CarType:
The type of car, for example, Saloon, Sedan, Estate, and so onTintedWindows
: To define whether or not the car has tinted windowsThe truck will require these fields:
Artic
: To define whether or not it is an articulated truckThe trailer will require these fields:
TrailerId
: This is the trailer's ID numberWe therefore need to create the EDTs first. It is sensible to create one for each of these fields and extend an EDT as appropriate.
Use this table to create the EDTs, setting the Label and HelpText properties as appropriate:
Name |
Type |
Extends |
Size |
---|---|---|---|
|
String |
10 | |
|
Enum |
|
n/a |
|
Enum |
|
n/a |
|
String |
|
n/a |
Once the EDTs are created, we can create the tables, use the following table to create the tables:
Name |
Label |
---|---|
|
Car details |
|
Truck details |
|
Trailer details |
In all cases, set the following properties:
TableGroup |
|
SupportsInheritance |
|
Extends |
|
There is no need to define find
or exist
methods on this table or create a primary index. Finally, note that the system automatically creates a relation to the base table based on RecId
. This is part of the mechanics behind the inheritance functionality.
The implementation of table inheritance is unique to AX and it is worth pointing out what we have done and why.
We are about to actually create an AX table for each type, all extending this base table. This will create a physical table in SQL Server to support them. SQL Server does not support table inheritance, so this technology is handled purely by AX.
As a user interface option, when we create a new record, the system will want to know which type it is (in our case, car, truck, and so on). It does this by offering the user a choice based on the tables that inherit from this table. AX will automatically create a dialog with one field labelled after the InstanceRelationType
field (which is why it is labeled Vehicle type
) and the options will be the labels of all tables, from the base table to all child tables.
Data will be physically stored in only one SQL table. AX knows what type each record is by looking at InstanceRelationType
. This field is actually a table ID and it stores the AX table ID of the child table that the record belongs to. The child table does not exist as a table in its own right in SQL. This is shown in the following diagram of ConFMSVehicleTable
:
InstanceRelationType |
VehicleId |
Car.CarType |
Truck.Artic |
Trailer.TrailerId |
---|---|---|---|---|
103649 |
V001 |
n/a |
n/a |
n/a |
103650 |
V002 |
Saloon |
n/a |
n/a |
103650 |
V003 |
Estate |
n/a |
n/a |
103651 |
V004 |
n/a |
No |
n/a |
103652 |
V005 |
n/a |
n/a |
T001 |
To see this for yourself, using the table browser, right-click on the table and select Open. You can use Ctrl + N to create records in any of the tables. You will see that the inherited tables show all fields from the parent and their own specific fields. Also, the records created in the inherited tables appear in both the base table and the specific table they were created in.
From AX 2012 R2 onwards, the way table inheritance was achieved in SQL server changed.
Before R2, a physical table was created for the parent table and its fields. Then another was created for each child table and those fields that it had and the parent did not. The InstanceRelationType
field was used to determine which child table to look at in order to find the extra fields.
Selecting data from SQL with this structure involved a complex set of SQL joins and, worse than that, updating one child record in AX would require updates to several tables in SQL.
Now all the fields required by the parent table and all its child tables are kept in a single SQL table. This makes reading and updating much less complex, as there is just one table on the disk to read and write to.
18.191.237.194