Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletter Hub
Free Learning
Arrow right icon
timer SALE ENDS IN
0 Days
:
00 Hours
:
00 Minutes
:
00 Seconds

Preparing our Solution

Save for later
  • 1260 min read
  • 2015-05-06 00:00:00

article-image

This article by Simon Buxton and Mat Fergusson, the authors of Microsoft Dynamics AX 2012 R3 Programming – Getting Started, covers the preparation work required before we start cutting code. Some parts of this may be skipped or reduced, depending on the scale of the development. This article does not cover the installation and configuration of the required environments; it is assumed that this is already done.

We also assume that our development environment has the AX client, management tools, and Visual Studio 2010 professional installed. If you are using cumulative update 8 (CU8), you need to use Visual Studio 2013 Professional.

If we are to use Team Foundation Server (TFS), each developer must have their own development environment. Typically, we will have a virtual server as a single box AX installation.

We will cover the following topics in this article:

  • Creating the models
  • Designing the technical solution

(For more resources related to this topic, see here.)

Creating the models

The models required depend on your organization's requirements. In this section, we will create the models based on our Fleet Management System, from a customer or end user perspective.

Models should also include your prefix. In this case, we will use the Con prefix for Contoso. We will create our models in the USR layer, as explained later in this section.

ISV models

ISVs will normally have a base model that contains shared code between all models, and a model per add-on or vertical solution. Some care is required in ensuring that there isn't a circular dependency chain between models; that is, both reference each other's models, requiring the installation to have special instructions.

By following the naming convention of prefixing elements—an ISV with the Axp prefix and an add-on named Documental—they can name the model AxpDocumental.

VAR models

If we are a VAR building a solution to customer-specific requirements, we will have three models: one for the actual modifications, another for changes to security, and the third for the labels. For example, if our prefix is Bcl and the customer is Contoso, we will have BclContoso, BclContosoLabels, and BclContosoSecurity.

Creating security in a separate model is not mandatory, but helps when implementing projects with the Sure Step methodology because it allows security to be worked on in separate streams.

Customer or end user models

If we are a global organization, with separate Dynamics AX installations, we may decide to develop a central application, which is then installed on each site. In this case, we will have three models placed in the CUS layer. For the Contoso example, we have ConGlobalApplication, ConGlobalLabels, and ConGlobalSecurity.

The most common scenario is to host Dynamics AX centrally, therefore having one application. The same three models are required, but this time in the USR layer: ConApplication, ConLabels, and ConSecurity.

It may make sense to place distinct sets of functionality in separate models, and this would certainly help in managing separate development streams. Over time, the models tend to develop cyclic dependencies, which require them to be merged in order to ensure that a complete set of code is deployed.

In our example, we will create a model for a specific functionality—our Fleet Management System—and it will make sense to hold it in a separate stream. However, it would be a particularly bad idea to hold each module's modifications in its own model.

Creating the models

Before we create the models, we must be in the correct layer; the USR layer in this case. The model creation is done by following these steps:

  1. Open the Dynamics AX Client, and open the development environment (Ctrl + D).
  2. From the main menu, go to Tools | Model management | Create model.
  3. Complete the Create model form as shown here:

    Field

    Description

    Model name

    This is the name of the model. It can contain spaces, but it is normally the same as the display name.

    Model Publisher

    Your organization or department.

    Layer

    The layer the model should be created in. This should be the current layer.

    Version

    The version of the model. This becomes part of the strong name of the model during signing.

    Model description

    Long description of the model. It is a good idea to link this to a functional or technical design document.

  4. Leave Set as current model checked and press OK.

An example of a completed Create model form is shown in the following screenshot:

preparing-our-solution-img-0

If we have version control enabled for TFS, we will also ask for the Model repository folder. It will suggest, in our example C:ProjectsVCSAX6015GettingStarted<Model folder>. Replace <Model folder> with the model name, as shown in the following screenshot:

preparing-our-solution-img-1

Using the preceding instructions as a guide, we will need to create the following models. Usefully, AX will remember the previous information, so we only need to populate Model name and Model display name:

  • ConFleetManagement
  • ConLabels
  • ConFleetManagementSecurity

Designing the technical solution

In most implementations, we have several roles involved in the solution design, build, and implementation. Our role is to design and develop a technical solution to a business requirement, and as discussed earlier, we will follow the design and build of a Fleet Management System.

The first steps in this are to analyze the business requirement and design a solution within Dynamics AX. This work will typically be led by a consultant, who will (in short) perform the following:

  • Match the business requirements to the AX functionality. The requirement may require new functionality or an extension of existing functionality.
  • Discuss the technical solution with a technical consultant/developer in order to design a solution that is feasible in AX in a suitable time frame.
  • Work through the solution with the solution architect to ensure that it fits in the overall solution design.

Create functional design documents. These will be signed by the customer stakeholders and process owners.

The consultant may propose table structures as parts of the functional design, but these are only to reinforce the requirement. The technical designer may find a more appropriate solution to this. The process is intended to leverage the skills of all parties in the solution delivery, allowing all parties to use their skills by abstracting the solution. Here is its summary:

  • The customer or process owner understands the process
  • The consultant is an expert in AX and focuses on the solution, creating a FDD
  • The solution architect validates the FDD against the solution design
  • The technical architect (or lead/analyst developer) creates the technical design while validating that it fits with the overall technical solution

These roles often merge, but there should always be a separation of business requirement definition and technical design definition. This freedom over the technical design does not mean we have total freedom over the technical solution. At all levels, it has to both match the original requirement and fit in with the overall solution. Just because it is technically cool does not mean it is appropriate. Our purpose is to create a technical solution to a business requirement.

We will evolve the design throughout this article, and the reason for each decision will be explained. It is important to understand and follow these design goals:

  • Upgrades and system maintainability: Minimize the footprint on standard AX.
  • Design for code reuse: This could span from creating a general framework to a useful static function on a global class.
  • Design for a service-oriented architecture: Always consider that your code might be used as a service or as part of a service. This paradigm also promotes code reuse.
  • Validate the design: Always validate the technical design against the original requirement, which is a very common cause of time overruns and cost. A prototype can be useful for this.
  • Use design patterns: Do not reinvent the wheel. Patterns save design time, reduce mistakes, and promote a solution that better conforms to best practice.

The technical design will include decisions on what technologies, frameworks, and patterns we will use. We may revert some decisions later on, but the majority we need to be sure we get right first time.

One such design element are the data structures. Once we start using them in code and the UI, it makes any changes to this more and more difficult. Some elements can't easily be reverted, such as whether to use table inheritance or not.

Table inheritance is a little like class inheritance. For example, we may have a core vehicle table, and specialized tables that inherit its properties (fields) and methods. As a more specific example, an articulated truck will have different attributes compared to a company car.

Data structure design considerations

The data structure architecture within Dynamics AX is breathtaking. When designing the technical structure, the tables and views should be considered along with classes as part of your static structure.

We are not making the classes persist in the database. The table definition may be designed on object-oriented (OO) principles, but we are using this to define physical tables that are transacted on reliably.

The key concepts within this are described in the following sections.

Extended data types

In traditional database design, a field tended to be one of a limited set of primitive types, such as string, number, and so on. The extended data type (EDT) system in AX allows us to define types with extended properties.

With this, we can control the following categories of properties:

Appearance

For example, the label, help text, size (string), alignment, and other type-specific properties.

Behavior

Direction, for example, RTL and presence information.

Business intelligence

Information used by the system to generate the OLAP database.

Data

A reference table, internal ID information, or a reference form (the form used to open the record identified by the database relation).

Relationships

For the example of the ItemId EDT, this would specify that this EDT references InventTable.ItemId.

In this way, the system knows when the EDT is associated with a field on a child table and which table and record it references. Additionally, the table will often have a reference to the form that is used to edit the data, allowing the user to quickly navigate to the details form.

The specific properties aren't important for now, but understanding the concept is. Using EDTs ensures database consistency (primarily type and size) and user interface consistency (label, help text, and so on). This is done with very little effort, as we only have to change the EDT properties. Even more powerfully, changing the size of an EDT will change the size of all fields that reference it.

Therefore, we will always use an EDT when creating a field, and almost always use EDTs as variable declarations and method parameters. We can override most of the other properties on the table field, but we rarely do this.

A key benefit is that we can control these properties with little effort, ensuring consistency throughout the user interface.

In some cases, we need to have a minor difference; for instance, we may wish to change a label when used in a specific context. Rather than changing the field label, it is better to create a new EDT that extends the primary EDT.

It is possible to change standard EDTs, but great care must be taken, as we need to know the full effects of the change.

Base enums – enumerated types

Base enums are what is more commonly known as enumerated types. They provide a list of options that are stored as a number in the database; the user interface will always display the corresponding text. They are equivalent to integers, and can be cast between the integer and the symbol (text).

Enums are great for status fields, where we need to have code written against a specific value. Writing code against number or string literals is bad practice. Should the option not exist or be removed from the enum's definition, we will get a compilation error when the code is compiled.

An example Enum is SalesStatus, which contains the following elements:

Symbol

Label (based on en-us)

Enum Value

None

None

0

Backorder

Open order

1

Delivered

Delivered

2

Unlock access to the largest independent learning library in Tech for FREE!
Get unlimited access to 7500+ expert-authored eBooks and video courses covering every tech area you can think of.
Renews at $19.99/month. Cancel anytime

Invoiced

Invoiced

3

Canceled

Canceled

4

You should always reference the symbol in the code. AX will understand that and translate it into the Enum value.

Tables

The table definitions stored within AX synchronize with the business database in SQL Server; often, this is automatic. No changes should be made to the SQL Server table definition, as this will be overwritten whenever the database is synchronized.

The table definition information controls both the user interface and how the physical table in SQL Server is created. This includes table properties, field definitions, and indexes. Relationships and referential integrity constraints are not created within SQL Server; these are managed within AX. They control what happens to a child table when a record is deleted or the primary key is renamed. The field definitions also control both the user interface and the physical field in SQL Server. These are usually set by the EDT it is associated with.

A key differentiator for tables is that we can create methods and override table event methods such as validateField, validateWrite, modifiedField, insert, delete, and so on. This allows us to place table-level validation and events on the table centrally and not on the interface.

In AX 2012, we can now have inheritance within tables and valid date/time states. Inheritance will allow us to have a base table, such as a vehicle table, and specialized tables that extend it, such as a bulk/loose product truck with silos and a vehicle designed to take pallets. The interface natively understands this relationship and can display records for all inherited tables in one grid control.

The valid time state provides a way to version records. For example, as data about an employee changes, we can at any time ask the system what the data was at a specific point in time.

The key consideration here in the design is to determine the structure and events we need to handle, which in turn drives the required EDTs and base enums.

Views

Views are SQL view definitions, created by constructing a query of one or more tables. They can contain aggregates and also calculate view fields, which are essentially a piece of Transact-SQL that equates to a column in view.

These are useful when flattening data from normalized tables. The only real drawback is that they are read-only views, and when they are placed on the user interface in a grid control, the grid control becomes read-only. This means that if we add a column from a related table that is editable, it will become read-only in the grid control.

Maps

Maps provide a method of sharing code for similar tables. A good example of this is the pricing logic for sales and purchase order lines that is handled by the SalesPurchLine map.

Maps contain a list of reference fields, details of how these reference fields map to the actual table fields, and methods. This is best explained with an example, such as calculating the stocking unit quantity from the quantity ordered (for example, stored in cases and sold in each).

Rather than write this code on the sales order line and the purchase order line, we can do this once using a map. On the SalesPurchLine map, there are fields for PurchSalesUnit, ItemId, and SalesPurchQty. They are mapped as follows:

Map field

SalesLine field

PurchLine field

ItemId

ItemId

ItemId

PurchSalesUnit

SalesUnit

PurchUnit

SalesPurchQty

SalesQty

PurchQty

We can create a simplified method on the map that contains the following code snippet:

InventQty calcQtyOrdered(Qty _qtySalesPurch = realMin())
{
   InventQty       qty;
   InventTable     inventTable;
   Qty             qtySalesPurch = _qtySalesPurch;
   ;
 
   if (qtySalesPurch == realMin())
       qtySalesPurch = this.SalesPurchQty;
 
   if (!qtySalesPurch)
       return 0;
   // this is actually calling a method that should exist
   // on the actual table, e.g SalesLine
   inventTable = this.inventTable();
 
 
   qty = UnitConvert::qty(qtySalesPurch,
                           this.PurchSalesUnit,
                           inventTable.inventUnitId(),
                           this.ItemId);
 
   return decround(qty,InventTable::inventDecimals(this.ItemId));
}

On the sales and purchase order line table, we can call the preceding method. An example from the PurchLine table is as follows:

AmountCur calcLineAmount(Qty qty = this.PurchQty)
{
   AmountCur ret;
 
   if (this.LineDeliveryType != LineDeliveryType::OrderLineWithMultipleDeliveries)
   {
       ret = this.SalesPurchLine::calcLineAmount(qty);
   }
 
   return ret;
}

The map will then automatically construct itself using the field mapping. Hence, we call methods on the map as if they are static methods with the :: sign. So, when called from the sales order line, this.PurchSalesUnit becomes SalesLine.SalesUnit and this.SalesPurchQty becomes SalesLine.SalesQty.

This can be a useful feature for reusing code across tables that provide similar functionalities.

Classes

Class definitions with AX provide functionality similar to C++, C#, or Java classes, in that they support inheritance and encapsulation. Interfaces can also be used and implemented in a way similar to C#.

Classes are created for the following purposes:

  • User interface interaction
  • Table event handling
  • Services
  • General processing (for example data updates, batch routines, and so on)

Although it is common to use a class to handle table events, the table itself will handle the interaction with the database. Form interaction classes are not mandatory for list pages, such as Accounts receivable | Common | Customers | All customers, but should be used on data entry forms that require logic. This ensures consistency and allows easier maintenance of logic.

When designing the static (mainly table and class) structure, we should break the design down so that we can easily expose that task as a service. An example can be a class that takes a vehicle out of service. This may perform many tasks: checking whether it was planned on loads, replacing the vehicle on the basis of a rule set, changing the status of the vehicle, and so on.

We may have other requirements to simply change the status of vehicle or associate suitable vehicles with unallocated loads. We have already written the code to change the status and the code to find a suitable vehicle. If we had classes for finding a suitable vehicle, changing status, and so on, we could've reused them, be it on a form or from code or linked to a service that could be used by a mobile application.

The point here is that we should break discrete pieces of functionality down into separate classes, as it then become much easier to reuse later on.

Forms

Most forms will be built from templates, which help us provide a consistent user interface that will look and feel much like the rest of AX. The helps reduce training time, reduce user error, and improve end user acceptance.

The following templates are available:

  • A list page
  • A details form—master
  • A details form—transaction
  • A simple list
  • A simple list, with a details section
  • A table of contents
  • A dialog
  • A drop dialog

The list page is our main entry point to both master data (items, customers, and so on) and transactional forms, such as sales orders.

The list page provides the user with a searchable list of records, with a button ribbon allowing the user to interact with the record, for example, Post sales invoice. They also provide key business intelligence about the current record, which means we don't have to navigate to the details form to make a decision on the record in question.

There are two types of details forms: master and transaction. Master forms are like customer and item forms, while sales order details and purchase order details are transaction forms. Details forms are designed to be opened from a list page.

Simple lists are useful for setup groups, where the form contains only a grid of a few fields. These are useful for simple lists of setup data. The simple list with details version contains a grid on the left and a details section that can be arranged into tabs to present many fields. Both of these form types are designed to be opened from the content area or menu.

Tables of contents are designed to be used for parameter forms. Although they may act on more than one data source (table), the tables will typically have a single record.

Dialog and drop dialog are similar in that both are designed to ask for limited information and then trigger an action. Both are usually called from another form. The difference is in how they are presented. The drop dialog will appear to drop down and be a part of the calling form, whereas a dialogs appears as a pop-up window. The difference is cosmetic, but drop dialogs are often preferred as they can't fall behind the current window.

Designing test plans

There are two main types of testing: unit testing and integration (process) testing. We are more concerned with unit testing.

Unit testing primarily ensures that the code performs functions for the functional design. We may also have performance requirements, where we need to test the code under a simulated load. AX provides a method to do this through a test project, where we can extend the test framework to write specific test cases.

These work well when simulating the load against the live hardware environment. We can use a range of performance tools to ascertain where performance bottlenecks may lie and correct them. It is better to know before "go live" that we have a bottleneck.

Even with this framework in place, manual testing is often the best method, especially since we are typically writing code based on database and UI interaction.

Let's take an example of a vehicle status change requirement. In this case, we will list the conditions that allow the status change to occur, and what should happen.

Status changed to

condition

Result

Available

Status: created

Vehicle: not acquired

Error "Vehicle not yet acquired"

Status remains unchanged

Available

Status: created

Vehicle: acquired

Success

Status changed to Available

Not available

Status: created

Vehicle: not acquired

Error "Vehicle not yet acquired"

Status remains unchanged

We then test our code to ensure that these statuses are being followed. Because we have one class that handles the status change, the form button, service call, and code call should also work. "Should" does not mean "will" of course, so each should be tested individually.

One of the biggest fears and causes of end user complaints is regression. The users involved in testing are usually key users or process owners, who are already busy with additional work brought on by an implementation. It is often their job to train their users, and "sell" the system's benefits; user buy-in is critical for successful user adoption.

There are two causes of regression: code that breaks other code or a change to a process that is incompatible with another process. The latter is mitigated by getting a solution architect or lead consultant who is responsible for the solution as a whole.

Code regression can be caused by the simplest change, and these changes are often the main cause of regression, as testing is often skipped in these cases. This is mitigated by thinking of testing as a component of the technical design, and having good technical documentation. The risks are further reduced if developer notes points where regression might occur, as the code is being written. Since the code that might be affected is commented with the TDD or FDD reference, it should be easy to locate the test plan to check for regression.

Summary

In this article, we covered the steps that we take to start up a new project. We covered both the theory and practical steps that are to be followed when starting work on a new solution. This includes creating a model and designing the technical solution.

Resources for Article:


Further resources on this subject:


Modal Close icon
Modal Close icon