views:

312

answers:

2

I am working on a logging application that requires me to have a Workflow that is exposed as a Service (Workflow Service). We want to host it as a Windows Service (don't want to host workflow service as .svc file in IIS). Another reason for having it as windows service is to be able to communicate with the service through the Named pipes.

Can we expose a Workflow Service through Named Pipes without hosting it in IIS?

+1  A: 

Yes it is possible. You will have to create your own service. See Hosting and Consuming WCF Services on MSDN, especially the section Hosting in Windows Services.

Anders Abel
Thanks for your reply. I had the idea of this. I was mainly concerned if it's going to error out, break, throw exception if I try to host Workflow based WCF service using Windows Service. Will share my experience once I'm able to achieve this.
arsayed
+2  A: 

Yep bep, you sure can. At least, I have accomplished as much with Workflow 4 Release Candidate.

Consider,

// 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 ();
        }
    }
}

I believe endpoint configuration may be set via standard Wcf service configuration sections in App.config. I have not personally attempted a change to default transport layer in my experiments with Workflow.

The above represents a generic pure hosting class [ie it self-hosts WorkflowServices]. This allows us to re-use this hosting functionality within a console, WinForm, WPF, or yes, even a WindowsService application. Below is a WindowsService that leverages our host class

// 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 ();
    }
}

If you have fiddled with WorkflowServices in VS2010RC, then you may already know that WorkflowServices are not first class Xaml classes like their Workflow cousins. Instead, they are saved as loose Xaml files with the .xamlx extension. There is no design-time intellisense support for WorkflowServices [as far as I know] and are not recognized as declared types, so our only options to load a WorkflowService at run-time are

  • Read pure Xaml markup from .xamlx file directly
  • Read pure Xaml markup from some other source [embedded string, resource, or other source]

Either way, we must interpret markup and create a WorkflowService definition. The following will transform a string [that may be a filename or markup] into a WorkflowService. Keeners may also note that there is a difference between this process and the process for transforming Workflow markup to Workflow definitions.

// 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;
}
johnny g
Thanks Johnny for the detailed response. I'll try and see if it can take me somewhere. I was aware that there is a way to expose WCF as a windows service. My specific question was related to Workflow exposed as WCF to be hosted as Windows service. Logical assumption could be that once you have WCF in hand (regardless of its type), you should be able to host it as a windows service. Will try this approach to find out it it works with my requirements.
arsayed
no worries, but take a closer look. the solution presented == WindowsService + WF + WCF, is this not what you require?
johnny g
ack, added more context between sample code. is it somewhat more clear how this illustrates WindowsService hosted WorkflowServices via WCF?
johnny g