/// <summary> /// Responsible for the processing of all inbound requests /// This method is reentrant and will call itself to /// for every invocation required in every generation /// of nesting. e.g services made up of services /// </summary> /// <param name="resourceDirectory">The singleton instance of the service library that contains all the logical services</param> /// <param name="xmlRequest">The actual client request message</param> /// <param name="dataListId">The id of the data list</param> /// <param name="errors">Errors resulting from this invoke</param> /// <returns></returns> public Guid Invoke(IDynamicServicesHost resourceDirectory, dynamic xmlRequest, Guid dataListId, out ErrorResultTO errors) { // Host = resourceDirectory #region Async processing of client request - queue the work item asynchronously //Get an UnlimitedObject from the xml string provided by the caller //TraceWriter.WriteTraceIf(_managementChannel != null && _loggingEnabled, "Inspecting inbound data request", Resources.TraceMessageType_Message); Guid result = GlobalConstants.NullDataListID; var allErrors = new ErrorResultTO(); errors = new ErrorResultTO(); if(xmlRequest.Async is string) { //TraceWriter.WriteTrace(_managementChannel, "Caller requested async execution"); bool isAsync; bool.TryParse(xmlRequest.Async, out isAsync); if(isAsync) { ThreadPool.QueueUserWorkItem(delegate { ErrorResultTO tmpErrors; //TraceWriter.WriteTrace(_managementChannel, "Queuing Asynchronous work", Resources.TraceMessageType_Message); xmlRequest.RemoveElementByTagName("Async"); IDynamicServicesInvoker invoker = new DynamicServicesInvoker(_dsfChannel, _managementChannel); result = invoker.Invoke(resourceDirectory, xmlRequest, dataListId, out tmpErrors); if(tmpErrors.HasErrors()) { allErrors.MergeErrors(tmpErrors); } //TraceWriter.WriteTrace(result.XmlString); if(result != GlobalConstants.NullDataListID) { // PBI : 5376 SvrCompiler.DeleteDataListByID(result, true); //TODO: Clean it up ;) } }); dynamic returnData = new UnlimitedObject(); returnData.Load(string.Format("<ServiceResponse>{0} Work Item Queued..</ServiceResponse>", DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff"))); return returnData; } } #endregion #region Get a handle on the service that is being requested from the service directory string serviceName = string.Empty; //Set the service name as this is a complex message //with multiple services requests embedded in the inbound data //This will allow us to if(xmlRequest.Service is IEnumerable<UnlimitedObject>) { IEnumerable<UnlimitedObject> services = xmlRequest.Service; dynamic serviceData = services.First(); if(serviceData.Service is string) { serviceName = serviceData.Service; } } //If there is only a single service request then get the service name if(xmlRequest.Service is string) { serviceName = xmlRequest.Service; } //If the service name does not exist return an error to the caller if(string.IsNullOrEmpty(serviceName)) { xmlRequest.Error = Resources.DynamicServiceError_ServiceNotSpecified; } //Try to retrieve the service from the service directory IEnumerable<DynamicService> service; Host.LockServices(); try { service = from c in resourceDirectory.Services where serviceName != null && c.Name.Trim().Equals(serviceName.Trim(), StringComparison.CurrentCultureIgnoreCase) select c; } finally { Host.UnlockServices(); } service = service.ToList(); if(!service.Any()) { TraceWriter.WriteTrace(_managementChannel, string.Format("Service '{0}' Not Found", serviceName), Resources.TraceMessageType_Error); allErrors.AddError(string.Format("Service '{0}' Not Found", serviceName)); throw new InvalidOperationException(string.Format("Service '{0}' Not Found", serviceName)); //xmlRequest.Error = Resources.DynamicServiceError_ServiceNotFound; } #endregion //Instantiate a Dynamic Invocation type to invoke the service #region Transactionalized Service Invocation with support for MS-DTC dynamic dseException = null; //The transactionScope is used to create an ambient transaction that every action will be subject to //This transaction try { //TraceWriter.WriteTrace(_managementChannel, string.Format("Setting up transaction scope", serviceName), Resources.TraceMessageType_Message); using(var transactionScope = new TransactionScope()) { //TraceWriter.WriteTrace(_managementChannel, string.Format("Invoking Service '{0}'", serviceName), Resources.TraceMessageType_Message); #region Process several requests to different services as a single unit of work //Type 3 request (read above) //This is handled differently to type 1 and 2 requests //as it can execute in the context of either a single or //multiple services. //if (xmlRequest.IsMultipleRequests) { // TraceWriter.WriteTrace(_managementChannel, "Caller requested multiple service execution in single request", Resources.TraceMessageType_Message); // dynamic results = new UnlimitedObject(); // foreach (dynamic request in xmlRequest.Requests) { // dynamic result = new DynamicServicesInvoker(_dsfChannel, _managementChannel).Invoke(resourceDirectory, request); // if (result.HasError) { // return result; // } // results.AddResponse(result); // } // transactionScope.Complete(); // return results; //} #endregion DynamicService s = service.First(); result = Invoke(s, xmlRequest, dataListId, out errors); if(result == GlobalConstants.NullDataListID) { allErrors.AddError("Failed to invoke service"); } if(!ClientCompiler.HasErrors(result)) { transactionScope.Complete(); } //The service exists so invoke the service which runs all actions defined for the service //Return the response //return xmlResponses; } } //Occurs when an operation is attempted on a rolled back transaction catch(TransactionAbortedException abortEx) { dseException = abortEx; } //This exception is thrown when an action is attempted on a transaction that is in doubt. //A transaction is in doubt when the state of the transaction cannot be determined. //Specifically, the final outcome of the transaction, whether it commits or aborts, is never known for this transaction. catch(TransactionInDoubtException inDoubtEx) { dseException = inDoubtEx; } //Thrown when a resource manager cannot communicate with the transaction manager. catch(TransactionManagerCommunicationException transactionManagerEx) { dseException = transactionManagerEx; } //Thrown when a promotion fails catch(TransactionPromotionException promotionException) { dseException = promotionException; } catch(TransactionException transactionEx) { dseException = transactionEx; } if(dseException != null) { TraceWriter.WriteTrace(_managementChannel, string.Format("Service Execution Failed With Error\r\n{0}", new UnlimitedObject(dseException).XmlString), Resources.TraceMessageType_Error); } // set error variable errors = allErrors; if(errors.HasErrors()) { DispatchDebugState(xmlRequest, dataListId, allErrors); } return result; #endregion }