//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.ServiceModel.Activities { using System; using System.Activities; using System.Activities.Expressions; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Runtime; using System.Runtime.DurableInstancing; using System.ServiceModel; using System.ServiceModel.Activities.Dispatcher; using System.ServiceModel.Channels; using System.Xaml; using Microsoft.VisualBasic.Activities; using SR2 = System.ServiceModel.Activities.SR; static class MessagingActivityHelper { static Type faultExceptionType = typeof(FaultException); static Type faultExceptionGenericType = typeof(FaultException<>); public const string ActivityInstanceId = "ActivityInstanceId"; public const string ActivityName = "ActivityName"; public const string ActivityType = "ActivityType"; public const string ActivityTypeExecuteUserCode = "ExecuteUserCode"; public const string MessagingActivityTypeActivityExecution = "MessagingActivityExecution"; public const string E2EActivityId = "E2EActivityId"; public const string MessageId = "MessageId"; public const string ActivityNameWorkflowOperationInvoke = "WorkflowOperationInvoke"; public const string MessageCorrelationReceiveRecord = "MessageCorrelationReceiveRecord"; public const string MessageCorrelationSendRecord = "MessageCorrelationSendRecord"; public static void FixMessageArgument(Argument messageArgument, ArgumentDirection direction, ActivityMetadata metadata) { Type messageType = (messageArgument == null) ? TypeHelper.ObjectType : messageArgument.ArgumentType; AddRuntimeArgument(messageArgument, "Message", messageType, direction, metadata); } public static void AddRuntimeArgument(Argument messageArgument, string runtimeArgumentName, Type runtimeArgumentType, ArgumentDirection runtimeArgumentDirection, ActivityMetadata metadata) { RuntimeArgument argument = new RuntimeArgument(runtimeArgumentName, runtimeArgumentType, runtimeArgumentDirection); metadata.Bind(messageArgument, argument); metadata.AddArgument(argument); } // public static IList GetCallbacks(ExecutionProperties executionProperties) where T : class { List list = null; if (!executionProperties.IsEmpty) { T temp; foreach (KeyValuePair item in executionProperties) { temp = item.Value as T; if (temp != null) { if (list == null) { list = new List(); } list.Add(temp); } } } return list; } public static Message InitializeCorrelationHandles(NativeActivityContext context, CorrelationHandle selectHandle, CorrelationHandle ambientHandle, Collection additionalCorrelations, CorrelationKeyCalculator keyCalculator, Message message) { InstanceKey instanceKey; ICollection additionalKeys; // MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue); if (keyCalculator.CalculateKeys(buffer, message, out instanceKey, out additionalKeys)) { InitializeCorrelationHandles(context, selectHandle, ambientHandle, additionalCorrelations, instanceKey, additionalKeys); } return buffer.CreateMessage(); } public static void InitializeCorrelationHandles(NativeActivityContext context, CorrelationHandle selectHandle, CorrelationHandle ambientHandle, Collection additionalCorrelations, MessageProperties messageProperties) { CorrelationMessageProperty correlationMessageProperty; if (CorrelationMessageProperty.TryGet(messageProperties, out correlationMessageProperty)) { InitializeCorrelationHandles(context, selectHandle, ambientHandle, additionalCorrelations, correlationMessageProperty.CorrelationKey, correlationMessageProperty.AdditionalKeys); } } // both receive and send initialize correlations using this method // if selectHandle is not null, we first try to initalize instanceKey with it , else we try to initalize the ambient handle // if ambient handle is not used for initializing instance key , we might use it for initalizing queryCorrelationsInitalizer. // SelectHandle usage: // Receive: selectHandle is the correlatesWith handle // SendReply: in case of context based correlation, this is the context handle // Send: in case of context based correlation, this will be the callback handle // ReceiveReply: selectHandle will be always null // Note that only Receive can initialize a content based correlation with a selectHandle (parallel convoy) internal static void InitializeCorrelationHandles(NativeActivityContext context, CorrelationHandle selectHandle, CorrelationHandle ambientHandle, Collection additionalCorrelations, InstanceKey instanceKey, ICollection additionalKeys) { bool isAmbientHandleUsed = false; if (instanceKey != null && instanceKey.IsValid) { if (selectHandle != null) { selectHandle.InitializeBookmarkScope(context, instanceKey); } else if (ambientHandle != null) { ambientHandle.InitializeBookmarkScope(context, instanceKey); isAmbientHandleUsed = true; } else if (context.DefaultBookmarkScope.IsInitialized) { if (context.DefaultBookmarkScope.Id != instanceKey.Value) { throw FxTrace.Exception.AsError( new InvalidOperationException(SR2.CorrelationHandleInUse(context.DefaultBookmarkScope.Id, instanceKey.Value))); } } else { context.DefaultBookmarkScope.Initialize(context, instanceKey.Value); } } if (additionalKeys != null && additionalCorrelations != null) { // The ordering of items in SelectAdditional and additional correlations are the same // Therefore, we assign keys iteratively IEnumerator enumerator = additionalCorrelations.GetEnumerator(); foreach (InstanceKey key in additionalKeys) { Fx.Assert(key != null && key.IsValid, "only valid keys should be passed into InitializeCorrelationHandles"); while (enumerator.MoveNext()) { QueryCorrelationInitializer queryCorrelation = enumerator.Current as QueryCorrelationInitializer; if (queryCorrelation != null) { CorrelationHandle handle = (queryCorrelation.CorrelationHandle != null ? queryCorrelation.CorrelationHandle.Get(context) : null); if (handle == null) { if (ambientHandle != null && !isAmbientHandleUsed) { handle = ambientHandle; isAmbientHandleUsed = true; } else { throw FxTrace.Exception.AsError( new InvalidOperationException(SR2.QueryCorrelationInitializerCannotBeInitialized)); } } handle.InitializeBookmarkScope(context, key); break; } } } } } public static CorrelationCallbackContext CreateCorrelationCallbackContext(MessageProperties messageProperties) { CallbackContextMessageProperty callbackMessageContextProperty; if (CallbackContextMessageProperty.TryGet(messageProperties, out callbackMessageContextProperty)) { EndpointAddress listenAddress; IDictionary context; callbackMessageContextProperty.GetListenAddressAndContext(out listenAddress, out context); return new CorrelationCallbackContext { ListenAddress = EndpointAddress10.FromEndpointAddress(listenAddress), Context = context }; } return null; } public static CorrelationContext CreateCorrelationContext(MessageProperties messageProperties) { ContextMessageProperty contextMessageProperty; if (ContextMessageProperty.TryGet(messageProperties, out contextMessageProperty)) { IDictionary context; context = contextMessageProperty.Context; return new CorrelationContext { Context = context }; } return null; } public static bool CompareContextEquality(IDictionary context1, IDictionary context2) { if (context1 != context2) { if (context1 == null || context2 == null || context1.Count != context2.Count) { return false; } foreach (KeyValuePair pair in context1) { if (!context2.Contains(pair)) { return false; } } } return true; } public static InArgument CreateReplyCorrelatesWith(InArgument requestCorrelatesWith) { Fx.Assert(requestCorrelatesWith != null, "Argument cannot be null!"); VariableValue variableValue = requestCorrelatesWith.Expression as VariableValue; if (variableValue != null) { return new InArgument(variableValue.Variable); } VisualBasicValue vbvalue = requestCorrelatesWith.Expression as VisualBasicValue; if (vbvalue != null) { return new InArgument(new VisualBasicValue(vbvalue.ExpressionText)); } // We use XAML roundtrip to clone expression string xamlStr = XamlServices.Save(requestCorrelatesWith.Expression); object obj = XamlServices.Parse(xamlStr); Activity expression = obj as Activity; Fx.Assert(expression != null, "Failed to clone CorrelationHandle using XAML roundtrip!"); return new InArgument(expression); } public static void ValidateCorrelationInitializer(ActivityMetadata metadata, Collection correlationInitializers, bool isReply, string displayName, string operationName) { Fx.Assert(metadata != null, "cannot be null"); if (correlationInitializers != null && correlationInitializers.Count > 0) { bool queryInitializerWithEmptyHandle = false; foreach (CorrelationInitializer correlation in correlationInitializers) { if (correlation is RequestReplyCorrelationInitializer && isReply) { // This is a reply, so additional correlations should not have a request reply handle metadata.AddValidationError(SR.ReplyShouldNotIncludeRequestReplyHandle(displayName, operationName)); } QueryCorrelationInitializer queryCorrelation = correlation as QueryCorrelationInitializer; if (queryCorrelation != null) { if (queryCorrelation.MessageQuerySet.Count == 0) { metadata.AddValidationError(SR.QueryCorrelationInitializerWithEmptyMessageQuerySet(displayName, operationName)); } } if (correlation.CorrelationHandle == null) { if (correlation is QueryCorrelationInitializer) { if (!queryInitializerWithEmptyHandle) { queryInitializerWithEmptyHandle = true; } else { // more than one queryInitializer present, in this case we don't permit null handle metadata.AddValidationError(SR.NullCorrelationHandleInMultipleQueryCorrelation); } } else { metadata.AddValidationError(SR.NullCorrelationHandleInInitializeCorrelation(correlation.GetType().Name)); } } } } } } }