Механизм размещения рабочих процессов, использующий несколько экземпляров WorkflowServiceHost, создаваемых динамически - PullRequest
1 голос
/ 11 июля 2011

Я пытаюсь добиться следующего.Я создал долго работающие рабочие процессы, которые просыпаются с запланированным интервалом, т.е. задерживают активность и выполняют свою работу, заполняют кэш.Эти рабочие процессы запускаются из основного приложения, которое представляет собой пользовательскую службу данных WCF, размещенную на IIS 7.Триггер для запуска рабочих процессов различается в зависимости от входа пользователя в систему, запроса на конкретную сущность.

В настоящее время я создал службу рабочего потока WCF, которая прослушивает триггеры, являющиеся частью стандартного контракта запроса.Получив триггер, он создает приложение WorkflowApplication с определением действия и запускает рабочий процесс.Это, однако, имеет свои ограничения, так как постоянство и регидратацию нужно выполнять вручную.

Я натолкнулся на образец WorkflowServiceHost, который может выступать в качестве хоста, который поддерживает постоянство и регидратацию в случае сбоя, посколькусреда веб-фермы.Но, когда я попытался сделать что-то вроде ниже, это не удается с различными ошибками.Так что просто хотел знать, иду ли я в правильном направлении.ИЛИ это невозможно.Пожалуйста, найдите код реализации контракта IWorkflowCreation, основанный на образце MSDN:

`  public Guid StartWorkflowReturnGuid(Dictionary<string,object> parameters)
        {
            Guid id = Guid.Empty;
            try
            {


            System.ServiceModel.Activities.WorkflowServiceHost host = new System.ServiceModel.Activities.WorkflowServiceHost(this.workflow, new Uri("net.pipe://localhost"));

            //string myConnectionString =
            //   "Data Source=localhost\\SQLEXPRESS;Initial Catalog=DefaultSampleStore;Integrated Security=True;Asynchronous Processing=True";
            //SqlWorkflowInstanceStoreBehavior sqlWorkflowInstanceStoreBehavior =
            //    new SqlWorkflowInstanceStoreBehavior(myConnectionString);
            //sqlWorkflowInstanceStoreBehavior.HostLockRenewalPeriod = TimeSpan.FromSeconds(30);
            //sqlWorkflowInstanceStoreBehavior.RunnableInstancesDetectionPeriod = TimeSpan.FromSeconds(30);
            //host.Description.Behaviors.Add(sqlWorkflowInstanceStoreBehavior);

            WorkflowUnhandledExceptionBehavior workflowUnhandledExceptionBehavior =
                new WorkflowUnhandledExceptionBehavior();

            workflowUnhandledExceptionBehavior.Action = WorkflowUnhandledExceptionAction.Terminate;

            //Set the TimeToUnload to 0 to force the WF to be unloaded. To have a durable delay, the WF needs to be unloaded otherwise it will be thread as an in-memory delay.
            //WorkflowIdleBehavior workflowIdleBehavior = new WorkflowIdleBehavior()
            //{
            //    TimeToUnload = TimeSpan.FromSeconds(0)
            //};
            //host.Description.Behaviors.Add(workflowIdleBehavior);
            host.WorkflowExtensions.Add(new LoginExtensions());
            host.WorkflowExtensions.Add(new MarketDataExtensions());
            ResumeBookmarkEndpoint endpoint = new ResumeBookmarkEndpoint(new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), new EndpointAddress(string.Format("net.pipe://localhost/workflowCreationEndpoint/{0}",Guid.NewGuid())));
            host.AddServiceEndpoint(endpoint);
            host.Open();
            IWorkflowCreation client = new ChannelFactory<IWorkflowCreation>(endpoint.Binding, endpoint.Address).CreateChannel();
            //create an instance
            id = client.Create(parameters);
            Helper.LogInfo(string.Format("Workflow instance {0} created", id));
            }
            catch (Exception exception)
            {

                Helper.LogError(string.Format("Exception starting BatchWorkflowServiceHost for type {0}", this.workflow.GetType().FullName), exception);
            }
            return id;
        }
public class ResumeBookmarkEndpoint : WorkflowHostingEndpoint
{

    public ResumeBookmarkEndpoint(Binding binding, EndpointAddress address)
        : base(typeof(IWorkflowCreation), binding, address)
    {
    }

    protected override Guid OnGetInstanceId(object[] inputs, OperationContext operationContext)
    {
        //Create called
        if (operationContext.IncomingMessageHeaders.Action.EndsWith("Create"))
        {
            return Guid.Empty;
        }
        //CreateWithInstanceId or ResumeBookmark called. InstanceId is specified by client
        else if (operationContext.IncomingMessageHeaders.Action.EndsWith("CreateWithInstanceId")||
                operationContext.IncomingMessageHeaders.Action.EndsWith("ResumeBookmark"))
        {
            return (Guid)inputs[0];
        }
        else
        {
            throw new InvalidOperationException("Invalid Action: " + operationContext.IncomingMessageHeaders.Action);
        }
    }


    protected override WorkflowCreationContext OnGetCreationContext(object[] inputs, OperationContext operationContext, Guid instanceId, WorkflowHostingResponseContext responseContext)
    {
        WorkflowCreationContext creationContext = new WorkflowCreationContext();
        if (operationContext.IncomingMessageHeaders.Action.EndsWith("Create"))
        {
            Dictionary<string, object> arguments = (Dictionary<string, object>)inputs[0];
            if (arguments != null && arguments.Count > 0)
            {
                foreach (KeyValuePair<string, object> pair in arguments)
                {
                    //arguments for the workflow
                    creationContext.WorkflowArguments.Add(pair.Key, pair.Value);
                }
            }
            //reply to client with the InstanceId
            responseContext.SendResponse(instanceId, null);
        }
        else if (operationContext.IncomingMessageHeaders.Action.EndsWith("CreateWithInstanceId"))
        {
            Dictionary<string, object> arguments = (Dictionary<string, object>)inputs[0];
            if (arguments != null && arguments.Count > 0)
            {
                foreach (KeyValuePair<string, object> pair in arguments)
                {
                    //arguments for the workflow
                    creationContext.WorkflowArguments.Add(pair.Key, pair.Value);
                }
            }
        }
        else
        {
            throw new InvalidOperationException("Invalid Action: " + operationContext.IncomingMessageHeaders.Action);
        }
        return creationContext;
    }
    protected override System.Activities.Bookmark OnResolveBookmark(object[] inputs, OperationContext operationContext, WorkflowHostingResponseContext responseContext, out object value)
    {
        Bookmark bookmark = null;
        value = null;
        if (operationContext.IncomingMessageHeaders.Action.EndsWith("ResumeBookmark"))
        {
            //bookmark name supplied by client as input to IWorkflowCreation.ResumeBookmark
            bookmark = new Bookmark((string)inputs[1]);
            //value supplied by client as argument to IWorkflowCreation.ResumeBookmark
            value = (string) inputs[2];
        }
        else
        {
            throw new NotImplementedException(operationContext.IncomingMessageHeaders.Action);
        }
        return bookmark;
    }
}

//ServiceContract exposed on the endpoint
[ServiceContract(Name = "IWorkflowCreation")]
public interface IWorkflowCreation
{
    [OperationContract(Name = "Create")]
    Guid Create(IDictionary<string, object> inputs);

    [OperationContract(Name = "CreateWithInstanceId", IsOneWay=true)]
    void CreateWithInstanceId(Guid instanceId, IDictionary<string, object> inputs);

    [OperationContract(Name = "ResumeBookmark", IsOneWay = true)]
    void ResumeBookmark(Guid instanceId, string bookmarkName, string message);

}

1 Ответ

1 голос
/ 11 июля 2011

То, что вы пытаетесь сделать, безусловно, возможно.Однако ваш вопрос вызывает вопросы.Вы говорите, что у вас есть служба рабочего процесса WCF, что означает, что вы уже используете WorkflowServiceHost.

Что касается использования WorkfowServiceHost.Использование IWorkflowCreation для самостоятельного создания и управления рабочими процессами возможно, есть несколько примеров того, что делать, но обычный способ - использовать инфраструктуру WCF-WF4 для создания рабочих процессов.Создайте рабочий процесс, добавьте действие Receive и установите для параметра CanCreateInstance значение true, и отправка запроса WCF этому действию создаст для вас новый экземпляр рабочего процесса.

Если вы хотите использовать WorklflowApplication с сохранением, которое также возможноно тогда вы должны управлять каждым экземпляром самостоятельно.Автоматическая перезагрузка отсутствует, как это может быть, если вам нужно управлять отдельными экземплярами, поэтому вы проверяете таймеры с истекшим сроком действия и возобновляете экземпляры.Отработка отказа будет работать так же хорошо, он использует тот же SqlWorkflowInstanceStore для фактического сохранения и загрузки состояния рабочего процесса.

...