Да, конечно, вы можете. По крайней мере, я достиг такого же уровня с Кандидатом на выпуск 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;
}