Можем ли мы разместить Сервис Workflow как Сервис Windows? - PullRequest
4 голосов
/ 30 марта 2010

Я работаю над приложением для ведения журнала, которое требует, чтобы у меня был рабочий процесс, который отображается как служба (служба рабочего процесса). Мы хотим разместить его как службу Windows (не хотим размещать службу рабочего процесса как файл .svc в IIS). Еще одна причина, по которой он используется в качестве службы Windows, заключается в возможности связи со службой через именованные каналы.

Можем ли мы предоставить сервис Workflow через именованные каналы, не размещая его в IIS?

Ответы [ 2 ]

5 голосов
/ 31 марта 2010

Да, конечно, вы можете. По крайней мере, я достиг такого же уровня с Кандидатом на выпуск Workflow 4.

Рассмотрим,

// a generic self-hosted workflow service hosting thingy. Actual
// implementation should contain more logging and thread safety, this
// is an abbreviated version ;)
public class WorkflowHost
{

    // NOTE: with Workflow, it helps to maintain a concept of
    // Workflow definition [the Activity or WorkflowService from
    // a designer] and a Workflow instance [what is running within
    // WorkflowInvoker, WorkflowApplication, WorkflowServiceHost].
    // a definition may be used to generate an instance. an instance
    // contains run-time state and cannot be recycled into a new
    // instance. therefore, to repeatedly re-host a WorkflowService
    // we need to maintain references to original definitions and
    // actual instances. ergo services and hosts maps
    // 
    // if you are special purpose and require support for one and 
    // only one service and endpoint\uri, then you may reduce this 
    // to a simple tuple of Uri, WorkflowService, WorkflowServiceHost

    // services represents a definition of hosted services
    private readonly Dictionary<Uri, WorkflowService> _services = 
        new Dictionary<Uri, WorkflowService> ();

    // hosts represents actual running instances of services
    private readonly Dictionary<Uri, WorkflowServiceHost> _hosts = 
        new Dictionary<Uri, WorkflowServiceHost> ();

    // constructor accepts a map of Uris (ie service endpoints) to
    // workflow service definitions
    public WorkflowHost (IDictionary<Uri, WorkflowService> services)
    {
        foreach (KeyValuePair<Uri, WorkflowService> servicePair in services)
        {
            _services.Add (servicePair.Key, servicePair.Value);
        }
    }

    // have your windows service invoke this to start hosting
    public void Start ()
    {
        if (_hosts.Count > 0)
        {
            Stop ();
        }

        foreach (KeyValuePair<Uri, WorkflowService> servicePair in _services)
        {
            WorkflowService service = servicePair.Value;
            Uri uri = servicePair.Key;
            WorkflowServiceHost host = new WorkflowServiceHost (service, uri);

            host.Open ();

            _hosts.Add (uri, host);
        }
    }

    // have your windows service invoke this to stop hosting
    public void Stop ()
    {
        if (_hosts.Count > 0)
        {
            foreach (KeyValuePair<Uri, WorkflowService> servicePair in 
                _services)
            {
                WorkflowService service = servicePair.Value;
                Uri uri = servicePair.Key;

                IDisposable host = _hosts[uri];
                host.Dispose ();
            }

            _hosts.Clear ();
        }
    }
}

Я полагаю, что конфигурация конечной точки может быть установлена ​​через стандартные разделы конфигурации сервиса Wcf в App.config. Я лично не пытался изменить транспортный уровень по умолчанию в моих экспериментах с Workflow.

Выше представлен общий класс чистого хостинга [то есть он самостоятельно размещает WorkflowServices]. Это позволяет нам повторно использовать эту функцию хостинга в консоли, WinForm, WPF или, да, даже в приложении WindowsService. Ниже приводится служба WindowsService, которая использует наш хост-класс

// windows service. personally i would abstract service behind
// an interface and inject it, but again, for brevity ;)
public partial class WorkflowWindowsService : ServiceBase
{
    WorkflowHost _host;

    public WorkflowWindowsService ()
    {
        InitializeComponent();

        Dictionary<Uri, WorkflowService> services = 
            new Dictionary<Uri, WorkflowService> ();

        // do your service loading ...

        // create host
        _host = new WorkflowHost (services);
    }

    protected override void OnStart(string[] args)
    {
        _host.Start ();
    }

    protected override void OnStop()
    {
        _host.Stop ();
    }
}

Если вы работали с WorkflowServices в VS2010RC, то вы, возможно, уже знаете, что WorkflowServices не являются классами Xaml первого класса, как их собратья Workflow. Вместо этого они сохраняются как свободные файлы Xaml с расширением .xamlx. Для WorkflowServices не существует поддержки IntelliSense во время разработки [насколько я знаю], и они не распознаются как объявленные типы, поэтому единственными вариантами загрузки WorkflowService во время выполнения являются

  • Считывание чистой разметки Xaml из файла .xamlx напрямую
  • Чтение чистой разметки Xaml из другого источника [встроенная строка, ресурс или другой источник]

В любом случае, мы должны интерпретировать разметку и создать определение WorkflowService. Следующее преобразует строку [которая может быть именем файла или разметкой] в WorkflowService. Кинерс может также отметить, что существует разница между этим процессом и процессом преобразования разметки рабочего процесса в определения рабочего процесса.

// converts a string value [either pure xaml or filename] to a
// WorkflowService definition
public WorkflowService ToWorkflowService (string value)
{
    WorkflowService service = null;

    // 1. assume value is Xaml
    string xaml = value;

    // 2. if value is file path,
    if (File.Exists (value))
    {
        // 2a. read contents to xaml
        xaml = File.ReadAllText (value);
    }

    // 3. build service
    using (StringReader xamlReader = new StringReader (xaml))
    {
        object untypedService = null;

        // NOTE: XamlServices, NOT ActivityXamlServices
        untypedService = XamlServices.Load (xamlReader);

        if (untypedService is WorkflowService)
        {
            service = (WorkflowService)(untypedService);
        }
        else
        {
            throw new ArgumentException (
                string.Format (
                "Unexpected error reading WorkflowService from " + 
                "value [{0}] and Xaml [{1}]. Xaml does not define a " + 
                "WorkflowService, but an instance of [{2}].", 
                value, 
                xaml, 
                untypedService.GetType ()));
        }
    }

    return service;
}
1 голос
/ 31 марта 2010

Да, это возможно. Вам нужно будет создать свой собственный сервис. См. Хостинг и использование служб WCF на MSDN, особенно раздел Хостинг в службах Windows .

...