Concurrency
When update operations are performed, the Dynamics GP service must handle any concurrency issues that occur. These issues happen because other users have locked the same rows in the database that the update operation is trying to change. Documents for which the Microsoft Dynamics GP client application actively locks rows, such as sales document, can produce these situations.
Typically, other users in Microsoft Dynamics GP won't see concurrency issues caused by Dynamics GP service operations. The update operations occur quickly, and hold row locks for only the time necessary to perform the update. If a concurrency issue is caused by a Dynamics GP service update operation, the other user affected will see the standard message displayed that indicates another user has updated a record.
The most likely scenario in which a Dynamics GP service update operations will encounter concurrency issues occurs when the web service application is updating a large number of rows that the Microsoft Dynamics GP client can actively lock. For example, if a web service application was updating all of the sales orders created on a specific date, and other users are accessing the Microsoft Dynamics GP system, it is possible the web service application will encounter one of the sales documents being locked (in use). If this occurs, a validation exception will occur, indicating the sales order being updated was in use.
Your code must be able to handle concurrency exceptions that occur. The following C# example demonstrates one way this can be done. This example updates the comment for all sales orders created on 3/17/10. If a concurrency exception occurs because a sales order is being edited by another user, the following message is displayed. The user can choose to try updating the sales order again, or canceling the update and moving to the next document.
In this example, a list of the sales orders for the specific date is retrieved. The comment for each of these order is updated. Note the try...catch block that handles the validation error if a concurrency issue occurs. If the user chooses to retry the update operation, the goto statement causes the update operation to be run again.
** Legacy endpoint**
using System; using System.Collections.Generic; using System.Text; using System.Windows.Forms; using DynamicsGPWebServiceSample.DynamicsGPService; using System.Xml; using System.Web.Services.Protocols; namespace DynamicsGPWebServiceSample { class Program { static void Main(string[] args) { CompanyKey companyKey; Context context; BetweenRestrictionOfNullableOfDateTime transactionDateRestriction; SalesOrderCriteria salesOrderCriteria; SalesOrderSummary[] salesOrderSummary; DateTime dateValue; SalesDocumentKey salesOrderKey; SalesOrder salesOrder; Policy salesOrderUpdatePolicy; ValidationResult validationResult; // Create an instance of the service DynamicsGP wsDynamicsGP = new DynamicsGP(); // Be sure the default credentials are used wsDynamicsGP.UseDefaultCredentials = true; // Create a context with which to call the service context = new Context(); // Specify which company to use (sample company) companyKey = new CompanyKey(); companyKey.Id = (-1); // Set up the context object context.OrganizationKey = (OrganizationKey)companyKey; // Create a date restriction object transactionDateRestriction = new BetweenRestrictionOfNullableOfDateTime(); dateValue = new DateTime(2010, 3, 17); transactionDateRestriction.EqualValue = dateValue; // Create a sales order criteria object salesOrderCriteria = new SalesOrderCriteria(); salesOrderCriteria.Date = transactionDateRestriction; // Retrieve the sales order summaries specified by the criteria salesOrderSummary = wsDynamicsGP.GetSalesOrderList( salesOrderCriteria, context); // Retrieve the update policy for sales orders salesOrderUpdatePolicy = wsDynamicsGP.GetPolicyByOperation( "UpdateSalesOrder", context); // Update the comment for each of the documents found foreach (SalesOrderSummary a in salesOrderSummary) { // Create a sales document key salesOrderKey = new SalesDocumentKey(); salesOrderKey = a.Key; // Retrieve the sales order salesOrder = wsDynamicsGP.GetSalesOrderByKey(salesOrderKey, context); // Set the comment property salesOrder.Comment = "Customer notified - March 17"; // Update the sales order object UpdateSalesDocument: try { wsDynamicsGP.UpdateSalesOrder(salesOrder, context, salesOrderUpdatePolicy); } catch (SoapException soapErr) { // Try retrieving the Message node XmlDocument doc = new XmlDocument(); XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable); nsManager.AddNamespace("sm", "http://schemas.datacontract.org/2004/07/System.ServiceModel"); doc.LoadXml(soapErr.Detail.InnerXml); XmlNode node = doc.SelectSingleNode( "//sm:InnerException//sm:Message", nsManager); if (node != null) { // Use the GUID to load the validation details Guid LogId = new Guid(node.InnerText.Trim()); // Get the validation result object validationResult = wsDynamicsGP. GetLoggedValidationResultByKey(LogId, context); if (validationResult.Errors[0].Id == "EConnectError-2079") { // It is a concurrency error, with record in use if (MessageBox.Show(null, "Document: " + a.Key.Id + ". " + validationResult.Errors[0].Message + ".", "Sales Document Update", MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1) == DialogResult.Retry) { goto UpdateSalesDocument; } } else { // It is a different error MessageBox.Show(null, validationResult.Errors[0].Message, "Sales Document Update"); } } else { // Display the system exception message MessageBox.Show(soapErr.Message, "Sample", MessageBoxButtons.OK, MessageBoxIcon.Stop); } } } } } }
** Native endpoint **
using System; using System.Linq; using System.Text; using System.Windows.Forms; using WebServiceSample.DynamicsGPService; using System.ServiceModel; namespace DynamicsGPWebServiceSample { class Program { static void Main(string[] args) { CompanyKey companyKey; Context context; BetweenRestrictionOfNullableOfdateTime transactionDateRestriction; SalesOrderCriteria salesOrderCriteria; SalesOrderSummary[] salesOrderSummary; DateTime dateValue; SalesDocumentKey salesOrderKey; SalesOrder salesOrder; Policy salesOrderUpdatePolicy; ValidationResult validationResult; // Create an instance of the service DynamicsGPClient wsDynamicsGP = new DynamicsGPClient(); // Create a context with which to call the service context = new Context(); // Specify which company to use (sample company) companyKey = new CompanyKey(); companyKey.Id = (-1); // Set up the context object context.OrganizationKey = (OrganizationKey)companyKey; // Create a date restriction object transactionDateRestriction = new BetweenRestrictionOfNullableOfdateTime(); dateValue = new DateTime(2010, 3, 19); transactionDateRestriction.EqualValue = dateValue; // Create a sales order criteria object salesOrderCriteria = new SalesOrderCriteria(); salesOrderCriteria.Date = transactionDateRestriction; // Retrieve the sales order summaries specified by the criteria salesOrderSummary = wsDynamicsGP.GetSalesOrderList( salesOrderCriteria, context); // Retrieve the update policy for sales orders salesOrderUpdatePolicy = wsDynamicsGP.GetPolicyByOperation( "UpdateSalesOrder", context); // Update the comment for each of the documents found foreach (SalesOrderSummary a in salesOrderSummary) { // Create a sales document key salesOrderKey = new SalesDocumentKey(); salesOrderKey = a.Key; // Retrieve the sales order salesOrder = wsDynamicsGP.GetSalesOrderByKey(salesOrderKey, context); // Set the comment property salesOrder.Comment = "Customer notified - March 17"; // Update the sales order object UpdateSalesDocument: try { wsDynamicsGP.UpdateSalesOrder(salesOrder, context, salesOrderUpdatePolicy); // Close the service if (wsDynamicsGP.State != CommunicationState.Faulted) { wsDynamicsGP.Close(); } } catch (FaultException<System.ServiceModel.ExceptionDetail> ex) { if (ex.Detail.InnerException != null) { // Use the GUID to load the validation details Guid LogId = new Guid( ex.Detail.InnerException.Message.Trim()); // Get the validation result object validationResult = wsDynamicsGP. GetLoggedValidationResultByKey(LogId, context); if (validationResult.Errors[0].Id == "EConnectError-2079") { // It is a concurrency error, with record in use if (MessageBox.Show(null, "Document: " + a.Key.Id + ". " + validationResult.Errors[0].Message + ".", "Sales Document Update", MessageBoxButtons.RetryCancel, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1) == DialogResult.Retry) { goto UpdateSalesDocument; } else { if (wsDynamicsGP.State != CommunicationState.Faulted) { wsDynamicsGP.Close(); } } } else { // It is a different error MessageBox.Show(null, validationResult.Errors[0].Message, "Sales Document Update"); } } else { // Display the system exception message MessageBox.Show(ex.Message, "Sample", MessageBoxButtons.OK, MessageBoxIcon.Stop); if (wsDynamicsGP.State != CommunicationState.Faulted) { wsDynamicsGP.Close(); } } } } } } }