private XDocument GetProductData <T>(IEnumerable <T> ids, string rangeFieldName) { CP.RetailTransactionServiceResponse serviceResponse = this.GetResponseFromMethod( GetProductDataMethodName, string.Join(",", ids), 1, rangeFieldName); // Check result if (!serviceResponse.Success) { throw new CommunicationException( CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterTransactionServiceMethodCallFailure, string.Format("Invoke method {0} failed: {1}", GetProductDataMethodName, serviceResponse.Message)); } // Throw if service response does not contain any data. if (serviceResponse.Data == null || serviceResponse.Data.Length == 0) { throw new CommunicationException( CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterResponseParsingError, "Service response does not contain any data."); } string productsXml = (string)serviceResponse.Data[0]; object[] dataSource = (object[])serviceResponse.Data[1]; var dictionary = BuildDataSourceMap(dataSource); return(UpdateTableNamesWithDataSourceMap(productsXml, dictionary)); }
/// <summary> /// Gets the product search results using the specified category identifier. /// </summary> /// <param name="currentChannelId">The identifier of the channel that the request is originating from.</param> /// <param name="searchText">The search text that the result should be relevant to.</param> /// <param name="targetChannelId">The identifier of the channel to which the resultant product representatives must belong to.</param> /// <param name="targetCatalogId">The identifier of the catalog to which the resultant product representatives must belong to.</param> /// <param name="attributeIdCollectionString">The attribute values to retrieve along with the result set.</param> /// <param name="settings">The settings to use while processing this request.</param> /// <returns>A collection of product search results representing products in the requested category or its sub-categories.</returns> internal ReadOnlyCollection <ProductSearchResult> SearchProductsByText(long currentChannelId, string searchText, long targetChannelId, long targetCatalogId, string attributeIdCollectionString, QueryResultSettings settings) { CP.RetailTransactionServiceResponse transactionServiceResponse = this.GetResponseFromMethod( GetProductsByKeywordMethodName, currentChannelId, searchText, settings.Paging.Skip + 1, // 1-based in AX settings.Paging.NumberOfRecordsToFetch, settings.Sorting == null ? RecordIdAttributeName : settings.Sorting.ToString(), // order by settings.Sorting == null || !settings.Sorting.IsSpecified ? (settings.Sorting.Columns.First().IsDescending ? 1 : 0) : 0, // sort order: Ascending by default false, // return total count string.Empty, // language targetChannelId, targetCatalogId, attributeIdCollectionString); // Check result if (!transactionServiceResponse.Success) { throw new CommunicationException( CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterCommunicationFailure, string.Format("Invoke method {0} failed: {1}", GetProductsByCategoryMethodName, transactionServiceResponse.Message)); } // Throw if service response does not contain any data. if (transactionServiceResponse.Data == null || transactionServiceResponse.Data.Length == 0) { throw new CommunicationException(CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterResponseParsingError, "Service response does not contain any data."); } string searchResultsXml = (string)transactionServiceResponse.Data[0]; var results = this.GetResultsFromXml(searchResultsXml); return(results); }
/// <summary> /// Invokes transaction service health check. /// </summary> /// <returns>True if IsAlive call succeeded and false otherwise.</returns> public bool IsAlive() { TransactionServiceInvoker invoker = (channel, info) => { return(channel.IsAlive(new CP.IsAlive()).result); }; CP.RetailTransactionServiceResponse response = this.GetResponseFromMethod(invoker, IsAliveMethodName, 0); return(response.Success); }
/// <summary> /// Gets the products using the specified category identifier. /// </summary> /// <param name="currentChannelId">The channel identifier of the current context.</param> /// <param name="targetChannelId">The channel identifier of the target channel.</param> /// <param name="targetCatalogId">The catalog identifier in the target channel.</param> /// <param name="targetCategoryId">The category identifier in the target channel.</param> /// <param name="skip">The number of records to skip.</param> /// <param name="top">The maximum number of records to return.</param> /// <param name="attributeIds">The comma-separated list of attribute record identifiers to retrieve. Specify '*' to retrieve all attributes.</param> /// <param name="includeProductsFromDescendantCategories">Whether category based product search should return products from all descendant categories.</param> /// <returns>A collection of products under the specified category.</returns> public ReadOnlyCollection <Product> GetProductsByCategory( long currentChannelId, long targetChannelId, long targetCatalogId, long targetCategoryId, long skip, long top, string attributeIds, bool includeProductsFromDescendantCategories) { if (currentChannelId <= 0) { throw new ArgumentOutOfRangeException("currentChannelId", "The current channel identifier is required."); } if (skip < 0 || skip == int.MaxValue) { throw new ArgumentOutOfRangeException("skip"); } if (top < 0) { throw new ArgumentOutOfRangeException("top", "The value must be a positive integer."); } ThrowIf.NullOrWhiteSpace(attributeIds, "attributeIds"); CP.RetailTransactionServiceResponse serviceResponse = this.GetResponseFromMethod( GetProductsByCategoryMethodName, currentChannelId, targetCategoryId, skip + 1, // 1-based in AX top, "ItemId", // order by 1, // sort order false, // return total count string.Empty, // language targetChannelId, targetCatalogId, attributeIds, true, // _includePrice includeProductsFromDescendantCategories); // Check result if (!serviceResponse.Success) { throw new CommunicationException( CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterCommunicationFailure, string.Format("Invoke method {0} failed: {1}", GetProductsByCategoryMethodName, serviceResponse.Message)); } // Throw if service response does not contain any data. if (serviceResponse.Data == null || serviceResponse.Data.Length == 0) { throw new CommunicationException( CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterResponseParsingError, "Service response does not contain any data."); } string productsXml = (string)serviceResponse.Data[0]; var products = this.ConvertProductsXmlToProducts(productsXml); return(products); }
/// <summary> /// Invoke method with given method name and parameter list from AX without the check for null on the responding data. /// </summary> /// <param name="methodName">Method name.</param> /// <param name="parameters">The parameter set.</param> /// <returns>A list of returned items if available.</returns> /// <exception cref="CommunicationException">Throws if the call failed.</exception> private ReadOnlyCollection <object> InvokeMethodAllowNullResponse(string methodName, params object[] parameters) { ThrowIf.Null <string>(methodName, "methodName"); CP.RetailTransactionServiceResponse serviceResponse = this.GetResponseFromMethod(methodName, parameters); if (serviceResponse == null) { throw new CRT.CommunicationException( CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterResponseParsingError, "Service response is null."); } return(new ReadOnlyCollection <object>(serviceResponse.Data)); }
/// <summary> /// Invoke extension method with given method name and parameter list from AX. /// </summary> /// <param name="methodName">Method name.</param> /// <param name="parameters">The parameter set.</param> /// <returns>A list of returned items if available.</returns> /// <exception cref="CommunicationException">Throws if the call failed.</exception> public ReadOnlyCollection <object> InvokeExtensionMethod(string methodName, params object[] parameters) { ThrowIf.Null <string>(methodName, "methodName"); CP.RetailTransactionServiceResponse serviceResponse = this.GetResponseFromMethodEx(methodName, parameters); // Throw if service response does not contain any data. if (serviceResponse.Data == null || serviceResponse.Data.Length == 0) { throw new CRT.CommunicationException( CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterResponseParsingError, "Service response does not contain any data."); } return(new ReadOnlyCollection <object>(serviceResponse.Data)); }
/// <summary> /// Gets the response from method. /// </summary> /// <param name="transactionServiceInvoker">Delegate that invokes a specific operation on channel object.</param> /// <param name="methodName">Name of the method.</param> /// <param name="parameterCount">Number of parameters used during the call. Used for instrumentation purposes.</param> /// <returns>The service response.</returns> private CP.RetailTransactionServiceResponse GetResponseFromMethod(TransactionServiceInvoker transactionServiceInvoker, string methodName, int parameterCount) { CP.RetailTransactionServiceResponse response = null; using (RealTimeServiceClientBoundaryPerfContext perfContext = new RealTimeServiceClientBoundaryPerfContext()) { Guid correlationId = Guid.NewGuid(); Guid relatedActivityId = Guid.NewGuid(); RetailLogger.Log.CrtTransactionServiceClientRtsCallStarted(correlationId, methodName, parameterCount, relatedActivityId); int resultCount = -1; string language = null; string company = null; CP.RetailRealTimeServiceContractChannel channel = null; Exception exception = null; try { channel = this.clientFactory.CreateTransactionServiceClient(); // Add HTTP header attribute named 'ms-dyn-aid' with value as activity id. using (var contextScope = new OperationContextScope(channel)) { this.SetActivityIdInHttpHeader(relatedActivityId); CP.RetailTransactionServiceRequestInfo requestInfo = this.clientFactory.CreateRequestInfo(); company = requestInfo.Company; language = requestInfo.Language; response = transactionServiceInvoker(channel, requestInfo); channel.Close(); } } catch (System.ServiceModel.CommunicationException ex) { // Retrieves the SubCode in the fault exception, and maps them to corresponding error resources and diagnostic entries. CommunicationErrors errorResourceId = CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterCommunicationFailure; string errorMessage = string.Empty; Tuple <string, string> faultCodes = TransactionServiceClient.ParseFaultException(ex); if (faultCodes.Item2.Equals(TransactionServiceClient.FailedAuthenticationFaultCode, StringComparison.OrdinalIgnoreCase)) { errorMessage = string.Format( "Real-time Service call for method '{0}' failed due to security reason such as misconfigured, or expired Real-time Service certificate. Please also verify if the Real-time Service certificate is being properly configured in AX.", methodName); errorResourceId = CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_TransactionServiceAuthenticationFailedFault; } else if (faultCodes.Item2.Equals(TransactionServiceClient.ForbiddenFaultCode, StringComparison.OrdinalIgnoreCase)) { errorMessage = string.Format( "Real-time Service call for method '{0}' failed due to invalid Real-time Service profile settings. Please make sure the Real-time Service profile user and identity provider fields are defined correctly in AX.", methodName); errorResourceId = CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_TransactionServiceForbiddenFault; } else if (faultCodes.Item1.Equals(TransactionServiceClient.SenderFaultCode, StringComparison.OrdinalIgnoreCase)) { errorMessage = string.Format( "Real-time Service call for method '{0}' failed due to an unhandled exception, or due to invalid user permissions settings in Real-time Service profile. Please refer to the exception details for more information.", methodName); errorResourceId = CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_TransactionServiceSenderFault; } exception = TransactionServiceClient.CreateCommunicationException(methodName, ex, errorResourceId, errorMessage); } catch (SecurityTokenException ex) { // channel.Abort() will never throw if (channel != null) { channel.Abort(); } exception = TransactionServiceClient.CreateCommunicationException(methodName, ex, CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_HeadquarterCommunicationFailure); } catch (TimeoutException ex) { exception = TransactionServiceClient.CreateCommunicationException(methodName, ex, CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_TransactionServiceTimeOut); } catch (Exception ex) { // channel.Abort() will never throw if (channel != null) { channel.Abort(); } exception = TransactionServiceClient.CreateCommunicationException(methodName, ex, CommunicationErrors.Microsoft_Dynamics_Commerce_Runtime_TransactionServiceException); } // Throws an exception wrapping localized AX message for unsuccessful request. if (exception == null && !response.Success) { exception = new HeadquarterTransactionServiceException( response.Data, string.Format("Real-time Service was successfully connected, but the method call {0} failed with this error : {1}", methodName, response.Message)) { // Since content in the response.Message is already localized on AX, we copy it directly to user message field. LocalizedMessage = response.Message }; } else if (response != null && response.Data != null) { resultCount = response.Data.Length; } if (exception != null) { RetailLogger.Log.CrtTransactionServiceClientRtsCallError(correlationId, methodName, parameterCount, language, company, exception.GetType().ToString(), exception, relatedActivityId); throw exception; } perfContext.ResultsCount = resultCount; perfContext.CallWasSuccessful(); RetailLogger.Log.CrtTransactionServiceClientRtsCallSuccessful(correlationId, methodName, parameterCount, resultCount, language, company, relatedActivityId); } return(response); }