You are on page 1of 23

Microsoft Dynamics AX

Tips for creating services in Microsoft Dynamics AX 2009


Concepts and recommendations for developers that are creating custom services.

Date: June 2009

Table of Contents
Introduction ................................................................................................ 3
Audience.............................................................................................................................. 3 Prerequisites ........................................................................................................................ 3

Services overview ....................................................................................... 3 Recommendations....................................................................................... 4


General recommendations ..................................................................................................... 4 Schema ............................................................................................................................... 4 Query ............................................................................................................................... 5 Service and schema generation ........................................................................................... 6 Keys ................................................................................................................................ 7 Generated service artifacts..................................................................................................... 7 Service ............................................................................................................................. 7 Service implementation ...................................................................................................... 8 Data containers ................................................................................................................. 8 Schema definition .............................................................................................................. 8 Ax<Table>/AxBC classes .................................................................................................... 9 Axd<Document> classes ................................................................................................... 11 AxdBase hierarchy ............................................................................................................ 11 Defaulting ...........................................................................................................................12 Interactive vs. non-interactive defaulting ............................................................................. 12 Service defaulting ............................................................................................................. 12 Defaulting and validation ................................................................................................... 14 Validation ........................................................................................................................ 16 Security ........................................................................................................................... 18 Debugging ....................................................................................................................... 21

Resources ................................................................................................. 22

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

Introduction
Microsoft Dynamics AX 2009 ships with document services that encapsulate common business logic and provide a method for exchanging data with other systems. If an existing document service does not provide the functionality that you need, you can create your own document service by using the AIF Document Service Wizard. Using the AIF Document Service Wizard to create a new service creates the necessary classes and artifacts. However, you must then add code and business logic to create a robust and fully functional service. This document provides recommendations for how to design and implement custom services, along with guidance to refactor business logic.

Audience
This document is for application developers responsible for creating new services in Microsoft Dynamics AX 2009.

Prerequisites
We recommend that you have knowledge in the following areas: Microsoft Dynamics AX 2009 document services framework and related classes. For more information, see Document Services Classes. The AIF Document Service Wizard. Some knowledge of Microsoft Dynamics AX 2009 AIF setup and configuration. This will help you understand how service design and configuration changes affect how the services runs in production. Familiarity with XML and XML schemas, namespaces, and XSLTs.

Services overview
Microsoft Dynamics AX services are designed and implemented in the Application Object Tree (AOT) using X++. A service is defined once, but can be exposed via different transport protocols, such as a Web Service, an MSMQ adapter, a BizTalk adapter, or a file system adapter. Microsoft Dynamics AX tools and wizards are available to expose and deploy services to these different environments, leaving the application developer to focus on the service definition and implementation. The application developer uses tools and wizards to generate and deploy the X++ service as a Windows Communication Foundation (WCF) service hosted in Internet Information Services (IIS). After a service has been implemented, any requests made to that service are forwarded from IIS and run on the Application Object Server (AOS). These generation tools create the service definition in the form of XML Schema Definitions (XSDs) and managed assemblies. From a high level, service requests are made to the managed assemblies that delegate them to the X++ service implementation using .NET Business Connector. The incoming message is not deserialized inside the managed assembly to increase performance.

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

The path of a service request is shown in the following architecture diagram.

Recommendations
General recommendations
When designing and implementing a custom service, consider how the service will be consumed and how it will be implemented. Services should be focused, easy to understand, and easy to use. You should try to create one set of business logic in the service so that business rules are not duplicated throughout the application. The following recommendations are themes that will be discussed in greater detail: Services should be easy to use. Base the service schema should on use case scenarios. Expose only elements that need to be exposed. Strive for few required elements. Ensure existing defaulting rules are implemented. Use AIF faults to communicate problems back to the caller. Services should use the same business logic as the client. Avoid duplicating business logic and refactor such that business logic is reused and applied consistently, irrespective of the entry point to the service. Strive to locate business logic on the tables. Throughout this white paper, concepts are discussed in terms of the client/interactive context or the service/non-interactive context. This is because certain functions such as defaulting and validation must occur whether the trigger for these functions comes from a user on a form (interactive context) or from a call to a Microsoft Dynamics AX services (non-interactive context).

Schema
Microsoft Dynamics AX supports create, read, update, delete, find, and findKeys service operations for services generated by using the wizard. However, you can create your own custom service operations. One AxdDocument is used as a parameter for these generated signatures. For example, the create operation and the find operation use the same document definition. For more information, see Schemas and About Axd<Document> Classes. The document schema is defined by the Axd query that the service is based on. It is a simple task to define a query and generate the schema, but deciding which tables and fields should be included in your schema requires critical thought. The following list contains recommendations for the document schema. Do consider the business requirements to determine which elements should be exposed.

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

Do consider which service operations are necessary for the service. Do consider what elements should be exposed for each service operation. Do expose the primary key. Do document which elements are used for each service operation. This information should be added as XML documentation to the service operations. Do consider exposing the recId and recVersion fields when they provide consumer value, for example, when they are a foreign key to another service. These fields are not required for update operations, but they can be used for concurrency. Do consider that fields that are not exposed may be defaulted. Providing default field values according to the business rules may satisfy the business requirements. Do consider the roles of the service consumers to help limit the schema. For example, customer roles should not be able to view cost information. Do define the schema as the union of all the elements needed by the individual service operations. Do not expose all the elements because someone may need them in the future.

Query
You should give careful consideration to which fields are exposed by the service schema. The fields for a query can be limited by changing the Dynamic property on the query data source fields node to No and deleting the unnecessary fields. The following list provides guidelines on creating the service query. Do prefix the query name with 'Axd'. Do use the module area when defining the query name. For example, Ledger. Do use the document description when defining the query name. For example, GeneralJournal. Do set the field Dynamic property to No and explicitly define the fields to expose. Because the dynamic metadata for the data source fields is set to No, the fields from higher layers will not automatically show up in the service contract after customizations. If you set the Dynamic property for the fields to Yes, you must regenerate the service contract and republish the service.

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

Service and schema generation


After you have defined the query, use the AIF Document Service to generate the service template and schema. To use this wizard, click Tools > Development tools > Application Integration Framework > Create document service or Tools > Development tools > Wizards > AIF Document Service Wizard. For more information, see Creating New Services in the Microsoft Dynamics AX SDK documentation on MSDN. The following list details service generation guidelines. Do accept the default service class name. The wizard removes the 'Axd' prefix from the query name and adds a 'Service' postfix to create the default service class name. Do accept the default document object class name. The wizard removes the 'Axd' prefix from the query name to create the default document object class name. Do accept the default Axd class name. It should be the same as the query name. Do not regenerate existing AxBC classes (also called Ax<Table> classes). These classes wrap the query tables and track state. If Ax<Table> classes exist, it is likely that custom code has been added to them.

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

Keys
The elements of the entity keys are not strongly typed and are typical schema elements. Therefore, all read service operations take the same list of key/value pair elements that define the table field names and the table field values. The consumer must determine the entity key for the service because it is not obvious with the generated schema. The following code shows an example of a weakly-typed key for a read service operation:
GeneralJournalServiceClient proxy = newGeneralJournalServiceClient(); AxdLedgerGeneralJournal journal = new AxdLedgerGeneralJournal(); EntityKey[] entityKeyList; EntityKey entityKey = new EntityKey(); KeyField keyField = new KeyField(); keyField.Field = "JournalNum"; keyField.Value = "000046_003"; entityKey.KeyData = new KeyField[1] {keyField}; entityKeyList = new EntityKey[1] {entityKey}; journal = proxy.read(entityKeyList);

Generated service artifacts


The AIF Document Service Wizard generates the service, the service implementation, data containers, schema definition (.xsd file), Ax<Table> classes, and the Axd<Document> class. For more information about the service artifacts that are created by the wizard, see How to: Create a Service Using the AIF Document Service Wizard and Document Services Classes.

Service
The service class is found in the AOT under the Services node. The service class defines the operations that the service supports and contains the service metadata. Do change the ExternalName property so that is does not include the module name. For more information, see AIF Class Naming Conventions. Do define a namespace by editing the Namespace property. Do create a security key with the name of the service under your module. Do assign the SecurityKey to this service by updating the SecurityKey property. Recognize that the X++ service class is the service implementation.

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

Service implementation
The generated X++ service implementation class is functional, meaning that the service can be used but it does not have the necessary business logic. The service implementation delegates its implementation to the Axd<Document> and Ax<Table> class hierarchies. Do consider adding additional business logic for documents that need additional logic for read and find operations. For example, you might add logic to these service operations in the ledger general journal service to prevent a locked journal from being read unless the requester is a member of the user group that has the journal locked. Do add custom logic to the service to restrict tables that contain more than one logical document to your service document type. For example, the LedgerJournalTable table contains many logical journals in the same physical table. The service infrastructure assumes that each physical table is one logical document. We recommend that you expose services for logical documents, for example, the general journal, and not for physical tables.

Data containers
The data containers represent the document schema in X++. They are defined by the document object class name in the AIF Document Service Wizard and the table name (see the example below). The design rationale behind these classes is to provide X++ programmability to the service, but not impact performance because of deserialization. The data containers expose the same fields as the schema definition. They support strong- and weakly-typed access, as well as the ability to switch between the two modes. When the service is invoked from outside of X++, these data containers are in the weakly-typed mode and just pass data to the Axd<Document> and Ax<Table> class hierarchies, and they have no impact. When the service is invoked from X++, the data containers are in the strongly-typed mode as shown in the following code example:
LedgerGeneralJournal LedgerGeneralJournal_LedgerJournalTable LedgerGeneralJournal_LedgerJournalTrans

To view these classes in the AOT, click AOT > Classes, and then navigate to the class. Do use the data containers to unit test the service. Do keep the data containers synchronized with the schema. Do not add custom code to the data containers.

Schema definition
The generated schema definition (.xsd file) describes the shape of the message and the rules that the message must follow. The Microsoft Dynamics AX service infrastructure regenerates the schema definition when the service is deployed to IIS. The service schema definition is used for two purposes. First, when the service consumers want to generate a proxy, they access the service definition (WSDL). The WSDL delegates to the schema definition to describe the document. Second, when messages are sent to the service, the service infrastructure ensures that the incoming message follows the schema definition rules. The service infrastructure uses the query, table metadata, and extended data type (EDT) definitions to define the value limits for each element.

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

For example, currency code must be a string with a length of three as shown in the following code snippet. If a consumer sends a currency code with a length of four, then the message will not be processed because it will fail schema validation.
<xs:simpleType name="AxdExtType_CurrencyCode"> <xs:annotation> <xs:documentation xml:lang="EN-US">Currency:Current currency code.</xs:documentation> </xs:annotation> <xs:restriction base="xs:string"> <xs:minLength value="0" /> <xs:maxLength value="3" /> </xs:restriction> </xs:simpleType>

The shape of the schema is defined by the query. The schema allows multiple documents to be processed in a single service call. A service consumer can make a service request to the general journal create service operation and pass one ledger journal or more than one ledger journal (AxdEntity_LedgerJournalTable). General Journal schema example:

Ax<Table>/AxBC classes
The Ax<Table> class is an X++ class that wraps a table to track state. An example of an Ax<Table> class is AxLedgerJournalTable. All Ax<Table> classes derive from the AxInternalBase class. From a service perspective, it is important to understand which elements that the consumer has passed to the service, so proper defaulting can occur. Ax<Table> classes share three common characteristics. They contain generated 'parm' methods that wrap all the fields on the table. When an incoming message is deserialized, the service infrastructure invokes each 'parm' method for which the consumer passed data. These 'parm' methods then set state and update the database table. Therefore, after deserialization, the class maintains the elements that the consumer passed to the service. This allows field defaulting to use the values the consumer passed. In general, the generated 'parm' methods are sufficient because they are only tracking state. In some cases, the value that you want to expose externally from the service is different than the internal value. Currency code is one example. Externally, you may want to use the ISO 4217

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

currency code, but internally, you must use the internal currency code. The Ax<Table> 'parm' methods can be used for transforming data that has different values external to the service. The this.valueMappingOutbound() method can be used to determine if the Ax<Table> 'parm' methods can be serialized for outside consumption as shown in this code from the AxLedgerJournalTrans.parmCurrencyCode method: public CurrencyCode parmCurrencyCode(CurrencyCode _currencyCode = '') { CurrencyCodeISO currencyCodeISO; ;

if (!prmisdefault(_currencyCode)) { // Because of de-serialization order, we cannot convert the external ISO 4217 currency // code to the internal AX currency code. prepareForSave This work is done in Axd

this.setField(fieldnum(LedgerJournalTrans, CurrencyCode), _currencyCode); } if (this.valueMappingOutbound()) { if (this.parmCompany()) { changecompany(this.parmCompany()) { currencyCodeISO = Currency::currencyCodeISO(ledgerJournalTrans.CurrencyCode); } } else { currencyCodeISO = Currency::currencyCodeISO(ledgerJournalTrans.CurrencyCode); } return currencyCodeISO; } else { return ledgerJournalTrans.CurrencyCode; } }

10

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

The generated 'set' methods are designed to be the place where the defaulting logic should be added. However, this approach keeps the defaulting logic inside the Ax<Table> class. We recommend that you do not implement defaulting logic in the 'set' methods. The code to determine the default value is typically the same in the service context and the interactive client context. Detailed recommendations for defaulting code are provided later in the document. The setTableFields method is called by the service infrastructure after deserialization has occurred. This is where you should put your defaulting code. Do remove the generated set<field name> methods. Do keep the generated setTableFields method, but remove its individual calls to the this.set<field name> methods. Do consider fields that should be translated into corresponding external values. Do not create class hierarchies of Ax<Table> classes to implement defaulting variations because this application code is locked into these classes.

Axd<Document> classes
The Axd<Document> class is an X++ class that is the document implementation for the document service. An example is the AxdLedgerGeneralJournal class. The service implementation delegates its implementation to the Axd<Document> class, so when the X++ document service is invoked, it, in turn, calls the Axd<Document> framework. Therefore, you must define the actions that the document supports. The Axd<Document> framework uses the term 'action' to describe the service operations in the service implementation. Both the service and Axd<Document> framework support similar CRUDF actions/service operations. Based on the service operations that you choose in the AIF Document Service Wizard, the generated Axd<Document> contains the correct implementation for the CRUDF operations. Within this class, you also find //TODO comments, where the application developer must add the appropriate messages. Be sure to search the class for all comments marked with //TODO and add the necessary code. The prepareForSave method is invoked by the service infrastructure after deserialization but before defaulting and validation by each Ax<Table> instance from the incoming message (parent and child). The Axd<Document> is related to only one service, while the Ax<Table> classes may be used by many services. Therefore, the Axd<Document> prepareForSave method is where you should default fields that are not visible externally and that are always a single value. For example, the general ledger journal service hides the JournalType field, but defaults the JournalType field to Daily. We also recommend that you default the parent Ax<Table> primary key on the childs Ax<Table> primary key in the prepareForSave method. The initQuery method allows application developers to adjust the read query before it is run. The initQuery method is a good place to limit the results to those that are appropriate for your service. For example, the general journal service only exposes the daily general journals (LedgerJournalType::Daily). A range was added to the query to include only daily journals. The updateNow method is invoked after the entire incoming document has been processed, so the data is in the tables, but not yet committed. The updateNow method enables application developers to make updates based on the results of the entire message. In the general journal service, the updateNow method is where header totals that were based on all the lines are updated.

AxdBase hierarchy
The AxdBase class hierarchy is the Axd<Document> framework document implementation. Logically, the AxdBase class acts as a controller because it determines what will be done and the order it will be done in. This class hierarchy is not intended to be varied by the application developer, but it is a good place to set breakpoints to debug problems. Because the AxdBase class acts as the controller, it invokes methods on the Axd<Document> and Ax<Table> classes. Most services delegate to the

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

11

AxBase class, so it is helpful for application developers to understand the relationship between the service methods and the AxdBase class hierarchy. The following table describes the class hierarchy and the method relationships: Custom service method calls
<your service name>.create <your service name>.delete <your service name>.find <your service name>.read <your service name>.update

AxdBase method calls


AxdBase.createList AxdBase.deleteList AxdBase.findList AxdBase.readList AxdBase.updateList

AxdBase<Action> method
AxdBaseUpdate.createDocumentList AxdBaseUpdate.deleteDocumentList AxdBaseRead.findDocumentList AxdBaseRead.readDocumentList AxdBaseUpdate.updateDocumentList

Defaulting
Interactive vs. non-interactive defaulting
In the interactive case, a real person is using a client form in Microsoft Dynamics AX; so the general purpose of defaulting field values is to reduce the amount of information that the user must enter. In this case, defaulting field values with precision is not necessary because the user can modify the values. In the non-interactive scenario when a service is called, there is no end user; so the purpose of defaulting is to set the default field values that would normally be set by a user. Therefore, it is possible that defaulting between the interactive and service contexts is different. The service defaulting must be more conservative because it must be correct 100 percent of the time. For example, in the interactive scenario, the general journal will always default the line offset account from the header default offset account. In the service scenario, it is not possible to default the offset account because the application developer must know the consumers intent to get a balanced transaction. Currently, it is not possible to understand the service consumers intent. In the interactive context, most defaulting is invoked after a single field has been entered and validated (the table modifiedField methods are invoked). As the end user enters or changes data, the application defaults more field values. Typically, the application does not track what information the user has entered and will replace it with default values. If this defaulting logic is always the same, it will likely be on the table. If it varies from form to form, then it will likely be in the form data source or a class hierarchy that is invoked from the form data source. In the service context, defaulting is invoked after deserialization of data but before validation. The service infrastructure invokes the Ax<Table> setTableFields method, which allows the application developer to default all fields. Because deserialization has occurred, the Ax<Table> contains the state, (which properties the consumer sent). Application developers should understand the relationship between fields so that the defaulting order can be determined. Generally, the order of defaulting fields is similar to the form tab order. If the field is writeable, then defaulting should not replace the value sent by the service consumer. If the field is read-only, then defaulting must always default the value ignoring the value sent by the service consumer.

Service defaulting
A service is easier to consume if it correctly defaults field values. Application developers should strive to limit the number of required elements and provide defaulting for the other elements. When a table buffer is initialized, the default values for the EDT are the base default values. If the table/EDT default is sufficient, we recommend that you do not explicitly code these default values. When a message is deserialized, the incoming message (XML) is put into an in-memory object. Because the document schema is the same for all operations, it is possible that a consumer can send values for fields in the schema that are considered read-only by the application to a create or update service operation. The deserialization process does not recognize read-only fields, so every field on the

12

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

Ax<Table> class must be read-write. If the incoming message cannot be deserialized, then the service infrastructure will stop processing and return a fault message to the caller. This behavior is consistent with services in general and is not limited to Microsoft Dynamics AX. Therefore, it is the application developers responsibility to add defaulting code for logical read-only properties. Service defaulting typically takes place only in the create service operation. When calling an update service operation, if the consumer did not send the value for update operations, the original value should be used. When a consumer calls the update service operation, the incoming message might contain only data for fields to be updated, and it might not contain all the field values needed for defaulting. The service infrastructure solves this problem for application developers by applying the incoming update message to the original document. When the incoming update message is received inside the AOS, the service infrastructure will read the document from the database and use that as the original document. Then, it applies the incoming update message to the original. The message must contain the key of the record to be updated because this is how AIF locates the record in the database. For more information, see Updating Data in AIF. Code that defaults values should be refactored and moved to the table because it is the only abstraction that is used by all consumers. This may involve moving the defaulting code from the form, form data source, form class hierarchies or existing Ax<Table> classes to the table. It is a common Microsoft Dynamics AX design pattern to store different, related types of data in the same table. For example, the LedgerJournalTable table contains ledger journals and financial journals. It is also common practice to write the common application logic for those different types on the table, while the specific validation logic for each of the related types was written closer to the forms. From a service perspective, the form code is not accessible from the service. Therefore, it is a best practice to consolidate the business logic behind the table using a strategy pattern to address the business logic for each of the related types. We recommend that you consider using the strategy design pattern to solve these varying defaulting rules and letting the table delegate to the strategies. In the strategy design pattern, you encapsulate algorithms and call them from the consumers that need them. This enables loose coupling between the client and algorithms, which allows your software to be more extensible and maintainable. This approach allows the logical table (the table plus the strategies) to contain the defaulting variations and creates one set of reusable defaulting code. If you do not have varying defaulting rules, then we recommend that you add the necessary behavior to the table itself. In the interactive context, the form does not have any knowledge of Ax<Table> classes, but in order to have reusable defaulting logic, they are necessary. By adding Ax<Table> classes as optional parameters to the table defaultField, defaultRow and modifiedField methods, the state can be forwarded to the table and then to its strategies. If strategies are necessary, then encapsulate the strategy construction to the static constructor on the base strategy (see AOT > Data Dictionary > Tables > LedgerJournalTable.type and AOT > Classes > LedgerJournalTableType::construct).

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

13

The following code is an example of an optional Ax<Table> parameter on the table buffer method LedgerJournalTable.defaultField:
public void defaultField(fieldId _fieldId, AxLedgerJournalTable _axLedgerJournalTable = null) { LedgerJournalTableType ledgerJournalTableType; ; ledgerJournalTableType = this.type(_axLedgerJournalTable); if (ledgerJournalTableType) { ledgerJournalTableType.defaultField(_fieldId); } }

Defaulting and validation


Data defaulting is related to data validation. Often, a default field value is dependent upon several other fields. Because of default field order, these other fields most likely have been defaulted, but have not been validated. Therefore, it is necessary to determine if a dependent field is valid during defaulting so that the defaulting logic can use the dependent field value to default another field. When validating dependent defaulting fields, it is also important that you do not log faults(problems) because the same problem should not be logged more than once. The validate<field name> log fault pattern adds a log fault to the validate<field name> methods allowing consumers to choose whether to log faults as shown in the following code:
Classes\LedgerJournalTransType.validateCurrency protected boolean validateCurrencyCode(boolean logFault = true) { }

The service infrastructure follows a top-down document approach, so the header is defaulted, validated, and inserted or updated. Then the children are defaulted, validated, and inserted or updated or deleted. It is possible to look at the parent values or the previous child's data in the database while defaulting the child, but you cannot see the entire defaulting graph while defaulting. Application developers must understand how AIF processes incoming messages, the data model, the relationships between fields, and the field defaulting rules in order to implement data defaulting in very complex scenarios. During defaulting, it is common to access data in different tables. It makes sense to cache this information in the strategy implementation so it is available for other defaulting and validation methods. Be sure to create class-level variables that hold the variable values, create an initialize<descriptive name> method to initialize the information when the primary key changes, and invoke the initialize<descriptive name> method from all the code in which the information is needed. For example, this code from the LedgerJournalTransType. initializeLedgerJournalTable method:
protected void initializeLedgerJournalTable() { if (ledgerJournalTable.JournalNum != ledgerJournalTrans.JournalNum) { ledgerJournalTable = LedgerJournalTable::find(ledgerJournalTrans.JournalNum); } }

14

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

After invoking the initialization code, be sure to check the recId to determine whether data was found as shown in the following example:
LedgerJournalTransType.determineDefaultTransDate() { ... this.initializeLedgerJournalName(); if (ledgerJournalName.RecId != 0) { ... } }

Do make your service easy to use by limiting the number of required elements. Do understand the data model and the defaulting rules. Do research and document the interactive defaulting rules. Do understand the defaulting between the form, form data source, and form-based class hierarchies. Do default fields that are exposed on the schema and the table buffer. Do respect the service consumer values. Do default and replace read-only fields. Do default and validate dependent fields before trying to determine the default value. Do document duplicate code. It is acceptable to have duplicate code, but you should document duplicate code and indicate when it can be removed. Do put the defaultRow method on the table or strategy. Do add the Ax<Table> class as an optional parameter to the table defaultField and defaultRow methods. Do override the Ax<Table> setTableFields method to pass the Ax<Table> class to the table or strategy defaultRow method. Do put default<field name> methods on the table or strategy. Do refactor logic from the determine<field name> method into separate methods. Do cache frequently accessed data in the initialize<descriptive name> methods. Do check to see if cached data exists before using it. Do default values using the Ax<Table> parm<field name> methods so that state is tracked. Do use protected visibility for strategy default<field name>, modified<field name>, determine<field name>, and initialize<descriptive name> methods. Do use public visibility for strategy defaultField and defaultRow methods. Do not explicitly recode table/EDT default values. Do not log faults (problems) during defaulting. Consider which fields cannot be defaulted accurately 100 percent of the time from the service context. Consider using the strategy pattern to vary defaulting rules on the table. Consider making the base strategy abstract.

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

15

Consider using the factory pattern on the base abstract strategy to construct the concrete strategy. Consider adding modified<field name> methods to the strategy. Consider fields that are defaulted externally to the table in the interactive mode.

Validation
Application developers write most of their validation logic by checking conditions, and when a problem occurs, they call the checkFailed method. The error information is added to the infoLog as a warning. Because an exception is not thrown, processing continues to the next statement. Therefore, validation code is typically structured to validate up to the point of a single failure and then completes the method. In the client context, the kernel detects that the processing has been completed and it verifies that the information has been written to the Infolog. When the Infolog contains information, the kernel displays it to the user. When processing cannot continue, the kernel and X++ code will throw an exception. This will halt processing, roll back any database transactions, and enter a catch statement to handle the exception. When the code is run in the client scenario, the exception will be caught at the nearest catch statement which is the normal expectation. When the code is run in the service scenario, the exception will stop processing, roll back any database transactions, but not be caught at the closest catch statement. This service exception behavior is because exceptions are coupled to the transaction, so exceptions cannot be caught within transactions. We recommend that you do not handle exceptions in a catch statement, and to throw exceptions only when necessary. By default, the service infrastructure does not share any exception information with the caller. This ensures that sensitive information is not shared with external consumers. The services infrastructure will log any exceptions and return a general message that the request could not be processed. The consumer must contact the Microsoft Dynamics AX administrator who then looks for the exception in the AIF exception log. To open the AIF exception log, click Basic > Periodic > Application Integration Framework > Exceptions. To configure AIF to allow an endpoint (one external consumer) the ability to see all exceptions, errors, and warnings, click Basic > Setup > Application Integration Framework > Endpoints and select the Propagate errors field for an endpoint. Like defaulting, validation code should be written in the table because it is the only abstraction that is used by all consumers. This may involve moving validation code from the form, form data source, form class hierarchies, or existing Ax<Table> classes to the table. If you have polymorphic data and validation rules, do not put them in the form. We recommend that you consider using the strategy pattern to solve these varying validation rules and let the table delegate to the strategies. Because the defaulting and validation are related to each other, we recommend that you keep the defaulting (defaultField, defaultRow, and modifiedField methods) and validation (validateField and validateRow methods) in the same strategy hierarchy. Microsoft Dynamics AX 2009 introduces the AifFault class that enables application developers to return validation errors back to the calling consumer. Most of the services that ship with Microsoft Dynamics AX do not use this new class, but it is the recommended way to communicate errors to the caller. The AifFault class is built on top of the Infolog, so it is possible to refactor validation logic to work in the client and the services mode. The service infrastructure wraps all message processing in a single database transaction. Application developers do not have any database transactional control. If problems are encountered, the entire transaction will roll back. Because the consumer can send a set of documents to any service, it is possible that the service will continue to process many more documents that will eventually fail. The services are designed to validate an entire message. Faults will be collected for the entire message and returned to the caller. The client protects the user from entering data that is not valid through the various controls in the user interface. The rich client only allows the user to select a valid value, so you typically do not see validation logic for these scenarios. On the server, the kernel will validate this information on your behalf in the super call from the table validateField method. Because it happens in the kernel, a fault will not be logged because the AifFault is defined in X++.

16

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

Consider adding foreign key validation prior to the super call to ensure that the integrity and log faults will be sent back to the caller in an AIF fault. The messages for the existing validation logic were written for the rich client consumer. When the validation problems are raised to the user in the rich client, they have enough context information because they are typically looking at a form and have just entered data in a specific field or tried to save the form. Application developers should try to use the same message for all consumers, but consider whether they make sense in the service context. You should not refactor every existing validation check used by a service to use faults. We recommend that you consider the schema that the service exposes and all of the scenarios that are not valid that exist. It is reasonable that sensitive information, such as errors and warnings, remain behind the service implementation and are visible only to the rich client user because of security implications. We also recommend that you modify the validation message so that it contains the appropriate amount of information without providing sensitive information. Typically, the validation message will need to be adjusted to remove some extra information. Because the rich client has very good context, the same message can typically be used for both the client and service scenarios. When refactoring messages, we recommend that you determine whether sensitive data that the service consumer does not need to see is being returned. The service consumer can send many documents in a single request, so their context is not as precise as the rich client. The service infrastructure tracks the document, table, row, and field that are being validated and logs this context information as it invokes validation methods. This approach allows the application developer to reuse most of the existing validation messages. In the rich client mode, the service logging code is not invoked, so it has no impact on the rich client. Because the service consumer is an application, it is necessary to provide fault information that is not localized, called a fault code. This allows the consumer to take programmatic action on the fault. These fault codes are implemented as macros. When the fault is logged, you pass the fault code to the AifFault API. The following code is a ledger journal fault macro found in the AOT under Macros > LedgerJournalFaults:
#DEFINE. LedgerAccountIsBlocked (' LedgerAccountIsBlocked')

The AifFault API has the ability to log errors and warnings and to throw exceptions. Most application developers will refactor code from the checkFailed method to the AifFault::checkFailedLogFault method as shown in the following code: Before:
return checkFailed(strfmt("@SYS19246", this.AccountNum));

After:
return AifFault::checkFailedLogFault(strfmt("@SYS19246", this.AccountNum), #LedgerAccountIsBlocked);

It is possible that you may want to override the global fault context when a row is validated and you want to be more precise. It is necessary to change the context before faults are logged and to reset the global context back to its previous state. This is shown in the following code:
boolean tableId fieldId ; isValid; tableId; fieldId;

tableId = tablenum(LedgerJournalTrans); fieldId = fieldnum(LedgerJournalTrans, Dimension); AifFaultContext::setGlobalContextField(tableId, fieldId); isValid = this.validateDimension(); AifFaultContext::setGlobalContext(originalFaultContext);

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

17

Do research and document the current validation rules. Do understand the validation between the form, form data source, and form-based class hierarchies. Do refactor validation checks based on the schema that is exposed. Consider using the strategy pattern to vary defaulting rules on the table. Consider making the base strategy abstract. Consider using the factory pattern on the base abstract strategy to construct the concrete strategy. Consider adding a validateRow method on the strategy. Consider validating foreign keys prior to the validateField super call. Do keep sensitive information inside the service boundary. Consider changing messages so you do not disclose too much or too little information. Do create non-localizable fault codes. Do use the United States English message as guidance for the fault code. Faults are not translated. Do use Pascal casing for fault codes. Do throw exceptions when it is not possible for code to continue. Do not attempt to catch exceptions.

Security
The services run in the context of the caller so that the calling user must be a Microsoft Dynamics AX user and have the appropriate permissions. Each service is secured by a single security key. Therefore, after a user is granted access to the security key, they have access to all of the service operations on that service. Each table has a security key. In the interactive context, the user only has to have access to this table security key. Services do not use or recognize the table security key, however, both the interactive and service scenarios enforce the table permissions framework (TPF). The security key for each service is specified in service properties accessed in the AOT through the Services node

18

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

19

Users are granted access to the service by using the User group permissions form. To open this form, click Administration > Setup > User groups > Permissions. To grant access to the user group, select the check box next to the service. Note that the access options do not apply to the service security.

20

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

Debugging
Because the service is implemented in X++, it can be invoked from X++ just like any other class. AIF forwards the request to the AOS using the .NET Business Connector. However, when the service is invoked from a Web service consumer, there are some additional configuration steps that you must take so that the X++ debugger will stop at your break points. Enable breakpoints 1. Click Start > Control Panel > Administrative Tools > Microsoft Dynamics AX 2009 Server Configuration to open the Microsoft Dynamics AX Server Configuration utility . 2. Click Manage and then select Create configuration. . 3. In the Create Configuration form, name the new configuration and click OK. 4. On the Application Object Server tab, select Enable breakpoints to debug code X++ code running on this server. 5. Click Apply. 6. If a message is displayed that indicates that the AOS should be restarted, click OK. Enable desktop interaction for the World Wide Web Publishing Service 1. Click Start > Control Panel > Administrative Tools > Services to open the services form. 2. Right-click the World Wide Web Publishing Service, and then click Properties. 3. Click the Log On tab and select Allow service to interact with desktop. 4. Click OK to close the properties window. Configure Business Connector debugging 1. Click Start > Control Panel > Administrative Tools > Microsoft Dynamics AX 2009 Configuration to open the Microsoft Dynamics AX Configuration utility. 2. Set the Configuration Target field to Business Connector (non-interactive use only). 3. Click Manage and then click Create configuration to create a new configuration that allows debugging. 4. Give the configuration a name and click OK. 5. On the Developer tab, select Enable user breakpoints to debug code running in the Business Connector. 6. Copy the Development license code to the Development license code and Confirm license code fields and click Apply. Configure debugging on the client 1. Open the Microsoft Dynamics AX client and click Tools > Options. 2. On the Development tab, in the Debug mode field, select When Breakpoint, and then click Apply. 3. Close the Options form 4. Open the Microsoft Dynamics AX development environment and set a breakpoint. 5. Click Tools > Developer tools > Debugger to launch the X++ debugger. 6. Run the Web service and the X++ debugger will stop running at the breakpoint.

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

21

Resources
The following list contains AIF resources and documentation: AIF developer documentation on MSDN AIF administrator documentation on TechNet AIF team blog Inside Microsoft Dynamics AX 2009 Book

22

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX 2009

Microsoft Dynamics is a line of integrated, adaptable business management solutions that enables you and your people to make business decisions with greater confidence. Microsoft Dynamics works like and with familiar Microsoft software, automating and streamlining financial, customer relationship and supply chain processes in a way that helps you drive business success. U.S. and Canada Toll Free 1-888-477-7989 Worldwide +1-701-281-6500 www.microsoft.com/dynamics

The information contained in this document represents the current view of Microsoft Corporation on the issues discussed as of the date of publication. Because Microsoft must respond to changing market conditions, this document should not be interpreted to be a commitment on the part of Microsoft, and Microsoft cannot guarantee the accuracy of any information presented after the date of publication. This document is for informational purposes only. MICROSOFT MAKES NO WARRANTIES, EXPRESS, IMPLIED, OR STATUTORY, AS TO THE INFORMATION IN THIS DOCUMENT. Complying with all applicable copyright laws is the responsibility of the user. Without limiting the rights under copyright, no part of this document may be reproduced, stored in or introduced into a retrieval system, or transmitted in any form or by any means (electronic, mechanical, photocopying, recording, or otherwise), or for any purpose, without the express written permission of Microsoft Corporation. Microsoft may have patents, patent applications, trademarks, copyrights, or other intellectual property rights covering subject matter in this document. Except as expressly provided in any written license agreement from Microsoft, the furnishing of this document does not give you any license to these patents, trademarks, copyrights, or other intellectual property. 2009 Microsoft Corporation. All rights reserved. Microsoft, Microsoft Dynamics AX, and the Microsoft Dynamics Logo are trademarks of the Microsoft group of companies.

TIPS FOR CREATING SERVICES IN MICROSOFT DYNAMICS AX

23

You might also like