734 lines
31 KiB
C#
734 lines
31 KiB
C#
|
//-----------------------------------------------------------------------------
|
|||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|||
|
//-----------------------------------------------------------------------------
|
|||
|
|
|||
|
namespace System.ServiceModel.Activities
|
|||
|
{
|
|||
|
using System.Activities;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Collections.ObjectModel;
|
|||
|
using System.Net.Security;
|
|||
|
using System.Runtime;
|
|||
|
using System.ServiceModel.Activities.Description;
|
|||
|
using System.ServiceModel.Channels;
|
|||
|
using System.ServiceModel.Description;
|
|||
|
using System.Xml;
|
|||
|
using System.Xml.Linq;
|
|||
|
|
|||
|
static class ContractInferenceHelper
|
|||
|
{
|
|||
|
static DataContractFormatAttribute dataContractFormatAttribute;
|
|||
|
static XmlSerializerFormatAttribute xmlSerializerFormatAttribute;
|
|||
|
static Type exceptionType;
|
|||
|
static Type faultExceptionType;
|
|||
|
|
|||
|
public static DataContractFormatAttribute DataContractFormatAttribute
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (dataContractFormatAttribute == null)
|
|||
|
{
|
|||
|
dataContractFormatAttribute = new DataContractFormatAttribute();
|
|||
|
}
|
|||
|
return dataContractFormatAttribute;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static XmlSerializerFormatAttribute XmlSerializerFormatAttribute
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (xmlSerializerFormatAttribute == null)
|
|||
|
{
|
|||
|
xmlSerializerFormatAttribute = new XmlSerializerFormatAttribute
|
|||
|
{
|
|||
|
SupportFaults = true
|
|||
|
};
|
|||
|
}
|
|||
|
return xmlSerializerFormatAttribute;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static Type ExceptionType
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (exceptionType == null)
|
|||
|
{
|
|||
|
exceptionType = typeof(Exception);
|
|||
|
}
|
|||
|
return exceptionType;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static Type FaultExceptionType
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
if (faultExceptionType == null)
|
|||
|
{
|
|||
|
faultExceptionType = typeof(FaultException<>);
|
|||
|
}
|
|||
|
return faultExceptionType;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void ProvideDefaultNamespace(ref XName serviceContractName)
|
|||
|
{
|
|||
|
Fx.Assert(serviceContractName != null, "Argument cannot be null!");
|
|||
|
|
|||
|
if (string.IsNullOrEmpty(serviceContractName.NamespaceName))
|
|||
|
{
|
|||
|
// If no namespace is given by the user, we provide default namespace. This is consistent with WCF.
|
|||
|
serviceContractName = XName.Get(serviceContractName.LocalName, NamingHelper.DefaultNamespace);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static ContractDescription CreateContractFromOperation(XName serviceContractName, OperationDescription operation)
|
|||
|
{
|
|||
|
Fx.Assert(serviceContractName != null, "serviceContractName cannot be null");
|
|||
|
ProvideDefaultNamespace(ref serviceContractName);
|
|||
|
|
|||
|
ContractDescription contract = new ContractDescription(serviceContractName.LocalName, serviceContractName.NamespaceName)
|
|||
|
{
|
|||
|
// For inferred client side contracts, we do not set ContractType
|
|||
|
|
|||
|
ConfigurationName = serviceContractName.LocalName,
|
|||
|
SessionMode = SessionMode.Allowed
|
|||
|
};
|
|||
|
contract.Operations.Add(operation);
|
|||
|
return contract;
|
|||
|
}
|
|||
|
|
|||
|
public static ContractDescription CreateOutputChannelContractDescription(XName serviceContractName, ProtectionLevel? protectionLevel)
|
|||
|
{
|
|||
|
Fx.Assert(serviceContractName != null, "cannot be null");
|
|||
|
Type channelType = typeof(IOutputChannel);
|
|||
|
|
|||
|
ProvideDefaultNamespace(ref serviceContractName);
|
|||
|
|
|||
|
ContractDescription contract = new ContractDescription(serviceContractName.LocalName, serviceContractName.NamespaceName)
|
|||
|
{
|
|||
|
ContractType = channelType,
|
|||
|
ConfigurationName = serviceContractName.LocalName,
|
|||
|
SessionMode = SessionMode.Allowed
|
|||
|
};
|
|||
|
OperationDescription operation = new OperationDescription("Send", contract);
|
|||
|
MessageDescription message = new MessageDescription(MessageHeaders.WildcardAction, MessageDirection.Input);
|
|||
|
operation.Messages.Add(message);
|
|||
|
|
|||
|
if (protectionLevel.HasValue)
|
|||
|
{
|
|||
|
operation.ProtectionLevel = protectionLevel.Value;
|
|||
|
}
|
|||
|
|
|||
|
contract.Operations.Add(operation);
|
|||
|
return contract;
|
|||
|
}
|
|||
|
|
|||
|
public static ContractDescription CreateRequestChannelContractDescription(XName serviceContractName, ProtectionLevel? protectionLevel)
|
|||
|
{
|
|||
|
Fx.Assert(serviceContractName != null, "cannot be null");
|
|||
|
Type channelType = typeof(IRequestChannel);
|
|||
|
|
|||
|
ProvideDefaultNamespace(ref serviceContractName);
|
|||
|
|
|||
|
ContractDescription contract = new ContractDescription(serviceContractName.LocalName, serviceContractName.NamespaceName)
|
|||
|
{
|
|||
|
ContractType = channelType,
|
|||
|
ConfigurationName = serviceContractName.LocalName,
|
|||
|
SessionMode = SessionMode.Allowed
|
|||
|
};
|
|||
|
OperationDescription operation = new OperationDescription("Request", contract);
|
|||
|
MessageDescription request = new MessageDescription(MessageHeaders.WildcardAction, MessageDirection.Input);
|
|||
|
MessageDescription reply = new MessageDescription(MessageHeaders.WildcardAction, MessageDirection.Output);
|
|||
|
operation.Messages.Add(request);
|
|||
|
operation.Messages.Add(reply);
|
|||
|
|
|||
|
if (protectionLevel.HasValue)
|
|||
|
{
|
|||
|
operation.ProtectionLevel = protectionLevel.Value;
|
|||
|
}
|
|||
|
|
|||
|
contract.Operations.Add(operation);
|
|||
|
return contract;
|
|||
|
}
|
|||
|
|
|||
|
public static void EnsureTransactionFlowOnContract(
|
|||
|
ref ServiceEndpoint serviceEndpoint,
|
|||
|
XName serviceContractName,
|
|||
|
string operationName,
|
|||
|
string action,
|
|||
|
ProtectionLevel? protectionLevel)
|
|||
|
{
|
|||
|
Fx.Assert(serviceEndpoint != null, "ServiceEndpoint cannot be null!");
|
|||
|
|
|||
|
// Client side fully inferred contract always has null ContractType
|
|||
|
if (serviceEndpoint.Contract.ContractType == null)
|
|||
|
{
|
|||
|
// If we are using the real contract, we only need to add TrancactionFlowAttribute to the operation
|
|||
|
Fx.Assert(serviceEndpoint.Contract.Operations.Count == 1, "Client side contract should have exactly one operation!");
|
|||
|
|
|||
|
serviceEndpoint.Contract.Operations[0].Behaviors.Add(new TransactionFlowAttribute(TransactionFlowOption.Allowed));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Replace the original fake contract with a fake contract tailored for transaction
|
|||
|
|
|||
|
ContractDescription contract = null;
|
|||
|
OperationDescription operation = null;
|
|||
|
MessageDescription request = null;
|
|||
|
MessageDescription reply = null;
|
|||
|
|
|||
|
Type channelType = typeof(IRequestChannel);
|
|||
|
|
|||
|
// We need to create a contract description with the real service contract name
|
|||
|
// and operation name and actions and with the TransactionFlow operation behavior
|
|||
|
// because the TransactionChannelFactory has a dictionary of "Directional Action" to
|
|||
|
// transaction flow value that it uses to decide whether or not to include the
|
|||
|
// transaction header in the message.
|
|||
|
Fx.Assert(serviceContractName != null, "Argument serviceContractName cannot be null!");
|
|||
|
Fx.Assert(operationName != null, "Argument operationName cannot be null!");
|
|||
|
|
|||
|
ProvideDefaultNamespace(ref serviceContractName);
|
|||
|
|
|||
|
contract = new ContractDescription(serviceContractName.LocalName, serviceContractName.NamespaceName)
|
|||
|
{
|
|||
|
ContractType = channelType,
|
|||
|
SessionMode = SessionMode.Allowed
|
|||
|
};
|
|||
|
operation = new OperationDescription(operationName, contract);
|
|||
|
operation.Behaviors.Add(new TransactionFlowAttribute(TransactionFlowOption.Allowed));
|
|||
|
|
|||
|
string requestAction = null;
|
|||
|
string replyAction = null;
|
|||
|
if (String.IsNullOrEmpty(action))
|
|||
|
{
|
|||
|
// Construct the action.
|
|||
|
requestAction = NamingHelper.GetMessageAction(operation, false);
|
|||
|
replyAction = NamingHelper.GetMessageAction(operation, true);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
requestAction = action;
|
|||
|
replyAction = action + TypeLoader.ResponseSuffix;
|
|||
|
}
|
|||
|
|
|||
|
request = new MessageDescription(requestAction, MessageDirection.Input);
|
|||
|
reply = new MessageDescription(replyAction, MessageDirection.Output);
|
|||
|
|
|||
|
operation.Messages.Add(request);
|
|||
|
operation.Messages.Add(reply);
|
|||
|
|
|||
|
if (protectionLevel.HasValue)
|
|||
|
{
|
|||
|
operation.ProtectionLevel = protectionLevel.Value;
|
|||
|
}
|
|||
|
|
|||
|
contract.Operations.Add(operation);
|
|||
|
|
|||
|
// We need to replace the ServiceEndpoint because ServiceEndpoint.Contract does not have a public setter
|
|||
|
Uri listenUri = serviceEndpoint.ListenUri;
|
|||
|
serviceEndpoint = new ServiceEndpoint(contract)
|
|||
|
{
|
|||
|
Binding = serviceEndpoint.Binding,
|
|||
|
Address = serviceEndpoint.Address,
|
|||
|
Name = serviceEndpoint.Name,
|
|||
|
};
|
|||
|
if (listenUri != null)
|
|||
|
{
|
|||
|
serviceEndpoint.ListenUri = listenUri;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static OperationDescription CreateOneWayOperationDescription(Send send)
|
|||
|
{
|
|||
|
Fx.Assert(send != null, "Argument cannot be null!");
|
|||
|
return CreateOperationDescriptionCore(send, null);
|
|||
|
}
|
|||
|
|
|||
|
public static OperationDescription CreateTwoWayOperationDescription(Send send, ReceiveReply receiveReply)
|
|||
|
{
|
|||
|
Fx.Assert(send != null && receiveReply != null, "Arguments cannot be null!");
|
|||
|
return CreateOperationDescriptionCore(send, receiveReply);
|
|||
|
}
|
|||
|
|
|||
|
static OperationDescription CreateOperationDescriptionCore(Send send, ReceiveReply receiveReply)
|
|||
|
{
|
|||
|
XName contractXName = send.ServiceContractName;
|
|||
|
ProvideDefaultNamespace(ref contractXName);
|
|||
|
|
|||
|
// Infer Name, Namespace, ConfigurationName
|
|||
|
ContractDescription contract = new ContractDescription(contractXName.LocalName, contractXName.NamespaceName);
|
|||
|
contract.ConfigurationName = send.EndpointConfigurationName;
|
|||
|
|
|||
|
OperationDescription operation = new OperationDescription(NamingHelper.XmlName(send.OperationName), contract);
|
|||
|
if (send.ProtectionLevel.HasValue)
|
|||
|
{
|
|||
|
operation.ProtectionLevel = send.ProtectionLevel.Value;
|
|||
|
}
|
|||
|
|
|||
|
AddKnownTypesToOperation(operation, send.KnownTypes);
|
|||
|
|
|||
|
// Infer In-Message
|
|||
|
send.InternalContent.InferMessageDescription(operation, send, MessageDirection.Input);
|
|||
|
|
|||
|
// Infer Out-Message
|
|||
|
if (receiveReply != null)
|
|||
|
{
|
|||
|
receiveReply.InternalContent.InferMessageDescription(operation, receiveReply, MessageDirection.Output);
|
|||
|
}
|
|||
|
|
|||
|
PostProcessOperation(operation);
|
|||
|
AddSerializerProvider(operation, send.SerializerOption);
|
|||
|
|
|||
|
contract.Operations.Add(operation);
|
|||
|
|
|||
|
return operation;
|
|||
|
}
|
|||
|
|
|||
|
// Create server side OperationDescription.
|
|||
|
// Note this method assumes that CacheMetadata has been called on the Receive activity (as part of
|
|||
|
// the activity tree walk that is done in WorkflowService.GetContractDescriptions) because it relies on
|
|||
|
// InternalReceiveMessage property of the Receive actitivy to be non-null.
|
|||
|
public static OperationDescription CreateOperationDescription(Receive receive, ContractDescription contract)
|
|||
|
{
|
|||
|
Fx.Assert(receive.InternalReceive != null, "This method can only be called if CacheMetadata has been called on the receive activity");
|
|||
|
|
|||
|
OperationDescription operation = new OperationDescription(NamingHelper.XmlName(receive.OperationName), contract);
|
|||
|
|
|||
|
if (receive.ProtectionLevel.HasValue)
|
|||
|
{
|
|||
|
operation.ProtectionLevel = receive.ProtectionLevel.Value;
|
|||
|
}
|
|||
|
|
|||
|
// Infer In-Message
|
|||
|
receive.InternalContent.InferMessageDescription(operation, receive, MessageDirection.Input);
|
|||
|
|
|||
|
// Infer Out-Message
|
|||
|
if (receive.HasReply)
|
|||
|
{
|
|||
|
// At this point, we already know all the following SendReplies are equivalent
|
|||
|
SendReply sendReply = receive.FollowingReplies[0];
|
|||
|
sendReply.InternalContent.InferMessageDescription(operation, sendReply, MessageDirection.Output);
|
|||
|
}
|
|||
|
else if (receive.HasFault)
|
|||
|
{
|
|||
|
// We infer Receive-SendFault pair as a two-way operation with void return value
|
|||
|
CheckForDisposableParameters(operation, Constants.EmptyTypeArray);
|
|||
|
AddOutputMessage(operation, null, Constants.EmptyStringArray, Constants.EmptyTypeArray);
|
|||
|
}
|
|||
|
|
|||
|
PostProcessOperation(operation);
|
|||
|
|
|||
|
// Behaviors
|
|||
|
AddSerializerProvider(operation, receive.SerializerOption);
|
|||
|
AddWorkflowOperationBehaviors(operation, receive.InternalReceive.OperationBookmarkName, receive.CanCreateInstance);
|
|||
|
|
|||
|
if (receive.InternalReceive.AdditionalData.IsInsideTransactedReceiveScope)
|
|||
|
{
|
|||
|
operation.IsInsideTransactedReceiveScope = true;
|
|||
|
EnableTransactionBehavior(operation);
|
|||
|
if (receive.InternalReceive.AdditionalData.IsFirstReceiveOfTransactedReceiveScopeTree)
|
|||
|
{
|
|||
|
operation.IsFirstReceiveOfTransactedReceiveScopeTree = true;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return operation;
|
|||
|
}
|
|||
|
|
|||
|
public static void AddInputMessage(OperationDescription operation, string overridingAction, Type type, SerializerOption serializerOption)
|
|||
|
{
|
|||
|
Fx.Assert(operation.Messages.Count == 0, "Operation already has input message");
|
|||
|
|
|||
|
bool isResponse = false;
|
|||
|
MessageDescription message = MessageBuilder.CreateMessageDescription(
|
|||
|
operation, isResponse, MessageDirection.Input, overridingAction, type, serializerOption);
|
|||
|
|
|||
|
operation.Messages.Add(message);
|
|||
|
}
|
|||
|
|
|||
|
public static void AddInputMessage(OperationDescription operation, string overridingAction,
|
|||
|
string[] argumentNames, Type[] argumentTypes)
|
|||
|
{
|
|||
|
Fx.Assert(operation.Messages.Count == 0, "Operation already has input message");
|
|||
|
|
|||
|
bool isResponse = false;
|
|||
|
MessageDescription message = MessageBuilder.CreateMessageDescription(
|
|||
|
operation, isResponse, MessageDirection.Input, overridingAction, argumentNames, argumentTypes);
|
|||
|
|
|||
|
operation.Messages.Add(message);
|
|||
|
}
|
|||
|
|
|||
|
public static void AddOutputMessage(OperationDescription operation, string overridingAction, Type type, SerializerOption serializerOption)
|
|||
|
{
|
|||
|
Fx.Assert(operation.Messages.Count > 0, "Operation does not have input message");
|
|||
|
Fx.Assert(operation.Messages.Count < 2, "Operation already has output message");
|
|||
|
|
|||
|
bool isResponse = true;
|
|||
|
MessageDescription message = MessageBuilder.CreateMessageDescription(
|
|||
|
operation, isResponse, MessageDirection.Output, overridingAction, type, serializerOption);
|
|||
|
|
|||
|
operation.Messages.Add(message);
|
|||
|
}
|
|||
|
|
|||
|
public static void AddOutputMessage(OperationDescription operation, string overridingAction,
|
|||
|
string[] argumentNames, Type[] argumentTypes)
|
|||
|
{
|
|||
|
Fx.Assert(operation.Messages.Count > 0, "Operation does not have input message");
|
|||
|
Fx.Assert(operation.Messages.Count < 2, "Operation already has output message");
|
|||
|
|
|||
|
bool isResponse = true;
|
|||
|
MessageDescription message = MessageBuilder.CreateMessageDescription(
|
|||
|
operation, isResponse, MessageDirection.Output, overridingAction, argumentNames, argumentTypes);
|
|||
|
|
|||
|
operation.Messages.Add(message);
|
|||
|
}
|
|||
|
|
|||
|
static void AddKnownTypesToOperation(OperationDescription operation, Collection<Type> knownTypes)
|
|||
|
{
|
|||
|
if (knownTypes != null)
|
|||
|
{
|
|||
|
foreach (Type knownType in knownTypes)
|
|||
|
{
|
|||
|
operation.KnownTypes.Add(knownType);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void CheckForDisposableParameters(OperationDescription operation, Type type)
|
|||
|
{
|
|||
|
if (type == null)
|
|||
|
{
|
|||
|
operation.HasNoDisposableParameters = true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
operation.HasNoDisposableParameters = !ServiceReflector.IsParameterDisposable(type);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void CheckForDisposableParameters(OperationDescription operation, Type[] types)
|
|||
|
{
|
|||
|
Fx.Assert(types != null, "Argument cannot be null!");
|
|||
|
|
|||
|
operation.HasNoDisposableParameters = true;
|
|||
|
foreach (Type type in types)
|
|||
|
{
|
|||
|
if (ServiceReflector.IsParameterDisposable(type))
|
|||
|
{
|
|||
|
operation.HasNoDisposableParameters = false;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void EnableTransactionBehavior(OperationDescription operationDescription)
|
|||
|
{
|
|||
|
Fx.Assert(operationDescription != null, "OperationDescription is null");
|
|||
|
|
|||
|
OperationBehaviorAttribute attribute = operationDescription.Behaviors.Find<OperationBehaviorAttribute>();
|
|||
|
if (attribute != null)
|
|||
|
{
|
|||
|
attribute.TransactionScopeRequired = true;
|
|||
|
attribute.TransactionAutoComplete = false;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
OperationBehaviorAttribute attr = new OperationBehaviorAttribute
|
|||
|
{
|
|||
|
TransactionAutoComplete = false,
|
|||
|
TransactionScopeRequired = true
|
|||
|
};
|
|||
|
operationDescription.Behaviors.Add(attr);
|
|||
|
}
|
|||
|
TransactionFlowAttribute transactionFlowAttribute = operationDescription.Behaviors.Find<TransactionFlowAttribute>();
|
|||
|
if (transactionFlowAttribute != null)
|
|||
|
{
|
|||
|
if (transactionFlowAttribute.Transactions != TransactionFlowOption.Allowed)
|
|||
|
{
|
|||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.ContractInferenceValidationForTransactionFlowBehavior));
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (!operationDescription.IsOneWay)
|
|||
|
{
|
|||
|
operationDescription.Behaviors.Add(new TransactionFlowAttribute(TransactionFlowOption.Allowed));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void PostProcessOperation(OperationDescription operation)
|
|||
|
{
|
|||
|
MessageBuilder.ClearWrapperNames(operation);
|
|||
|
}
|
|||
|
|
|||
|
static void AddSerializerProvider(OperationDescription operation, SerializerOption serializerOption)
|
|||
|
{
|
|||
|
switch (serializerOption)
|
|||
|
{
|
|||
|
case SerializerOption.DataContractSerializer:
|
|||
|
AddDataContractSerializerFormat(operation);
|
|||
|
break;
|
|||
|
case SerializerOption.XmlSerializer:
|
|||
|
AddXmlSerializerFormat(operation);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void AddDataContractSerializerFormat(OperationDescription operation)
|
|||
|
{
|
|||
|
if (operation.Behaviors.Find<DataContractSerializerOperationBehavior>() != null)
|
|||
|
{
|
|||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.OperationHasSerializerBehavior(
|
|||
|
operation.Name, operation.DeclaringContract.Name, typeof(DataContractSerializerOperationBehavior))));
|
|||
|
}
|
|||
|
operation.Behaviors.Add(new DataContractSerializerOperationBehavior(operation, DataContractFormatAttribute));
|
|||
|
if (!operation.Behaviors.Contains(typeof(DataContractSerializerOperationGenerator)))
|
|||
|
{
|
|||
|
operation.Behaviors.Add(new DataContractSerializerOperationGenerator());
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void AddXmlSerializerFormat(OperationDescription operation)
|
|||
|
{
|
|||
|
if (operation.Behaviors.Find<XmlSerializerOperationBehavior>() != null)
|
|||
|
{
|
|||
|
throw FxTrace.Exception.AsError(new InvalidOperationException(
|
|||
|
SR.OperationHasSerializerBehavior(operation.Name, operation.DeclaringContract.Name, typeof(XmlSerializerOperationBehavior))));
|
|||
|
}
|
|||
|
operation.Behaviors.Add(new XmlSerializerOperationBehavior(operation, XmlSerializerFormatAttribute));
|
|||
|
if (!operation.Behaviors.Contains(typeof(XmlSerializerOperationGenerator)))
|
|||
|
{
|
|||
|
operation.Behaviors.Add(new XmlSerializerOperationGenerator(new XmlSerializerImportOptions()));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
static void AddWorkflowOperationBehaviors(OperationDescription operation, string bookmarkName, bool canCreateInstance)
|
|||
|
{
|
|||
|
KeyedByTypeCollection<IOperationBehavior> behaviors = operation.Behaviors;
|
|||
|
WorkflowOperationBehavior workflowOperationBehavior = behaviors.Find<WorkflowOperationBehavior>();
|
|||
|
if (workflowOperationBehavior == null)
|
|||
|
{
|
|||
|
behaviors.Add(new WorkflowOperationBehavior(new Bookmark(bookmarkName), canCreateInstance));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
workflowOperationBehavior.CanCreateInstance = workflowOperationBehavior.CanCreateInstance || canCreateInstance;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void CorrectOutMessageForOperation(Receive receive, OperationDescription operation)
|
|||
|
{
|
|||
|
// Remove the original outMessage
|
|||
|
Fx.Assert(operation.Messages.Count == 2, "OperationDescription must be two-way for CorrectOutMessageForOperation to be invoked!");
|
|||
|
operation.Messages.RemoveAt(1);
|
|||
|
|
|||
|
SendReply sendReply = receive.FollowingReplies[0];
|
|||
|
sendReply.InternalContent.InferMessageDescription(operation, sendReply, MessageDirection.Output);
|
|||
|
|
|||
|
ContractInferenceHelper.PostProcessOperation(operation);
|
|||
|
}
|
|||
|
|
|||
|
public static void UpdateIsOneWayFlag(Receive receive, OperationDescription operation)
|
|||
|
{
|
|||
|
// Set InternalReceiveMessage.IsOneWay to false for two-way operations
|
|||
|
if (!operation.IsOneWay)
|
|||
|
{
|
|||
|
receive.SetIsOneWay(false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void AddFaultDescription(Receive activity, OperationDescription operation)
|
|||
|
{
|
|||
|
if (activity.HasFault)
|
|||
|
{
|
|||
|
foreach (SendReply sendFault in activity.FollowingFaults)
|
|||
|
{
|
|||
|
string action = null;
|
|||
|
Type type = null;
|
|||
|
|
|||
|
action = sendFault.Action;
|
|||
|
|
|||
|
SendMessageContent sendReply = sendFault.InternalContent as SendMessageContent;
|
|||
|
if (sendReply != null)
|
|||
|
{
|
|||
|
type = sendReply.InternalDeclaredMessageType;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
SendParametersContent sendReplyParameters = sendFault.InternalContent as SendParametersContent;
|
|||
|
if (sendReplyParameters != null)
|
|||
|
{
|
|||
|
type = sendReplyParameters.ArgumentTypes[0]; // Exception should be the only parameter in SendFault
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Fx.Assert(type != null, "Exception type cannot be null!");
|
|||
|
if (type.IsGenericType && type.GetGenericTypeDefinition() == FaultExceptionType)
|
|||
|
{
|
|||
|
Type faultType = type.GetGenericArguments()[0];
|
|||
|
bool exists = false;
|
|||
|
|
|||
|
// We expect the number of fault types to be small, so we use iterative comparison
|
|||
|
foreach (FaultDescription faultDescription in operation.Faults)
|
|||
|
{
|
|||
|
if (faultDescription.DetailType == faultType)
|
|||
|
{
|
|||
|
if (faultDescription.Action != action)
|
|||
|
{
|
|||
|
throw FxTrace.Exception.AsError(new ValidationException(SR.SendRepliesHaveSameFaultTypeDifferentAction));
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
exists = true;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!exists)
|
|||
|
{
|
|||
|
FaultDescription faultDescription = MessageBuilder.CreateFaultDescription(operation, faultType, action);
|
|||
|
operation.Faults.Add(faultDescription);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void AddKnownTypesToOperation(Receive receive, OperationDescription operation)
|
|||
|
{
|
|||
|
Collection<Type> knownTypes = receive.InternalKnownTypes;
|
|||
|
|
|||
|
if (knownTypes != null)
|
|||
|
{
|
|||
|
foreach (Type knownType in knownTypes)
|
|||
|
{
|
|||
|
// We expect the number of known types to be small, so we use iterative comparison
|
|||
|
if (!operation.KnownTypes.Contains(knownType))
|
|||
|
{
|
|||
|
operation.KnownTypes.Add(knownType);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void AddReceiveToFormatterBehavior(Receive receive, OperationDescription operation)
|
|||
|
{
|
|||
|
Fx.Assert(receive != null && operation != null, "Argument cannot be null!");
|
|||
|
|
|||
|
KeyedByTypeCollection<IOperationBehavior> behaviors = operation.Behaviors;
|
|||
|
WorkflowFormatterBehavior formatterBehavior = behaviors.Find<WorkflowFormatterBehavior>();
|
|||
|
if (formatterBehavior == null)
|
|||
|
{
|
|||
|
formatterBehavior = new WorkflowFormatterBehavior();
|
|||
|
behaviors.Add(formatterBehavior);
|
|||
|
}
|
|||
|
|
|||
|
formatterBehavior.Receives.Add(receive);
|
|||
|
}
|
|||
|
|
|||
|
public static void RemoveReceiveFromFormatterBehavior(Receive receive, OperationDescription operation)
|
|||
|
{
|
|||
|
Fx.Assert(receive != null && operation != null, "Arguments cannot be null!");
|
|||
|
|
|||
|
KeyedByTypeCollection<IOperationBehavior> behaviors = operation.Behaviors;
|
|||
|
WorkflowFormatterBehavior formatterBehavior = behaviors.Find<WorkflowFormatterBehavior>();
|
|||
|
if (formatterBehavior != null)
|
|||
|
{
|
|||
|
formatterBehavior.Receives.Remove(receive);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static CorrelationQuery CreateServerCorrelationQuery(MessageQuerySet select, Collection<CorrelationInitializer> correlationInitializers,
|
|||
|
OperationDescription operation, bool isResponse)
|
|||
|
{
|
|||
|
Fx.Assert(operation != null, "Argument cannot be null!");
|
|||
|
|
|||
|
CorrelationQuery correlationQuery = CreateCorrelationQueryCore(select, correlationInitializers);
|
|||
|
|
|||
|
if (correlationQuery != null)
|
|||
|
{
|
|||
|
string action = !isResponse ? operation.Messages[0].Action : operation.Messages[1].Action;
|
|||
|
correlationQuery.Where = new CorrelationActionMessageFilter { Action = action };
|
|||
|
}
|
|||
|
|
|||
|
return correlationQuery;
|
|||
|
}
|
|||
|
|
|||
|
// this method generates the correlationQuery for client side send and receiveReply
|
|||
|
public static Collection<CorrelationQuery> CreateClientCorrelationQueries(MessageQuerySet select, Collection<CorrelationInitializer> correlationInitializers,
|
|||
|
string overridingAction, XName serviceContractName, string operationName, bool isResponse)
|
|||
|
{
|
|||
|
Fx.Assert(serviceContractName != null && operationName != null, "Argument cannot be null!");
|
|||
|
|
|||
|
Collection<CorrelationQuery> queryCollection = new Collection<CorrelationQuery>();
|
|||
|
CorrelationQuery correlationQuery = CreateCorrelationQueryCore(select, correlationInitializers);
|
|||
|
|
|||
|
if (correlationQuery != null)
|
|||
|
{
|
|||
|
if (overridingAction != null)
|
|||
|
{
|
|||
|
correlationQuery.Where = new CorrelationActionMessageFilter { Action = overridingAction };
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
ProvideDefaultNamespace(ref serviceContractName);
|
|||
|
string defaultAction = NamingHelper.GetMessageAction(new XmlQualifiedName(serviceContractName.LocalName, serviceContractName.NamespaceName),
|
|||
|
operationName, null, isResponse);
|
|||
|
|
|||
|
correlationQuery.Where = new CorrelationActionMessageFilter { Action = defaultAction };
|
|||
|
}
|
|||
|
|
|||
|
queryCollection.Add(correlationQuery);
|
|||
|
|
|||
|
if (isResponse)
|
|||
|
{
|
|||
|
// we need an additional query with empty action to support soap1.1 reply cases
|
|||
|
CorrelationQuery noActionQuery = correlationQuery.Clone();
|
|||
|
noActionQuery.Where = new CorrelationActionMessageFilter { Action = String.Empty };
|
|||
|
queryCollection.Add(noActionQuery);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return queryCollection;
|
|||
|
}
|
|||
|
|
|||
|
static CorrelationQuery CreateCorrelationQueryCore(MessageQuerySet select, Collection<CorrelationInitializer> correlationInitializers)
|
|||
|
{
|
|||
|
CorrelationQuery correlationQuery = null;
|
|||
|
|
|||
|
if (select != null)
|
|||
|
{
|
|||
|
Fx.Assert(select.Count != 0, "Empty MessageQuerySet is not allowed!");
|
|||
|
|
|||
|
correlationQuery = new CorrelationQuery
|
|||
|
{
|
|||
|
Select = select
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
if (correlationInitializers != null && correlationInitializers.Count > 0)
|
|||
|
{
|
|||
|
foreach (CorrelationInitializer correlation in correlationInitializers)
|
|||
|
{
|
|||
|
QueryCorrelationInitializer queryCorrelation = correlation as QueryCorrelationInitializer;
|
|||
|
if (queryCorrelation != null)
|
|||
|
{
|
|||
|
Fx.Assert(queryCorrelation.MessageQuerySet.Count != 0, "Empty MessageQuerySet is not allowed!");
|
|||
|
|
|||
|
correlationQuery = correlationQuery ?? new CorrelationQuery();
|
|||
|
correlationQuery.SelectAdditional.Add(queryCorrelation.MessageQuerySet);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return correlationQuery;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|