You've already forked linux-packaging-mono
							
							
		
			
				
	
	
		
			1282 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			1282 lines
		
	
	
		
			54 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| //----------------------------------------------------------------
 | |
| // Copyright (c) Microsoft Corporation.  All rights reserved.
 | |
| //----------------------------------------------------------------
 | |
| 
 | |
| namespace System.ServiceModel.Activities
 | |
| {
 | |
|     using System.Activities;
 | |
|     using System.Activities.Debugger;
 | |
|     using System.Activities.DynamicUpdate;
 | |
|     using System.Activities.XamlIntegration;
 | |
|     using System.Activities.Statements;
 | |
|     using System.Activities.Validation;
 | |
|     using System.Collections;
 | |
|     using System.Collections.Generic;
 | |
|     using System.Collections.ObjectModel;
 | |
|     using System.ComponentModel;
 | |
|     using System.Reflection;
 | |
|     using System.Runtime;
 | |
|     using System.Runtime.Collections;
 | |
|     using System.ServiceModel.Activities.Description;
 | |
|     using System.ServiceModel.Description;
 | |
|     using System.ServiceModel.XamlIntegration;
 | |
|     using System.Text;
 | |
|     using System.Windows.Markup;
 | |
|     using System.Xaml;
 | |
|     using System.Xml;
 | |
|     using System.Xml.Linq;
 | |
| 
 | |
|     [ContentProperty("Body")]
 | |
|     public class WorkflowService : IDebuggableWorkflowTree 
 | |
|     {
 | |
|         Collection<Endpoint> endpoints;
 | |
|         Collection<Type> implementedContracts;
 | |
|         NullableKeyDictionary<WorkflowIdentity, DynamicUpdateMap> updateMaps;
 | |
| 
 | |
|         IDictionary<XName, ContractDescription> cachedInferredContracts;
 | |
|         IDictionary<XName, Collection<CorrelationQuery>> correlationQueryByContract;
 | |
|         IDictionary<ContractAndOperationNameTuple, OperationInfo> keyedByNameOperationInfo;
 | |
| 
 | |
|         IList<Receive> knownServiceActivities;
 | |
|         HashSet<ReceiveAndReplyTuple> receiveAndReplyPairs;
 | |
|         ServiceDescription serviceDescription;
 | |
| 
 | |
|         XName inferedServiceName;
 | |
|         
 | |
|         public WorkflowService()
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         [DefaultValue(null)]
 | |
|         public Activity Body
 | |
|         {
 | |
|             get;
 | |
|             set;
 | |
|         }
 | |
| 
 | |
|         [Fx.Tag.KnownXamlExternal]
 | |
|         [DefaultValue(null)]
 | |
|         [TypeConverter(typeof(ServiceXNameTypeConverter))]
 | |
|         public XName Name
 | |
|         {
 | |
|             get;
 | |
|             set;
 | |
|         }
 | |
| 
 | |
|         [DefaultValue(null)]
 | |
|         public string ConfigurationName
 | |
|         {
 | |
|             get;
 | |
|             set;
 | |
|         }
 | |
| 
 | |
|         [DefaultValue(false)]
 | |
|         public bool AllowBufferedReceive
 | |
|         {
 | |
|             get;
 | |
|             set;
 | |
|         }
 | |
| 
 | |
|         public Collection<Endpoint> Endpoints
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (this.endpoints == null)
 | |
|                 {
 | |
|                     this.endpoints = new Collection<Endpoint>();
 | |
|                 }
 | |
|                 return this.endpoints;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [Fx.Tag.KnownXamlExternal]
 | |
|         [DefaultValue(null)]
 | |
|         public WorkflowIdentity DefinitionIdentity
 | |
|         {
 | |
|             get;
 | |
|             set;
 | |
|         }
 | |
| 
 | |
| 
 | |
|         public Collection<Type> ImplementedContracts
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (this.implementedContracts == null)
 | |
|                 {
 | |
|                     this.implementedContracts = new Collection<Type>();
 | |
|                 }
 | |
|                 return this.implementedContracts;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public IDictionary<WorkflowIdentity, DynamicUpdateMap> UpdateMaps
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (this.updateMaps == null)
 | |
|                 {
 | |
|                     this.updateMaps = new NullableKeyDictionary<WorkflowIdentity, DynamicUpdateMap>();
 | |
|                 }
 | |
|                 return this.updateMaps;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal bool HasImplementedContracts
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 return this.implementedContracts != null && this.implementedContracts.Count > 0;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [DefaultValue(null)]
 | |
|         internal Dictionary<OperationIdentifier, OperationProperty> OperationProperties
 | |
|         {
 | |
|             get;
 | |
|             set;
 | |
|         }
 | |
|        
 | |
|         IDictionary<ContractAndOperationNameTuple, OperationInfo> OperationsInfo
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (this.keyedByNameOperationInfo == null)
 | |
|                 {
 | |
|                     GetContractDescriptions();
 | |
|                 }
 | |
|                 return this.keyedByNameOperationInfo;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal XName InternalName
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 if (this.Name != null)
 | |
|                 {
 | |
|                     return this.Name;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     if (this.inferedServiceName == null)
 | |
|                     {
 | |
|                         Fx.Assert(this.Body != null, "Body cannot be null!");
 | |
| 
 | |
|                         if (this.Body.DisplayName.Length == 0)
 | |
|                         {
 | |
|                             throw FxTrace.Exception.AsError(new InvalidOperationException(SR.MissingDisplayNameInRootActivity));
 | |
|                         }
 | |
| 
 | |
|                         this.inferedServiceName = XName.Get(XmlConvert.EncodeLocalName(this.Body.DisplayName));
 | |
|                     }
 | |
|                     return this.inferedServiceName;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal IDictionary<XName, Collection<CorrelationQuery>> CorrelationQueries
 | |
|         {
 | |
|             get
 | |
|             {
 | |
|                 Fx.Assert(this.cachedInferredContracts != null, "Must infer contract first!");
 | |
|                 return this.correlationQueryByContract;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public Activity GetWorkflowRoot()
 | |
|         {
 | |
|             return this.Body;
 | |
|         }
 | |
| 
 | |
|         internal ServiceDescription GetEmptyServiceDescription()
 | |
|         {
 | |
|             if (this.serviceDescription == null)
 | |
|             {
 | |
|                 WalkActivityTree();
 | |
| 
 | |
|                 ServiceDescription result = new ServiceDescription
 | |
|                 {
 | |
|                     Name = this.InternalName.LocalName,
 | |
|                     Namespace = string.IsNullOrEmpty(this.InternalName.NamespaceName) ? NamingHelper.DefaultNamespace : this.InternalName.NamespaceName,
 | |
|                     ConfigurationName = this.ConfigurationName ?? this.InternalName.LocalName
 | |
|                 };                
 | |
|                 this.serviceDescription = result;
 | |
|             }
 | |
|             return this.serviceDescription;
 | |
|         }
 | |
| 
 | |
|         static void AddAdditionalConstraint(ValidationSettings workflowServiceSettings, Type constraintType, Constraint constraint)
 | |
|         {
 | |
|             IList<Constraint> constraintList;
 | |
|             if (workflowServiceSettings.AdditionalConstraints.TryGetValue(constraintType, out constraintList))
 | |
|             {
 | |
|                 constraintList.Add(constraint);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 constraintList = new List<Constraint>(1)
 | |
|                     {
 | |
|                         constraint,
 | |
|                     };
 | |
|                 workflowServiceSettings.AdditionalConstraints.Add(constraintType, constraintList);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public ValidationResults Validate(ValidationSettings settings)
 | |
|         {
 | |
|             Collection<ValidationError> errors = new Collection<ValidationError>();
 | |
|             ValidationSettings workflowServiceSettings = this.CopyValidationSettions(settings);
 | |
| 
 | |
|             if (this.HasImplementedContracts)
 | |
|             {
 | |
|                 this.OperationProperties = CreateOperationProperties(errors);
 | |
| 
 | |
|                 // Add additional constraints
 | |
|                 AddAdditionalConstraint(workflowServiceSettings, typeof(Receive), GetContractFirstValidationReceiveConstraints());
 | |
|                 AddAdditionalConstraint(workflowServiceSettings, typeof(SendReply), GetContractFirstValidationSendReplyConstraints());
 | |
|             }
 | |
| 
 | |
|             ValidationResults results = null;
 | |
|             if (this.Body != null)
 | |
|             {
 | |
|                 results = ActivityValidationServices.Validate(this.Body, workflowServiceSettings);
 | |
| 
 | |
|                 if (!this.HasImplementedContracts)
 | |
|                 {
 | |
|                     return results;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // If the user specified implemented contract, then we need to add the errors into the error collection
 | |
|                     foreach (ValidationError validationError in results.Errors)
 | |
|                     {
 | |
|                         errors.Add(validationError);
 | |
|                     }
 | |
| 
 | |
|                     foreach (ValidationError validationWarning in results.Warnings)
 | |
|                     {
 | |
|                         errors.Add(validationWarning);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (this.HasImplementedContracts)
 | |
|             {
 | |
|                 this.AfterValidation(errors);
 | |
|             }
 | |
| 
 | |
|             return new ValidationResults(errors);
 | |
|         }
 | |
| 
 | |
|         bool IsContractValid(ContractDescription contractDescription, Collection<ValidationError> validationError)
 | |
|         {
 | |
|             bool isValid = true; 
 | |
|             if (contractDescription.IsDuplex())
 | |
|             {
 | |
|                 validationError.Add(new ValidationError(SR.DuplexContractsNotSupported));
 | |
|                 isValid = false;
 | |
|             }
 | |
|             
 | |
|             return isValid; 
 | |
|         }
 | |
| 
 | |
|         ValidationSettings CopyValidationSettions(ValidationSettings source)
 | |
|         {
 | |
|             if ( source == null )
 | |
|             {
 | |
|                 return new ValidationSettings();
 | |
|             }
 | |
| 
 | |
|             ValidationSettings clonedSettings = new ValidationSettings
 | |
|             {
 | |
|                 OnlyUseAdditionalConstraints = source.OnlyUseAdditionalConstraints,
 | |
|                 SingleLevel = source.SingleLevel,
 | |
|                 SkipValidatingRootConfiguration = source.SkipValidatingRootConfiguration,
 | |
|                 PrepareForRuntime = source.PrepareForRuntime,
 | |
|                 Environment = source.Environment,
 | |
|                 // Retain the same cancellation token. Otherwise we can't cancel the validation of WorkflowService objects
 | |
|                 // which can make the designer unreponsive if the validation takes a long time.
 | |
|                 CancellationToken = source.CancellationToken
 | |
| 
 | |
|             };
 | |
| 
 | |
|             foreach (KeyValuePair<Type, IList<Constraint>> constrants in source.AdditionalConstraints)
 | |
|             {
 | |
|                 if (constrants.Key != null && constrants.Value != null)
 | |
|                 {
 | |
|                     clonedSettings.AdditionalConstraints.Add(constrants.Key, new List<Constraint>(constrants.Value));
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return clonedSettings;
 | |
|         }
 | |
| 
 | |
|         void AfterValidation(Collection<ValidationError> errors)
 | |
|         {
 | |
|             if (this.HasImplementedContracts)
 | |
|             {
 | |
|                 Dictionary<OperationIdentifier, OperationProperty> operationProperties = this.OperationProperties;
 | |
|                 if (operationProperties != null)
 | |
|                 {
 | |
|                     foreach (OperationProperty property in operationProperties.Values)
 | |
|                     {
 | |
|                         Fx.Assert(property.Operation != null, "OperationProperty.Operation should not be null!");
 | |
| 
 | |
|                         if (property.ImplementingReceives.Count < 1)
 | |
|                         {
 | |
|                             errors.Add(new ValidationError(SR.OperationIsNotImplemented(property.Operation.Name, property.Operation.DeclaringContract.Name), true));
 | |
|                         }
 | |
|                         else if (!property.Operation.IsOneWay)
 | |
|                         {
 | |
|                             foreach (Receive recv in property.ImplementingReceives)
 | |
|                             {
 | |
|                                 if (!property.ImplementingSendRepliesRequests.Contains(recv))
 | |
|                                 {
 | |
|                                     // passing the receive activity without a matching SendReply as the SourceDetail
 | |
|                                     errors.Add(new ValidationError(SR.TwoWayIsImplementedAsOneWay(property.Operation.Name, property.Operation.DeclaringContract.Name), true, string.Empty, recv));
 | |
|                                 }
 | |
|                             }                            
 | |
|                         }                        
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         Dictionary<OperationIdentifier, OperationProperty> CreateOperationProperties(Collection<ValidationError> validationErrors)
 | |
|         {
 | |
|             Dictionary<OperationIdentifier, OperationProperty> operationProperties = null;
 | |
| 
 | |
|             if (this.HasImplementedContracts)
 | |
|             {
 | |
|                 operationProperties = new Dictionary<OperationIdentifier, OperationProperty>();
 | |
|                 foreach (Type contractType in this.ImplementedContracts)
 | |
|                 {
 | |
|                     ContractDescription contract = null;
 | |
|                     try
 | |
|                     {
 | |
|                         contract = ContractDescription.GetContract(contractType);
 | |
|                                                 
 | |
|                         if (contract != null)
 | |
|                         {
 | |
|                             if (this.IsContractValid(contract, validationErrors))
 | |
|                             {
 | |
|                                 foreach (OperationDescription operation in contract.Operations)
 | |
|                                 {
 | |
|                                     OperationIdentifier id = new OperationIdentifier(operation.DeclaringContract.Name, operation.DeclaringContract.Namespace, operation.Name);
 | |
|                                     if (operationProperties.ContainsKey(id))
 | |
|                                     {
 | |
|                                         validationErrors.Add(new ValidationError(SR.DuplicatedContract(operation.DeclaringContract.Name, operation.Name), true));
 | |
|                                     }
 | |
|                                     else
 | |
|                                     {
 | |
|                                         operationProperties.Add(id, new OperationProperty(operation));
 | |
|                                     }
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     catch (Exception exception)
 | |
|                     {
 | |
|                         if (Fx.IsFatal(exception))
 | |
|                         {
 | |
|                             throw;
 | |
|                         }
 | |
| 
 | |
|                         validationErrors.Add(new ValidationError(exception.Message));
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return operationProperties;
 | |
|         }
 | |
|         
 | |
|         public virtual IDictionary<XName, ContractDescription> GetContractDescriptions()
 | |
|         {
 | |
|             if (this.cachedInferredContracts == null)
 | |
|             {
 | |
|                 WalkActivityTree();
 | |
| 
 | |
|                 Fx.Assert(this.knownServiceActivities != null && this.receiveAndReplyPairs != null, "Failed to walk the activity tree!");
 | |
|                 this.correlationQueryByContract = new Dictionary<XName, Collection<CorrelationQuery>>();
 | |
| 
 | |
|                 // Contract inference
 | |
|                 IDictionary<XName, ContractDescription> inferredContracts = new Dictionary<XName, ContractDescription>();
 | |
|                 this.keyedByNameOperationInfo = new Dictionary<ContractAndOperationNameTuple, OperationInfo>();
 | |
| 
 | |
|                 foreach (Receive receive in this.knownServiceActivities)
 | |
|                 {
 | |
|                     XName contractXName = FixServiceContractName(receive.ServiceContractName);
 | |
|                     ContractAndOperationNameTuple tuple = new ContractAndOperationNameTuple(contractXName, receive.OperationName);
 | |
| 
 | |
|                     OperationInfo operationInfo;
 | |
|                     if (this.keyedByNameOperationInfo.TryGetValue(tuple, out operationInfo))
 | |
|                     {
 | |
|                         // All Receives with same ServiceContractName and OperationName need to be validated
 | |
|                         ContractValidationHelper.ValidateReceiveWithReceive(receive, operationInfo.Receive);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         // Note that activities in keyedByNameOperationInfo are keyed by
 | |
|                         // ServiceContractName and OperationName tuple. So we won't run into the case where
 | |
|                         // two opertions have the same OperationName.
 | |
| 
 | |
|                         ContractDescription contract;
 | |
|                         if (!inferredContracts.TryGetValue(contractXName, out contract))
 | |
|                         {
 | |
|                             // Infer Name, Namespace
 | |
|                             contract = new ContractDescription(contractXName.LocalName, contractXName.NamespaceName);
 | |
| 
 | |
|                             // We use ServiceContractName.LocalName to bind contract with config
 | |
|                             contract.ConfigurationName = contractXName.LocalName;
 | |
| 
 | |
|                             // We do NOT infer ContractDescription.ProtectionLevel
 | |
| 
 | |
|                             inferredContracts.Add(contractXName, contract);
 | |
|                         }
 | |
| 
 | |
|                         OperationDescription operation = ContractInferenceHelper.CreateOperationDescription(receive, contract);
 | |
|                         contract.Operations.Add(operation);
 | |
| 
 | |
|                         operationInfo = new OperationInfo(receive, operation);
 | |
|                         this.keyedByNameOperationInfo.Add(tuple, operationInfo);
 | |
|                     }
 | |
| 
 | |
|                     CorrectOutMessageForOperationWithFault(receive, operationInfo);
 | |
| 
 | |
|                     ContractInferenceHelper.UpdateIsOneWayFlag(receive, operationInfo.OperationDescription);
 | |
| 
 | |
|                     // FaultTypes and KnownTypes need to be collected from all Receive activities
 | |
|                     ContractInferenceHelper.AddFaultDescription(receive, operationInfo.OperationDescription);
 | |
|                     ContractInferenceHelper.AddKnownTypesToOperation(receive, operationInfo.OperationDescription);
 | |
| 
 | |
|                     // WorkflowFormatterBehavior should have reference to all the Receive activities
 | |
|                     ContractInferenceHelper.AddReceiveToFormatterBehavior(receive, operationInfo.OperationDescription);
 | |
| 
 | |
|                     Collection<CorrelationQuery> correlationQueries = null;
 | |
| 
 | |
|                     // Collect CorrelationQuery from Receive
 | |
|                     if (receive.HasCorrelatesOn || receive.HasCorrelationInitializers)
 | |
|                     {
 | |
|                         MessageQuerySet select = receive.HasCorrelatesOn ? receive.CorrelatesOn : null;
 | |
|                         CorrelationQuery correlationQuery = ContractInferenceHelper.CreateServerCorrelationQuery(select,
 | |
|                             receive.CorrelationInitializers, operationInfo.OperationDescription, false);
 | |
|                         CollectCorrelationQuery(ref correlationQueries, contractXName, correlationQuery);
 | |
|                     }
 | |
| 
 | |
|                     // Find all known Receive-Reply pair in the activity tree. Remove them from this.receiveAndReplyPairs
 | |
|                     // Also collect CorrelationQuery from following replies
 | |
|                     if (receive.HasReply)
 | |
|                     {
 | |
|                         foreach (SendReply reply in receive.FollowingReplies)
 | |
|                         {
 | |
|                             ReceiveAndReplyTuple pair = new ReceiveAndReplyTuple(receive, reply);
 | |
|                             this.receiveAndReplyPairs.Remove(pair);
 | |
| 
 | |
|                             CollectCorrelationQueryFromReply(ref correlationQueries, contractXName,
 | |
|                                 reply, operationInfo.OperationDescription);
 | |
| 
 | |
|                             reply.SetContractName(contractXName);
 | |
|                         }
 | |
|                     }
 | |
|                     if (receive.HasFault)
 | |
|                     {
 | |
|                         foreach (SendReply fault in receive.FollowingFaults)
 | |
|                         {
 | |
|                             ReceiveAndReplyTuple pair = new ReceiveAndReplyTuple(receive, fault);
 | |
|                             this.receiveAndReplyPairs.Remove(pair);
 | |
| 
 | |
|                             CollectCorrelationQueryFromReply(ref correlationQueries, contractXName,
 | |
|                                 fault, operationInfo.OperationDescription);
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     // Have to do this here otherwise message/fault formatters 
 | |
|                     // non-WorkflowServiceHost case won't be set. Cannot do this
 | |
|                     // during CacheMetadata time because activity order in which 
 | |
|                     // CacheMetadata calls are made doesn't yield the correct result.
 | |
|                     // Not possible to do it at runtime either because the ToReply and
 | |
|                     // ToRequest activities that use the formatters do not have access
 | |
|                     // to related OperationDescription. Note: non-WorkflowServiceHosts will
 | |
|                     // need to call GetContractDescriptions() to get these default formatters
 | |
|                     // wired up.
 | |
|                     receive.SetDefaultFormatters(operationInfo.OperationDescription);
 | |
| 
 | |
|                 }
 | |
| 
 | |
|                 // Check for Receive referenced by SendReply but no longer in the activity tree
 | |
|                 if (this.receiveAndReplyPairs.Count != 0)
 | |
|                 {
 | |
|                     throw FxTrace.Exception.AsError(new ValidationException(SR.DanglingReceive));
 | |
|                 }
 | |
| 
 | |
|                 // Print out tracing information
 | |
|                 if (TD.InferredContractDescriptionIsEnabled())
 | |
|                 {
 | |
|                     foreach (ContractDescription contract in inferredContracts.Values)
 | |
|                     {
 | |
|                         TD.InferredContractDescription(contract.Name, contract.Namespace);
 | |
| 
 | |
|                         if (TD.InferredOperationDescriptionIsEnabled())
 | |
|                         {
 | |
|                             foreach (OperationDescription operation in contract.Operations)
 | |
|                             {
 | |
|                                 TD.InferredOperationDescription(operation.Name, contract.Name, operation.IsOneWay.ToString());
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 this.cachedInferredContracts = inferredContracts;
 | |
|             }
 | |
| 
 | |
|             return this.cachedInferredContracts;
 | |
|         }
 | |
| 
 | |
|         internal void ValidateForVersioning(WorkflowService baseWorkflowService)
 | |
|         {
 | |
|             if (this.knownServiceActivities == null)
 | |
|             {
 | |
|                 WalkActivityTree();
 | |
|             }
 | |
|             
 | |
|             foreach (Receive receive in this.knownServiceActivities)
 | |
|             {
 | |
|                 XName contractXName = FixServiceContractName(receive.ServiceContractName);
 | |
|                 ContractAndOperationNameTuple tuple = new ContractAndOperationNameTuple(contractXName, receive.OperationName);
 | |
| 
 | |
|                 OperationInfo operationInfo;
 | |
|                 if (baseWorkflowService.OperationsInfo.TryGetValue(tuple, out operationInfo))
 | |
|                 {
 | |
|                     // All Receives with same ServiceContractName and OperationName need to be validated
 | |
|                     ContractValidationHelper.ValidateReceiveWithReceive(receive, operationInfo.Receive);
 | |
|                     ContractInferenceHelper.AddReceiveToFormatterBehavior(receive, operationInfo.OperationDescription);
 | |
|                     ContractInferenceHelper.UpdateIsOneWayFlag(receive, operationInfo.OperationDescription);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     throw FxTrace.Exception.AsError(new ValidationException(SR.OperationNotFound(contractXName, receive.OperationName)));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal void DetachFromVersioning(WorkflowService baseWorkflowService)
 | |
|         {
 | |
|             if (this.knownServiceActivities == null)
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             foreach (Receive receive in this.knownServiceActivities)
 | |
|             {
 | |
|                 XName contractXName = FixServiceContractName(receive.ServiceContractName);
 | |
|                 ContractAndOperationNameTuple tuple = new ContractAndOperationNameTuple(contractXName, receive.OperationName);
 | |
| 
 | |
|                 OperationInfo operationInfo;
 | |
|                 if (baseWorkflowService.OperationsInfo.TryGetValue(tuple, out operationInfo))
 | |
|                 {   
 | |
|                     ContractInferenceHelper.RemoveReceiveFromFormatterBehavior(receive, operationInfo.OperationDescription);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void WalkActivityTree()
 | |
|         {
 | |
|             if (this.knownServiceActivities != null)
 | |
|             {
 | |
|                 // We return if we have already walked the activity tree
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             if (this.Body == null)
 | |
|             {
 | |
|                 throw FxTrace.Exception.AsError(new ValidationException(SR.MissingBodyInWorkflowService));
 | |
|             }
 | |
| 
 | |
|             // Validate the activity tree
 | |
|             ValidationResults validationResults = null;
 | |
|             StringBuilder exceptionMessage = new StringBuilder();
 | |
|             bool doesErrorExist = false; 
 | |
|             try
 | |
|             {
 | |
|                 if (this.HasImplementedContracts)
 | |
|                 {
 | |
|                     validationResults = this.Validate(new ValidationSettings() { PrepareForRuntime = true, });
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     WorkflowInspectionServices.CacheMetadata(this.Body);
 | |
|                 }                
 | |
|             }
 | |
|             catch (InvalidWorkflowException e)
 | |
|             {
 | |
|                 doesErrorExist = true; 
 | |
|                 exceptionMessage.AppendLine(e.Message);
 | |
|             }
 | |
| 
 | |
|             if (validationResults != null)
 | |
|             {
 | |
|                 if (validationResults.Errors != null && validationResults.Errors.Count > 0)
 | |
|                 {
 | |
|                     doesErrorExist = true; 
 | |
|                     foreach (ValidationError error in validationResults.Errors)
 | |
|                     {
 | |
|                         exceptionMessage.AppendLine(error.Message);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (doesErrorExist)
 | |
|             {
 | |
|                 throw FxTrace.Exception.AsError(new InvalidWorkflowException(exceptionMessage.ToString()));
 | |
|             }
 | |
| 
 | |
|             this.knownServiceActivities = new List<Receive>();
 | |
|             this.receiveAndReplyPairs = new HashSet<ReceiveAndReplyTuple>();
 | |
| 
 | |
|             // Now let us walk the tree here
 | |
|             Queue<QueueItem> activities = new Queue<QueueItem>();
 | |
|             // The root activity is never "in" a TransactedReceiveScope
 | |
|             activities.Enqueue(new QueueItem(this.Body, null, null));
 | |
|             while (activities.Count > 0)
 | |
|             {
 | |
|                 QueueItem queueItem = activities.Dequeue();
 | |
|                 Fx.Assert(queueItem != null, "Queue item cannot be null");
 | |
| 
 | |
|                 Activity activity = queueItem.Activity;
 | |
|                 Fx.Assert(queueItem.Activity != null, "Queue item's Activity cannot be null");
 | |
| 
 | |
|                 Activity parentReceiveScope = queueItem.ParentReceiveScope;
 | |
|                 Activity rootTransactedReceiveScope = queueItem.RootTransactedReceiveScope;
 | |
| 
 | |
|                 if (activity is Receive)  // First, let's see if this is a Receive activity
 | |
|                 {
 | |
|                     Receive receive = (Receive)activity;
 | |
| 
 | |
|                     if (rootTransactedReceiveScope != null)
 | |
|                     {
 | |
|                         receive.InternalReceive.AdditionalData.IsInsideTransactedReceiveScope = true;
 | |
|                         Fx.Assert(parentReceiveScope != null, "Internal error.. TransactedReceiveScope should be valid here");
 | |
|                         if (IsFirstTransactedReceive(receive, parentReceiveScope, rootTransactedReceiveScope))
 | |
|                         {
 | |
|                             receive.InternalReceive.AdditionalData.IsFirstReceiveOfTransactedReceiveScopeTree = true;
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     this.knownServiceActivities.Add(receive);
 | |
|                 }
 | |
|                 else if (activity is SendReply)  // Let's see if this is a SendReply
 | |
|                 {
 | |
|                     SendReply sendReply = (SendReply)activity;
 | |
| 
 | |
|                     Receive pairedReceive = sendReply.Request;
 | |
|                     Fx.Assert(pairedReceive != null, "SendReply must point to a Receive!");
 | |
| 
 | |
|                     if (sendReply.InternalContent.IsFault)
 | |
|                     {
 | |
|                         pairedReceive.FollowingFaults.Add(sendReply);
 | |
|                     }
 | |
|                     else
 | |
|                     {
 | |
|                         if (pairedReceive.HasReply)
 | |
|                         {
 | |
|                             SendReply followingReply = pairedReceive.FollowingReplies[0];
 | |
|                             ContractValidationHelper.ValidateSendReplyWithSendReply(followingReply, sendReply);
 | |
|                         }
 | |
| 
 | |
|                         pairedReceive.FollowingReplies.Add(sendReply);
 | |
|                     }
 | |
| 
 | |
|                     ReceiveAndReplyTuple tuple = new ReceiveAndReplyTuple(pairedReceive, sendReply);
 | |
|                     this.receiveAndReplyPairs.Add(tuple);
 | |
|                 }
 | |
| 
 | |
|                 // Enqueue child activities and delegates
 | |
|                 if (activity is TransactedReceiveScope)
 | |
|                 {
 | |
|                     parentReceiveScope = activity;
 | |
|                     if (rootTransactedReceiveScope == null)
 | |
|                     {
 | |
|                         rootTransactedReceiveScope = parentReceiveScope;
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 foreach (Activity childActivity in WorkflowInspectionServices.GetActivities(activity))
 | |
|                 {
 | |
|                     QueueItem queueData = new QueueItem(childActivity, parentReceiveScope, rootTransactedReceiveScope);
 | |
|                     activities.Enqueue(queueData);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         XName FixServiceContractName(XName serviceContractName)
 | |
|         {
 | |
|             // By default, we use WorkflowService.Name as ServiceContractName
 | |
|             XName contractXName = serviceContractName ?? this.InternalName;
 | |
| 
 | |
|             ContractInferenceHelper.ProvideDefaultNamespace(ref contractXName);
 | |
| 
 | |
|             return contractXName;
 | |
|         }
 | |
| 
 | |
|         static void CorrectOutMessageForOperationWithFault(Receive receive, OperationInfo operationInfo)
 | |
|         {
 | |
|             Fx.Assert(receive != null && operationInfo != null, "Argument cannot be null!");
 | |
| 
 | |
|             Receive prevReceive = operationInfo.Receive;
 | |
|             if (receive != prevReceive && receive.HasReply &&
 | |
|                 !prevReceive.HasReply && prevReceive.HasFault)
 | |
|             {
 | |
|                 ContractInferenceHelper.CorrectOutMessageForOperation(receive, operationInfo.OperationDescription);
 | |
|                 operationInfo.Receive = receive;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         void CollectCorrelationQuery(ref Collection<CorrelationQuery> queries, XName serviceContractName, CorrelationQuery correlationQuery)
 | |
|         {
 | |
|             Fx.Assert(serviceContractName != null, "Argument cannot be null!");
 | |
| 
 | |
|             if (correlationQuery == null)
 | |
|             {
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             if (queries == null && !this.correlationQueryByContract.TryGetValue(serviceContractName, out queries))
 | |
|             {
 | |
|                 queries = new Collection<CorrelationQuery>();
 | |
|                 this.correlationQueryByContract.Add(serviceContractName, queries);
 | |
|             }
 | |
| 
 | |
|             queries.Add(correlationQuery);
 | |
|         }
 | |
| 
 | |
|         void CollectCorrelationQueryFromReply(ref Collection<CorrelationQuery> correlationQueries, XName serviceContractName,
 | |
|             Activity reply, OperationDescription operation)
 | |
|         {
 | |
|             SendReply sendReply = reply as SendReply;
 | |
|             if (sendReply != null)
 | |
|             {
 | |
|                 CorrelationQuery correlationQuery = ContractInferenceHelper.CreateServerCorrelationQuery(
 | |
|                     null, sendReply.CorrelationInitializers, operation, true);
 | |
|                 CollectCorrelationQuery(ref correlationQueries, serviceContractName, correlationQuery);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         internal void ResetServiceDescription()
 | |
|         {
 | |
|             this.serviceDescription = null;
 | |
|             this.cachedInferredContracts = null;
 | |
|         }
 | |
| 
 | |
|         bool IsFirstTransactedReceive(Receive request, Activity parent, Activity root)
 | |
|         {
 | |
|             Receive receive = null;
 | |
|             if (parent != null && root != null)
 | |
|             {
 | |
|                 TransactedReceiveScope rootTRS = root as TransactedReceiveScope;
 | |
|                 if (rootTRS != null)
 | |
|                 {
 | |
|                     receive = rootTRS.Request;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return (parent == root && receive == request);
 | |
|         }
 | |
|         
 | |
|         Constraint GetContractFirstValidationReceiveConstraints()
 | |
|         {
 | |
|             DelegateInArgument<Receive> element = new DelegateInArgument<Receive> { Name = "ReceiveElement" };
 | |
|             DelegateInArgument<ValidationContext> validationContext = new DelegateInArgument<ValidationContext> { Name = "validationContext" };
 | |
|             Variable<IEnumerable<Activity>> parentChainVar = new Variable<IEnumerable<Activity>>("parentChainVar");
 | |
|             return new Constraint<Receive>
 | |
|             {
 | |
|                 Body = new ActivityAction<Receive, ValidationContext>
 | |
|                 {
 | |
|                     Argument1 = element,
 | |
|                     Argument2 = validationContext,
 | |
|                     Handler = new Sequence
 | |
|                     {
 | |
|                         Variables = { parentChainVar },
 | |
|                         Activities =
 | |
|                         {
 | |
|                             new GetParentChain { ValidationContext = validationContext, Result = parentChainVar },
 | |
|                             new ValidateReceiveContract()
 | |
|                             {
 | |
|                                 DisplayName = "ValidateReceiveContract",
 | |
|                                 ReceiveActivity = element,
 | |
|                                 WorkflowService = new InArgument<WorkflowService>()
 | |
|                                 {
 | |
|                                     Expression = new GetWorkflowSerivce(this)
 | |
|                                 },
 | |
|                                 ParentChain = parentChainVar,
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         Constraint GetContractFirstValidationSendReplyConstraints()
 | |
|         {
 | |
|             DelegateInArgument<SendReply> element = new DelegateInArgument<SendReply> { Name = "ReceiveElement" };
 | |
|             DelegateInArgument<ValidationContext> validationContext = new DelegateInArgument<ValidationContext> { Name = "validationContext" };
 | |
| 
 | |
|             return new Constraint<SendReply>
 | |
|             {
 | |
|                 Body = new ActivityAction<SendReply, ValidationContext>
 | |
|                 {
 | |
|                     Argument1 = element,
 | |
|                     Argument2 = validationContext,
 | |
|                     Handler = new Sequence
 | |
|                     {
 | |
|                         Activities =
 | |
|                         {
 | |
|                             new ValidateSendReplyContract()
 | |
|                             {
 | |
|                                 DisplayName = "ValidateReceiveContract",
 | |
|                                 ReceiveActivity = element,
 | |
|                                 WorkflowSerivce = new InArgument<WorkflowService>()
 | |
|                                 {
 | |
|                                     Expression = new GetWorkflowSerivce(this)
 | |
|                                 },
 | |
|                                 ValidationContext = validationContext
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             };
 | |
|         }
 | |
| 
 | |
|         struct ContractAndOperationNameTuple : IEquatable<ContractAndOperationNameTuple>
 | |
|         {
 | |
|             XName serviceContractXName;
 | |
|             string operationName;
 | |
| 
 | |
|             public ContractAndOperationNameTuple(XName serviceContractXName, string operationName)
 | |
|             {
 | |
|                 this.serviceContractXName = serviceContractXName;
 | |
|                 this.operationName = operationName;
 | |
|             }
 | |
| 
 | |
|             public bool Equals(ContractAndOperationNameTuple other)
 | |
|             {
 | |
|                 return this.serviceContractXName == other.serviceContractXName && this.operationName == other.operationName; 
 | |
|             }
 | |
| 
 | |
|             public override int GetHashCode()
 | |
|             {
 | |
|                 int hashCode = 0;
 | |
|                 if (this.serviceContractXName != null)
 | |
|                 {
 | |
|                     hashCode ^= this.serviceContractXName.GetHashCode();
 | |
|                 }
 | |
| 
 | |
|                 hashCode ^= this.operationName.GetHashCode();
 | |
| 
 | |
|                 return hashCode;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         struct ReceiveAndReplyTuple : IEquatable<ReceiveAndReplyTuple>
 | |
|         {
 | |
|             Receive receive;
 | |
|             Activity reply;
 | |
| 
 | |
|             public ReceiveAndReplyTuple(Receive receive, SendReply reply)
 | |
|             {
 | |
|                 this.receive = receive;
 | |
|                 this.reply = reply;
 | |
|             }
 | |
| 
 | |
|             public bool Equals(ReceiveAndReplyTuple other)
 | |
|             {
 | |
|                 return this.receive == other.receive && this.reply == other.reply;
 | |
|             }
 | |
| 
 | |
|             public override int GetHashCode()
 | |
|             {
 | |
|                 int hash = 0; 
 | |
|                 if (this.receive != null)
 | |
|                 {
 | |
|                     hash ^= this.receive.GetHashCode();
 | |
|                 }
 | |
| 
 | |
|                 if (this.reply != null)
 | |
|                 {
 | |
|                     hash ^= this.reply.GetHashCode();
 | |
|                 }
 | |
| 
 | |
|                 return hash;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         class OperationInfo
 | |
|         {
 | |
|             Receive receive;
 | |
|             OperationDescription operationDescription;
 | |
| 
 | |
|             public OperationInfo(Receive receive, OperationDescription operationDescription)
 | |
|             {
 | |
|                 this.receive = receive;
 | |
|                 this.operationDescription = operationDescription;
 | |
|             }
 | |
| 
 | |
|             public Receive Receive
 | |
|             {
 | |
|                 get { return this.receive; }
 | |
|                 set { this.receive = value; }
 | |
|             }
 | |
| 
 | |
|             public OperationDescription OperationDescription
 | |
|             {
 | |
|                 get { return this.operationDescription; }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         class QueueItem
 | |
|         {
 | |
|             Activity activity;
 | |
|             Activity parent;
 | |
|             Activity rootTransactedReceiveScope;
 | |
| 
 | |
|             public QueueItem(Activity element, Activity parent, Activity root)
 | |
|             {
 | |
|                 this.activity = element;
 | |
|                 this.parent = parent;
 | |
|                 this.rootTransactedReceiveScope = root; 
 | |
|             }
 | |
| 
 | |
|             public Activity Activity
 | |
|             {
 | |
|                 get { return this.activity; }
 | |
|             }
 | |
| 
 | |
|             public Activity ParentReceiveScope
 | |
|             {
 | |
|                 get { return this.parent; }
 | |
|             }
 | |
| 
 | |
|             public Activity RootTransactedReceiveScope
 | |
|             {
 | |
|                 get { return this.rootTransactedReceiveScope; }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         class GetWorkflowSerivce : CodeActivity<WorkflowService>
 | |
|         {
 | |
|             WorkflowService workflowService; 
 | |
|             public GetWorkflowSerivce(WorkflowService serviceReference)
 | |
|             {
 | |
|                 workflowService = serviceReference;
 | |
|             }
 | |
| 
 | |
|             protected override void CacheMetadata(CodeActivityMetadata metadata)
 | |
|             {
 | |
|                 RuntimeArgument resultArgument = new RuntimeArgument("Result", typeof(WorkflowService), ArgumentDirection.Out);
 | |
|                 metadata.Bind(this.Result, resultArgument);
 | |
| 
 | |
|                 metadata.SetArgumentsCollection(
 | |
|                     new Collection<RuntimeArgument>
 | |
|                 {
 | |
|                     resultArgument
 | |
|                 });
 | |
|             }
 | |
| 
 | |
|             protected override WorkflowService Execute(CodeActivityContext context)
 | |
|             {
 | |
|                 return workflowService;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         class ValidateReceiveContract : NativeActivity
 | |
|         {
 | |
|             public InArgument<Receive> ReceiveActivity
 | |
|             {
 | |
|                 get;
 | |
|                 set;
 | |
|             }
 | |
| 
 | |
|             public InArgument<IEnumerable<Activity>> ParentChain
 | |
|             {
 | |
|                 get;
 | |
|                 set;
 | |
|             }
 | |
| 
 | |
|             public InArgument<WorkflowService> WorkflowService
 | |
|             {
 | |
|                 get;
 | |
|                 set; 
 | |
|             }
 | |
| 
 | |
|             protected override void CacheMetadata(NativeActivityMetadata metadata)
 | |
|             {
 | |
|                 RuntimeArgument receiveActivity = new RuntimeArgument("ReceiveActivity", typeof(Receive), ArgumentDirection.In);
 | |
|                 RuntimeArgument parentChain = new RuntimeArgument("ParentChain", typeof(IEnumerable<Activity>), ArgumentDirection.In);
 | |
|                 RuntimeArgument operationProperties = new RuntimeArgument("OperationProperties", typeof(WorkflowService), ArgumentDirection.In);
 | |
| 
 | |
|                 if (this.ReceiveActivity == null)
 | |
|                 {
 | |
|                     this.ReceiveActivity = new InArgument<Receive>();
 | |
|                 }
 | |
|                 metadata.Bind(this.ReceiveActivity, receiveActivity);
 | |
| 
 | |
|                 if (this.ParentChain == null)
 | |
|                 {
 | |
|                     this.ParentChain = new InArgument<IEnumerable<Activity>>();
 | |
|                 }
 | |
|                 metadata.Bind(this.ParentChain, parentChain);
 | |
| 
 | |
|                 if (this.WorkflowService == null)
 | |
|                 {
 | |
|                     this.WorkflowService = new InArgument<WorkflowService>();
 | |
|                 }
 | |
|                 metadata.Bind(this.WorkflowService, operationProperties);
 | |
| 
 | |
|                 Collection<RuntimeArgument> arguments = new Collection<RuntimeArgument>
 | |
|                 {
 | |
|                     receiveActivity,
 | |
|                     parentChain,
 | |
|                     operationProperties,
 | |
|                 };
 | |
| 
 | |
|                 metadata.SetArgumentsCollection(arguments);
 | |
|             }
 | |
| 
 | |
|             protected override void Execute(NativeActivityContext context)
 | |
|             {
 | |
|                 Receive receiveActivity = this.ReceiveActivity.Get(context);
 | |
|                 Dictionary<OperationIdentifier, OperationProperty> operationProperties;
 | |
| 
 | |
|                 Fx.Assert(receiveActivity != null, "ValidateReceiveContract needs the receive activity to be present");
 | |
| 
 | |
|                 if (string.IsNullOrEmpty(receiveActivity.OperationName))
 | |
|                 {
 | |
|                     Constraint.AddValidationError(context, new ValidationError(SR.MissingOperationName(this.DisplayName)));
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     WorkflowService workflowService = this.WorkflowService.Get(context);
 | |
|                     operationProperties = workflowService.OperationProperties;
 | |
|                     XName serviceName = workflowService.FixServiceContractName(receiveActivity.ServiceContractName);
 | |
|                     // We only do contract first validation if there are contract specified
 | |
|                     if (operationProperties != null)
 | |
|                     {
 | |
|                         string contractName = serviceName.LocalName;
 | |
|                         string contractNamespace = string.IsNullOrEmpty(serviceName.NamespaceName) ?
 | |
|                             NamingHelper.DefaultNamespace : serviceName.NamespaceName;
 | |
|                         string operationXmlName = NamingHelper.XmlName(receiveActivity.OperationName);
 | |
| 
 | |
|                         OperationProperty property;
 | |
|                         OperationIdentifier operationId = new OperationIdentifier(contractName, contractNamespace, operationXmlName);
 | |
|                         if (operationProperties.TryGetValue(operationId, out property))
 | |
|                         {
 | |
|                             property.ImplementingReceives.Add(receiveActivity);
 | |
|                             Fx.Assert(property.Operation != null, "OperationProperty.Operation should not be null!");                            
 | |
|                             ValidateContract(context, receiveActivity, property.Operation);
 | |
|                         }
 | |
|                         else
 | |
|                         {
 | |
|                             // It is OK to add a new contract, but we do not allow adding a new operation to a specified contract.
 | |
|                             foreach (OperationIdentifier id in operationProperties.Keys)
 | |
|                             {
 | |
|                                 if (contractName == id.ContractName && contractNamespace == id.ContractNamespace)
 | |
|                                 {
 | |
|                                     Constraint.AddValidationError(context, new ValidationError(SR.OperationDoesNotExistInContract(receiveActivity.OperationName, contractName, contractNamespace)));
 | |
|                                     break;
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             void ValidateTransactionBehavior(NativeActivityContext context, Receive receiveActivity, OperationDescription targetOperation)
 | |
|             {
 | |
|                 TransactionFlowAttribute transactionFlowAttribute = targetOperation.Behaviors.Find<TransactionFlowAttribute>();
 | |
|                 Activity parent = null;
 | |
| 
 | |
|                 // we know it's IList<Activity>
 | |
|                 IList<Activity> parentChain = (IList<Activity>)this.ParentChain.Get(context);
 | |
|                 if (parentChain.Count > 0)
 | |
|                 {
 | |
|                     parent = parentChain[0];
 | |
|                 }
 | |
| 
 | |
|                 bool isInTransactedReceiveScope = false;
 | |
| 
 | |
|                 TransactedReceiveScope trs = parent as TransactedReceiveScope;
 | |
|                 if (trs != null && trs.Request == receiveActivity)
 | |
|                 {
 | |
|                     isInTransactedReceiveScope = true;
 | |
|                 }
 | |
| 
 | |
|                 if (transactionFlowAttribute != null)
 | |
|                 {
 | |
|                     if (transactionFlowAttribute.Transactions == TransactionFlowOption.Mandatory)
 | |
|                     {
 | |
|                         if (targetOperation.IsOneWay)
 | |
|                         {
 | |
|                             Constraint.AddValidationError(context, new ValidationError(SR.TargetContractCannotBeOneWayWithTransactionFlow(targetOperation.Name, targetOperation.DeclaringContract.Name)));
 | |
|                         }
 | |
| 
 | |
|                         // Receive has to be in a transacted receive scope
 | |
|                         if (!isInTransactedReceiveScope)
 | |
|                         {
 | |
|                             Constraint.AddValidationError(context, new ValidationError(SR.ReceiveIsNotInTRS(targetOperation.Name, targetOperation.DeclaringContract.Name)));
 | |
|                         }
 | |
|                     }
 | |
|                     else if (transactionFlowAttribute.Transactions == TransactionFlowOption.NotAllowed)
 | |
|                     {
 | |
|                         if (isInTransactedReceiveScope)
 | |
|                         {
 | |
|                             Constraint.AddValidationError(context, new ValidationError(SR.ReceiveIsInTRSWhenTransactionFlowNotAllowed(targetOperation.Name, targetOperation.DeclaringContract.Name), true));
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             void ValidateContract(NativeActivityContext context, Receive receiveActivity, OperationDescription targetOperation)
 | |
|             {
 | |
|                 SerializerOption targetSerializerOption = targetOperation.Behaviors.Contains(typeof(XmlSerializerOperationBehavior)) ?
 | |
|                     SerializerOption.XmlSerializer : SerializerOption.DataContractSerializer;
 | |
|                 if (receiveActivity.SerializerOption != targetSerializerOption)
 | |
|                 {
 | |
|                     Constraint.AddValidationError(context, new ValidationError(SR.PropertyMismatch(receiveActivity.SerializerOption.ToString(), "SerializerOption", targetSerializerOption.ToString(), targetOperation.DeclaringContract.Name, targetSerializerOption.ToString())));
 | |
|                 }
 | |
| 
 | |
|                 if ((!targetOperation.HasProtectionLevel && receiveActivity.ProtectionLevel.HasValue && receiveActivity.ProtectionLevel != Net.Security.ProtectionLevel.None)
 | |
|                     || (receiveActivity.ProtectionLevel.HasValue && receiveActivity.ProtectionLevel.Value != targetOperation.ProtectionLevel)
 | |
|                     || (!receiveActivity.ProtectionLevel.HasValue && targetOperation.ProtectionLevel != Net.Security.ProtectionLevel.None))
 | |
|                 {
 | |
|                     string targetProtectionLevelString = targetOperation.HasProtectionLevel ?
 | |
|                         targetOperation.ProtectionLevel.ToString() : SR.NotSpecified;
 | |
|                     string receiveProtectionLevelString = receiveActivity.ProtectionLevel.HasValue ? receiveActivity.ProtectionLevel.ToString() : SR.NotSpecified;
 | |
|                     Constraint.AddValidationError(context, new ValidationError(SR.PropertyMismatch(receiveProtectionLevelString, "ProtectionLevel", targetProtectionLevelString, targetOperation.Name, targetOperation.DeclaringContract.Name)));
 | |
|                 }
 | |
| 
 | |
|                 // We validate that all known types on the contract be present on the activity.
 | |
|                 // If activity contains more known types, we don't mind.
 | |
|                 if (targetOperation.KnownTypes.Count > 0)
 | |
|                 {
 | |
|                     // We require that each Receive contains all the known types specified on the contract.
 | |
|                     // Known type collections from multiple Receive activities with same contract name and operation name will NOT be merged.
 | |
| 
 | |
|                     // We expect the number of known types to be small, therefore we choose to use simple iterative search.
 | |
|                     foreach (Type targetType in targetOperation.KnownTypes)
 | |
|                     {
 | |
|                         if (receiveActivity.KnownTypes == null || !receiveActivity.KnownTypes.Contains(targetType))
 | |
|                         {
 | |
|                             if (targetType != null && targetType != TypeHelper.VoidType)
 | |
|                             {
 | |
|                                 Constraint.AddValidationError(context, new ValidationError(SR.MissingKnownTypes(targetType.FullName, targetOperation.Name, targetOperation.DeclaringContract.Name)));
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 this.ValidateTransactionBehavior(context, receiveActivity, targetOperation);
 | |
|                 receiveActivity.InternalContent.ValidateContract(context, targetOperation, receiveActivity, MessageDirection.Input);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         class ValidateSendReplyContract : NativeActivity
 | |
|         {
 | |
|             public InArgument<SendReply> ReceiveActivity
 | |
|             {
 | |
|                 get;
 | |
|                 set;
 | |
|             }
 | |
| 
 | |
|             public InArgument<ValidationContext> ValidationContext
 | |
|             {
 | |
|                 get;
 | |
|                 set;
 | |
|             }
 | |
|             
 | |
|             public InArgument<WorkflowService> WorkflowSerivce
 | |
|             {
 | |
|                 get;
 | |
|                 set;
 | |
|             }
 | |
| 
 | |
|             protected override void CacheMetadata(NativeActivityMetadata metadata)
 | |
|             {
 | |
|                 RuntimeArgument receiveActivity = new RuntimeArgument("ReceiveActivity", typeof(SendReply), ArgumentDirection.In);
 | |
|                 RuntimeArgument validationContext = new RuntimeArgument("ValidationContext", typeof(ValidationContext), ArgumentDirection.In);
 | |
|                 RuntimeArgument operationProperties = new RuntimeArgument("OperationProperties", typeof(WorkflowService), ArgumentDirection.In);
 | |
| 
 | |
|                 if (this.ReceiveActivity == null)
 | |
|                 {
 | |
|                     this.ReceiveActivity = new InArgument<SendReply>();
 | |
|                 }
 | |
|                 metadata.Bind(this.ReceiveActivity, receiveActivity);
 | |
| 
 | |
|                 if (this.ValidationContext == null)
 | |
|                 {
 | |
|                     this.ValidationContext = new InArgument<ValidationContext>();
 | |
|                 }
 | |
|                 metadata.Bind(this.ValidationContext, validationContext);
 | |
| 
 | |
|                 if (this.WorkflowSerivce == null)
 | |
|                 {
 | |
|                     this.WorkflowSerivce = new InArgument<WorkflowService>();
 | |
|                 }
 | |
|                 metadata.Bind(this.WorkflowSerivce, operationProperties);
 | |
| 
 | |
|                 Collection<RuntimeArgument> arguments = new Collection<RuntimeArgument>
 | |
|                 {
 | |
|                     receiveActivity,
 | |
|                     validationContext,
 | |
|                     operationProperties
 | |
|                 };
 | |
| 
 | |
|                 metadata.SetArgumentsCollection(arguments);
 | |
|             }
 | |
| 
 | |
|             protected override void Execute(NativeActivityContext context)
 | |
|             {
 | |
|                 SendReply sendReplyActivity = this.ReceiveActivity.Get(context);
 | |
|                 Dictionary<OperationIdentifier, OperationProperty> operationProperties;
 | |
| 
 | |
|                 if (sendReplyActivity.Request != null)
 | |
|                 {
 | |
|                     if (sendReplyActivity.Request.ServiceContractName != null && sendReplyActivity.Request.OperationName != null)
 | |
|                     {
 | |
|                         WorkflowService workflowService = this.WorkflowSerivce.Get(context);
 | |
|                         operationProperties = workflowService.OperationProperties;
 | |
|                         if (operationProperties != null)
 | |
|                         {
 | |
|                             XName contractXName = sendReplyActivity.Request.ServiceContractName;
 | |
|                             string contractName = contractXName.LocalName;
 | |
|                             string contractNamespace = string.IsNullOrEmpty(contractXName.NamespaceName) ?
 | |
|                                 NamingHelper.DefaultNamespace : contractXName.NamespaceName;
 | |
|                             string operationXmlName = NamingHelper.XmlName(sendReplyActivity.Request.OperationName);
 | |
| 
 | |
|                             OperationProperty property;
 | |
|                             OperationIdentifier id = new OperationIdentifier(contractName, contractNamespace, operationXmlName);
 | |
|                             if (operationProperties.TryGetValue(id, out property))
 | |
|                             {
 | |
|                                 if (!property.Operation.IsOneWay)
 | |
|                                 {
 | |
|                                     property.ImplementingSendRepliesRequests.Add(sendReplyActivity.Request);
 | |
|                                     Fx.Assert(property.Operation != null, "OperationProperty.Operation should not be null!");
 | |
|                                     ValidateContract(context, sendReplyActivity, property.Operation);
 | |
|                                 }
 | |
|                                 else
 | |
|                                 {
 | |
|                                     Constraint.AddValidationError(context, new ValidationError(SR.OnewayContractIsImplementedAsTwoWay(property.Operation.Name, contractName)));
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             void ValidateContract(NativeActivityContext context, SendReply sendReply, OperationDescription targetOperation)
 | |
|             {
 | |
|                 sendReply.InternalContent.ValidateContract(context, targetOperation, sendReply, MessageDirection.Output);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 |