Let's head to Visual Studio and start consuming the document service that we created. You can open the Visual Studio project for this chapter, included in the code files for the book, to see the service in action.
If you are a more experienced Visual Studio user, you can create a project yourself by performing the following steps:
To add the service reference, perform the following steps:
DYNAX01:8101
with the server and port of your installation. You should see a form that looks similar to the following screenshot:TitleServiceRef
in the Namespace field and click on OK.In the following sections, we will go through all of the operations that are available in the service by using a console application. The console application prompts the user to select the action that needs to be executed. We will look only at the methods that consume the service. For the complete sample application, download and install the sample project for this chapter, which is included in the code files of the book. Be sure to read Appendix, Installing the Demo Application, for information on how to install and run the code.
The first service operation lets us insert records into Microsoft Dynamics AX. The flow for using the Create
operation is as follows:
Create
operation that returns the entity keys of the inserted records.The following code reads an XML file that contains some sample titles to be inserted:
static void createTitles() { List<MovieTitle> sampleTitles; int i = 0; // Read the XML file containing the sample data into a list using (var reader = new StreamReader(@"C:TempTitleDemoData.xml")) { XmlSerializer deserializer = new XmlSerializer(typeof(List<MovieTitle>)); sampleTitles = (List<MovieTitle>) deserializer.Deserialize(reader); } // For all of the titles, create a title in the Ax database foreach (MovieTitle title in sampleTitles) { i++; // Create a title entity AxdEntity_CVRTitle titleEntity = new AxdEntity_CVRTitle(); // Fill in all the fields titleEntity.Name = title.Name; titleEntity.Description = title.Description; titleEntity.LengthInMinutes = Convert.ToInt32(title.Length); // For int and real values, you must flag them as fill in // This is to tell Dynamics that they are not null, but the // default value titleEntity.LengthInMinutesSpecified = true; // Create a title document instance AxdCVRTitle titleDocument = new AxdCVRTitle(); // Initialize the list of title entities in the document // CVRTitle is a list of title entities titleDocument.CVRTitle = new AxdEntity_CVRTitle[1] { titleEntity }; // Create an instance of the document service client using (CVRTitleDocumentServiceClient client = new CVRTitleDocumentServiceClient()) { // Insert the title in Ax EntityKey[] entityKeys = client.create(null, titleDocument); // Report progress to the user Console.WriteLine(String.Format("Title {0} created", titleEntity.Name)); } } }
After executing the previous code, we can see the titles within Microsoft Dynamics AX:
The Find
operation uses a QueryCriteria
object that contains the criteria to be filtered and returns a document that contains the record entities. The flow for using the Find
operation is as follows:
QueryCriteria
object that contains the criteria's elements.Find
operation to retrieve a document instance that contains the resulting records.For some operations, including the Find
operation, you are required to pass a QueryCriteria
object. Based on this QueryCriteria
object, records are queried in Microsoft Dynamics AX. To facilitate the creation of QueryCriteria
instances, a method was created, which is shown as follows:
static QueryCriteria createSingleCriteria(string dataSource , string fieldName , Operator op , string value1 , string value2) { // Prepare a queryCriteria instance QueryCriteria criteria = new QueryCriteria(); // Create a criteria element that represents a query range CriteriaElement criteriaElement = new CriteriaElement(); criteriaElement.DataSourceName = dataSource; criteriaElement.FieldName = fieldName; criteriaElement.Operator = op; criteriaElement.Value1 = value1; criteriaElement.Value2 = value2; // Put the criteria element in the QueryCriteria instance criteria.CriteriaElement = new CriteriaElement[1] { criteriaElement }; return criteria; }
Now that we have a way to create the query criteria that is needed for the Find
operation, we can go ahead and use the Find
operation to get the data from Microsoft Dynamics AX, as demonstrated in the following code snippet:
static void getTitles_Find() { // Variable to hold the title document AxdCVRTitle titleDocument = new AxdCVRTitle(); // Create a criteria element that selects titles that run over 110 // minutes QueryCriteria criteria = Program.createSingleCriteria("CVRTitle" , "LengthInMinutes", Operator.Greater, "110", null); // Create a client for as long as we need to using (CVRTitleDocumentServiceClient client = new CVRTitleDocumentServiceClient()) { // Find the titles that match the criteria titleDocument = client.find(null, criteria); // Loop all the titles foreach (AxdEntity_CVRTitle title in titleDocument.CVRTitle) { // Report the results to the console window Console.WriteLine(title.Id + ' ' + title.Name + ' ' + title.LengthInMinutes); } } }
A part of the result looks as follows:
T000000006 Schindler's List 114 T000000007 The Dark Knight 119 T000000008 The Lord of the Rings: The Return of the King 112 T000000011 Fight Club 115
The Read
operation sounds like the Find
operation, but there is a difference. Find
uses the query criteria as input and returns the title document immediately with all of the resulting rows. Read
returns the same document but does not take the query criteria as a parameter. Instead, Read
uses a set of Entitykey
objects as input. As a result, the Read
operation returns only one record for each entity key in the set, because the entity keys correspond to the primary key of the record.
You could be asking yourself why you would want to use Read
instead of Find
if the latter gives you the same result in one operation. Well, the answer is twofold.
The first scenario is one where you have already cached the entity keys in your application. In other words, you already know the unique key of the records that you want to retrieve. Then you can just construct an array of entity keys and invoke the Read
operation.
The flow for using the Read
operation with the custom entity keys is as follows:
The following is the code implementation:
static void getTitle_ReadWithEntityKey() { // Let the user enter an Id in the console window Console.WriteLine("Enter a title Id to look for :"); string titleId = Console.ReadLine(); // Create an instance of the keyfield containing the title id to // search for KeyField keyField = new KeyField() { Field = "Id", Value = titleId }; // Create an entity key instance and put in the key field data EntityKey entityKey = new EntityKey(); entityKey.KeyData = new KeyField[1] { keyField }; // Create an array of entity keys and put in the previously created key EntityKey[] entityKeys = new EntityKey[1] { entityKey }; AxdCVRTitle titleDocument = new AxdCVRTitle(); // Create a client for as long as we need to using (CVRTitleDocumentServiceClient client = new CVRTitleDocumentServiceClient()) { // Use the keys to read all of the titles titleDocument = client.read(null, entityKeys); } // Loop all the titles to report to the console window foreach (AxdEntity_CVRTitle title in titleDocument.CVRTitle) { Program.printSingleTitle(title); } }
If we execute the previous code for title ID T000000007
, the following result is printed to the console window:
Title : T000000007 Name : The Dark Knight Length in minutes : 119 Description : When Batman, […]
The second scenario, in which you can use the Read
operation, is used in combination with the FindKeys
operation. This can enhance the performance. Let's say that you have a .NET application that queries Microsoft Dynamics AX. It's possible that your query will return a large number of records but you want to use paging so that you don't have to load all of the data at once. So, you can use the FindKeys
operation to return only the keys of the records instead of all of the fields. Once you have the keys, you can implement paging in your application and call the Read
operation with the subset of keys that are actually needed.
The flow for using the Read
operation with FindKeys
is as follows:
QueryCriteria
instance that contains the criteria for finding the records.FindKeys
operation to retrieve the keys that match the query.Read
operation to return a document that contains the related records.The following is the code implementation:
static void getAllTitles_ReadWithFindKeys() { // Variable to hold the title document AxdCVRTitle titleDocument = new AxdCVRTitle(); // Create a criteria element that selects titles that run over 110 // minutes QueryCriteria criteria = Program.createSingleCriteria("CVRTitle" , "LengthInMinutes", Operator.Greater, "110", null); // Create a client for as long as we need to using (CVRTitleDocumentServiceClient client = new CVRTitleDocumentServiceClient()) { // Call the findKeys operation to fetch all of the keys that // match the query criteria EntityKey[] entityKeys = client.findKeys(null, criteria); // Check if we had matching titles if (entityKeys.Length > 0) { // Use the keys to read all of the title records titleDocument = client.read(null, entityKeys); } } if (titleDocument != null) { // Loop all the titles to report to the user foreach (AxdEntity_CVRTitle title in titleDocument.CVRTitle) { Console.WriteLine(title.Id + ' ' + title.Name + ' ' + title.LengthInMinutes); } } }
As we are using the same query criteria, we should see the same result as the Find
operation. A part of the result looks as follows:
T000000006 Schindler's List 114 T000000007 The Dark Knight 119 T000000008 The Lord of the Rings: The Return of the King 112 T000000011 Fight Club 115
To update records in the Microsoft Dynamics AX database, the Update
operation can be used. First, you need to use the Read
operation to get the records that you want to update. For this, you need to specify the entity keys. Once you have the document that contains the records that you want to update, you can edit the fields and then call the Update
operation. The basic flow for updating the records is as follows:
Entitykey
objects.Read
operation to retrieve the data from Microsoft Dynamics AX.action
property on the changed records to be updated.Update
operation.The following is the code implementation:
static void updateTitle() { Console.WriteLine("Enter a title Id to look for :"); string titleId = Console.ReadLine(); // Create an instance of the keyfield containing a record id to // search for KeyField keyField = new KeyField() { Field = "Id", Value = titleId }; // Create an entity key instance and put in the key field data EntityKey entityKey = new EntityKey(); entityKey.KeyData = new KeyField[1] { keyField }; // Call the findKeys operation to fetch all of the keys that match // the query criteria EntityKey[] entityKeys = new EntityKey[1] { entityKey }; // Create a client for as long as we need to using (CVRTitleDocumentServiceClient client = new CVRTitleDocumentServiceClient()) { // Use the keys to read all of the titles AxdCVRTitle titleDocument = client.read(null, entityKeys); // Get the CVRTitle record entity AxdEntity_CVRTitle title = titleDocument.CVRTitle.First(); title.Description = "Updated Description"; title.action = AxdEnum_AxdEntityAction.update; title.actionSpecified = true; // Invoke the update operation client.update(null, entityKeys, titleDocument); } }
As you might have guessed already, the Delete
operation will delete records from Microsoft Dynamics AX. The flow for using the Delete
operation is as follows:
EntityKey
objects.Delete
operation to delete the data from Microsoft Dynamics AX.The following code will prompt the user to enter a title ID and then delete that title from Microsoft Dynamics AX:
static void deleteTitle() { // Let the user enter an Id in the console window Console.WriteLine("Enter a title Id to delete :"); string titleId = Console.ReadLine(); // Create an instance of the keyfield containing the title id to // search for KeyField keyField = new KeyField() { Field = "Id", Value = titleId }; // Create an entity key instance and put in the key field data EntityKey entityKey = new EntityKey(); entityKey.KeyData = new KeyField[1] { keyField }; // Create an array of entity keys and put in the previously // created key EntityKey[] entityKeys = new EntityKey[1] { entityKey }; using (CVRTitleDocumentServiceClient client = new CVRTitleDocumentServiceClient()) { client.delete(null, entityKeys); } }
The GetKeys
operation will return the keys for records that match the document filter that is configured on the integration port. Document filters are only available on enhanced integration ports.
For the sake of this demonstration, we assume that a document filter is added on the port used by the CVRTitleDocumentService service, as shown in the following screenshot:
The flow for using the GetKeys
operation is as follows:
DocumentPaging
object that contains the number of keys to be returned (optional).getKeys
operation that returns the entity keys that match the document filter.Read
operation to retrieve the data of the related records when needed.The following code uses GetKeysoperation
to fetch the records from Microsoft Dynamics AX that match the document filter that we just discussed:
static void getKeys() { AxdCVRTitle titleDocument = new AxdCVRTitle(); // Create a client for as long as we need to using (CVRTitleDocumentServiceClient client = new CVRTitleDocumentServiceClient()) { // Call the findKeys operation to fetch all of the keys that // match the document filter EntityKeyPage keyPage = client.getKeys(null, null); // Fetch the entity key list from the page EntityKey[] entityKeys = keyPage.EntityKeyList; // Check if we had matching titles if (entityKeys.Length >= 0) { // Use the keys to read all of the titles titleDocument = client.read(null, entityKeys); } } // Loop all the titles to report to the console foreach (AxdEntity_CVRTitle title in titleDocument.CVRTitle) { Console.WriteLine(title.Id + ' ' + title.Name + ' ' + title.LengthInMinutes); } }
Based on the document filter that selects titles starting with The
, a part of the resulting titles looks as follows:
T000000001 The Godfather 102 T000000002 The Godfather: Part II 102 T000000004 The Good, the Bad and the Ugly 97 T000000007 The Dark Knight 119 T000000008 The Lord of the Rings: The Return of the King 112 T000000009 The Dark Knight Rises 103
The GetChangedKeys
operation also fetches the keys of records from Microsoft Dynamics AX that match the document filter. In addition to this, it also restricts the returned keys to records that have changed since a given date and time.
Change tracking
To be able to use getChangedKeys
, SQL Server Change Tracking has to be configured. Once change tracking has been configured, the integration ports will need to be reactivated.
Information about change tracking can be found at the following links:
The flow for using the GetChangedKeys
operation is the same as the one for using the getKeys
operation. The difference is that you can retrieve only the records that have changed since a given date instead of all the records that the document filter applies to. The following code shows us the use of the same document filter. It also illustrates the use of change tracking to further narrow down the list to only the records that were changed. This assumes that we have updated the records with change tracking enabled:
static void getChangedKeys() { AxdCVRTitle titleDocument = new AxdCVRTitle(); // Create a client for as long as we need to using (CVRTitleDocumentServiceClient client = new CVRTitleDocumentServiceClient()) { // Call the getChangedKeys operation to fetch all of the keys // that were changed // The change date used here was 2013/12/01 15:30 EntityKeyPage keyPage = client.getChangedKeys(null, null, new DateTime(2013, 12, 01, 15, 30, 00)); // Fetch the entity key list from the page EntityKey[] entityKeys = keyPage.EntityKeyList; // Check if we had matching titles if (keyPage.PageStatus == EntityKeyPageStatus.Success && entityKeys.Length > 0) { // Use the keys to read all of the titles titleDocument = client.read(null, entityKeys); } } // Loop all the titles to report to the console foreach (AxdEntity_CVRTitle title in titleDocument.CVRTitle) { Console.WriteLine(title.Id + ' ' + title.Name + ' ' + title.LengthInMinutes + ' ' + title.Description); } }
As we updated one record for this sample, the following is the result:
T000000007 The Dark Knight 119 Updated Description for the dark knight
3.141.200.3