File: System\ServiceModel\Activities\WorkflowHostingEndpoint.cs
Project: ndp\cdf\src\NetFx40\System.ServiceModel.Activities\System.ServiceModel.Activities.csproj (System.ServiceModel.Activities)
//----------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//----------------------------------------------------------------
 
namespace System.ServiceModel.Activities
{
    using System.Activities;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Diagnostics.CodeAnalysis;
    using System.Globalization;
    using System.Runtime;
    using System.ServiceModel.Activities.Description;
    using System.ServiceModel.Activities.Dispatcher;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
 
    public abstract class WorkflowHostingEndpoint : ServiceEndpoint
    {
        Collection<CorrelationQuery> correlationQueries;
 
        protected WorkflowHostingEndpoint(Type contractType)
            : this(contractType, null, null)
        {
        }
 
        protected WorkflowHostingEndpoint(Type contractType, Binding binding, EndpointAddress address)
            : base(ContractDescription.GetContract(contractType), binding, address)
        {
            this.IsSystemEndpoint = true;
            this.Contract.Behaviors.Add(new ServiceMetadataContractBehavior(false));
            this.Contract.Behaviors.Add(new WorkflowHostingContractBehavior());
            Fx.Assert(!this.Behaviors.Contains(typeof(CorrelationQueryBehavior)), "Must not contain correlation query!");
            this.correlationQueries = new Collection<CorrelationQuery>();
            this.Behaviors.Add(new CorrelationQueryBehavior(this.correlationQueries));
 
            // If TransactionFlowOption.Allowed or TransactionFlowOption.Mandatory is defined on an operation, we will set 
            // TransactionScopeRequired = true for that operation.  The operation will become transacted (use transaction flow, 
            // or create one locally).  For usability reason, we assume this is the majority usage.  User could opt out by 
            // setting TransactionScopeRequired to false or remove the TransactionFlowAttribute from the operation.
            foreach (OperationDescription operationDescription in this.Contract.Operations)
            {
                TransactionFlowAttribute transactionFlow = operationDescription.Behaviors.Find<TransactionFlowAttribute>();
                if (transactionFlow != null && transactionFlow.Transactions != TransactionFlowOption.NotAllowed)
                {
                    OperationBehaviorAttribute operationAttribute = operationDescription.Behaviors.Find<OperationBehaviorAttribute>();
                    operationAttribute.TransactionScopeRequired = true;
                }
            }
        }
 
        public Collection<CorrelationQuery> CorrelationQueries
        {
            get { return this.correlationQueries; }
        }
 
        // There are two main scenario that user will override this api.
        // - For ResumeBookmark, User explicitly put or know how to extract InstanceId from Message.  This enables user to provide
        //   customized and lighter-weight (no InstanceKeys indirection) correlation.
        // - For Workflow Creation, User could provide a preferred Id for newly created Workflow Instance.
        protected internal virtual Guid OnGetInstanceId(object[] inputs, OperationContext operationContext)
        {
            return Guid.Empty;
        }
 
        protected internal virtual WorkflowCreationContext OnGetCreationContext(
            object[] inputs, OperationContext operationContext,
            Guid instanceId, WorkflowHostingResponseContext responseContext)
        {
            return null;
        }
 
        [SuppressMessage(FxCop.Category.Design, FxCop.Rule.AvoidOutParameters,
            Justification = "By design, return both Bookmark and its payload.")]
        protected internal virtual Bookmark OnResolveBookmark(object[] inputs, OperationContext operationContext,
            WorkflowHostingResponseContext responseContext, out object value)
        {
            value = null;
            return null;
        }
 
        internal static FaultException CreateDispatchFaultException()
        {
            FaultCode code = new FaultCode(FaultCodeConstants.Codes.InternalServiceFault, FaultCodeConstants.Namespaces.NetDispatch);
            code = FaultCode.CreateReceiverFaultCode(code);
            MessageFault dispatchFault = MessageFault.CreateFault(code,
                    new FaultReason(new FaultReasonText(SR.InternalServerError, CultureInfo.CurrentCulture)));
            return new FaultException(dispatchFault, FaultCodeConstants.Actions.NetDispatcher);
        }
 
        class WorkflowHostingContractBehavior : IContractBehavior
        {
            public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
            {
            }
 
            public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
            }
 
            public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
            {
                Fx.Assert(endpoint is WorkflowHostingEndpoint, "Must be hosting endpoint!");
                foreach (OperationDescription operation in contractDescription.Operations)
                {
                    if (operation.Behaviors.Find<WorkflowHostingOperationBehavior>() == null)
                    {
                        operation.Behaviors.Add(new WorkflowHostingOperationBehavior());
                    }
                }
            }
 
            public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
            {
                Fx.Assert(endpoint is WorkflowHostingEndpoint, "Must be hosting endpoint!");
            }
 
            class WorkflowHostingOperationBehavior : WorkflowOperationBehavior
            {
                public WorkflowHostingOperationBehavior()
                    : base(true)
                {
                }
 
                protected internal override Bookmark OnResolveBookmark(WorkflowOperationContext context, out BookmarkScope bookmarkScope, out object value)
                {
                    CorrelationMessageProperty correlationMessageProperty;
                    if (CorrelationMessageProperty.TryGet(context.OperationContext.IncomingMessageProperties, out correlationMessageProperty))
                    {
                        bookmarkScope = new BookmarkScope(correlationMessageProperty.CorrelationKey.Value);
                    }
                    else
                    {
                        bookmarkScope = null;
                    }
 
                    WorkflowHostingResponseContext responseContext = new WorkflowHostingResponseContext(context);
                    Fx.Assert(context.ServiceEndpoint is WorkflowHostingEndpoint, "serviceEnpoint must be of WorkflowHostingEndpoint type!");
                    Bookmark bookmark = ((WorkflowHostingEndpoint)context.ServiceEndpoint).OnResolveBookmark(context.Inputs, context.OperationContext, responseContext, out value);
                    if (bookmark == null)
                    {
                        throw FxTrace.Exception.AsError(CreateDispatchFaultException());
                    }
                    return bookmark;
                }
            }
        }
    }
}