Windows Workflow Foundation: Calling WCF Services 

Having the ability to call services from inside your workflows is a good thing. Things get a little obfuscated when one wants to expose a WCF based service to Windows Workflow Foundation (WF).

What it comes down to is that currently (beta 2 build of WF) we do not have any built-in workflow activities which would allow you to communicate with the WCF advanced services (meaning WS-Security enabled, TCP based, WS-RM enabled, WS-Tx enabled, ...).

Assuming we have an OrderService exposed using the following config:

<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="Default">
          <security mode="None"/>
        </binding>
        <binding name="Secure">
          <security mode="Message">
            <message clientCredentialType="Windows"/>
          </security>
        </binding>
        <binding name="Reliable">
          <reliableSession enabled="true" ordered="true"/>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="WCFCalledByWorkflow.OrderService" >
        <endpoint address=""
                  binding="wsHttpBinding"
                  behaviorConfiguration="Default"
                  contract="WCFCalledByWorkflow.IOrderService" />
        <endpoint address="/secure"
                  binding="wsHttpBinding"
                  behaviorConfiguration="Secure"
                  contract="WCFCalledByWorkflow.IOrderService" />
        <endpoint address="/reliable"
                  binding="wsHttpBinding"
                  behaviorConfiguration="Reliable"
                  contract="WCFCalledByWorkflow.IOrderService" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

This, combined with the following hosting app:

using (ServiceHost sh = new ServiceHost(typeof(OrderService),
  
new Uri("http://localhost:666/OrderService")))
{
   sh.Open();
   Console.WriteLine("Order service running..\n");
   Console.WriteLine("Listening on:");
   foreach(ServiceEndpoint se in sh.Description.Endpoints)
   {
      Console.WriteLine(se.Address.ToString());
   }
   Console.WriteLine("\nPress [Enter] to stop the service.");
   Console.ReadLine();
   sh.Close();
}

Would produce the following output when run:

Order service running..

Listening on:
http://localhost:666/OrderService
http://localhost:666/OrderService/secure
http://localhost:666/OrderService/reliable

Press [Enter] to stop the service.

Now, this is not enough. Our service exposes three different endpoints, each one has different message requirements. And what is more important, all three are based on SOAP 1.2 + WS-Addressing.

There is a 'Default' endpoint which is plain vanilla SOAP 1.2 endpoint, without any security (have to mention it: WCF services are secure by default, one has to turn off security to achive this). Second endpoint uses Windows based message security and third turns on WS-RM. Security wise, second and third endpoints are the same (remember, defaults?).

As said, WF is currently not able to communicate with any of the above endpoints. What needs to be done is adding another endpoint to your service, which would expose it in ASMX compatible way.

The new config is this:

<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="Default">
          <security mode="None"/>
        </binding>
        <binding name="Secure">
          <security mode="Message">
            <message clientCredentialType="Windows"/>
          </security>
        </binding>
        <binding name="Reliable">
          <reliableSession enabled="true" ordered="true"/>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="WCFCalledByWorkflow.OrderService" >
        <endpoint address=""
                  binding="wsHttpBinding"
                  behaviorConfiguration="Default"
                  contract="WCFCalledByWorkflow.IOrderService" />
        <endpoint address="/secure"
                  binding="wsHttpBinding"
                  behaviorConfiguration="Secure"
                  contract="WCFCalledByWorkflow.IOrderService" />
        <endpoint address="/reliable"
                  binding="wsHttpBinding"
                  behaviorConfiguration="Reliable"
                  contract="WCFCalledByWorkflow.IOrderService" />
        <endpoint address="/legacy"
                  binding="basicHttpBinding"
                  contract="WCFCalledByWorkflow.IOrderService"/>
      </service>
    </services>
  </system.serviceModel>
</configuration>

So our Windows Workflow Foundation compatible endpoint is <base address>/legacy.

What this means is that you can bind all your InvokeWebService activities inside workflows to published WCF services by just adding another ASMX compatible endpoint to the WCF service.

The difference in two WCF configs is here:

<endpoint address="/legacy"
   binding="basicHttpBinding"
   contract="WCFCalledByWorkflow.IOrderService"
/>

Code sample can be downloaded here. It includes two projects, a WF and a WCF project. ServiceHost is implemented.

Categories:  .NET 3.0 - WCF | .NET 3.0 - WF
Wednesday, 31 May 2006 11:54:58 (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 On AJAX being dead 

A fellow MVP, Daniel Cazzulino, has a post titled AJAX may be the biggest waste of time for the web. While I agree with most of the points there, one should think about what Microsoft is doing to lower the AJAX development experience boundary.

Having to deal with JavaScript, raw (D)HTML and XML is definitely not going to scale from the developer penetration perspective. Nobody wants to do this is 2006. Therefore if Atlas guys make their magic happen, this would actually not be neccessary. It they achieve what they started, one would be abstracted from client side programming in most of the situations.

<atlas:UpdatePanel/> and <atlas:ScriptManager/> are your friends. And they could go a long way.

If this actually happens then we are actually discussing whether rich web based apps are more appropriate for the future web. There are scenarios that benefit from all these technologies, obviously. And if the industry concludes that DHTML with XmlHttpRequests is not powerful enough, who would stop the same model to produce rich WPF/E code from being emitted out of an Atlas enabled app.

We have, for the most part, been able to abstract the plumbing that is going on behind the scenes. If it's server side generated code, that should be running on a client, and if it is JavaScript, because all browsers run it, so be it.

We have swallowed the pill on the SOAP stacks already. We don't care if the communication starts with a SCT Request+Response messages, following by the key exchange. We do not care that a simple request-response model produces 15 messages while starting up. We do not care that there is raw XML being transfered. After all, it is all a fog, doing what it is supposed to do best - hiding the abstraction behind our beautiful SOAP/Services stack API.

Categories:  Other | Web Services | XML
Saturday, 27 May 2006 11:07:39 (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 NT Conference 2006 (NTK) - Distraction Overflow 

This year's NT conference has closed its doors. And it's a big event, this year around 2.150 attendees came. NTK is the biggest IT conference in Slovenia, hell; it's the biggest conference in Slovenia.

This is not without cause. Gathering 1 in 1000 from the complete country population is not simple - there has to be at least some additional fun present to pull this off.

As Dejan writes, some Microsoft partners went overboard this year. Having said that, one has to acknowledge that there are at least four profiles present at every conference:

  1. Those who get there to have fun
  2. Those who get there to have fun and learn a lot
  3. The speakers
  4. Other

Now, satisfying the first class of people is simple. Since NTK is a technical conference, one's expectation of a non-stop, 24 hour party is diminished by the fact that that is not appropriate for the conference of this caliber. These guys have fun with Nr. 2 and Nr. 3 guys during the conference evening events and make up their own things of interest between session hours.

Satisfying number 2 is harder. Anyone who wants to learn a lot and still have fun has some issues with the current agenda. There are fun things to visit during session hours and if you're a guy who wants to learn a lot, but still have fun, you have to decide what is more important. Now, why would someone want to make you, the paying customer, decision-ambiguous?

Number 3 can be satisfied by a couple of things. Technical readiness should be top-notch, and this year it was even better. The second thing speakers like is that their attendees are present and in good shape to follow the sessions. There should be no, or at least a minimum amount of distraction present during the session hours.

I am not discussing the 'Other' category, since its heterogeneous enough to make any relevant observations.

It is easy to see that a couple of speakers have some issues with the way things worked out. I am one of them.

Overall, NTK is one of the best Microsoft IT events in Europe. This year, it was just stunning - no major issues with the event organization, smooth transitions, and great evening events. If there is a solution to the problems raised, one would get the right quotient between pleasure and work.


 

Categories:  Conferences | Personal
Friday, 26 May 2006 22:23:41 (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Google goes AJAX: Positive addiction in the framework space 

Today, Google released Google Web Toolkit, which is an alternative to Microsoft's Atlas.

Indeed, Hell is freezing over.

Industry at large is competing for every piece of programming world. The idea of having ability to influence programmers into using your platform has become eligible for every vendor.

These situations are prevalent even on smaller markets. What we see is that companies are willing to offer their frameworks to big clients for a couple of reasons:

  • They can, because they own them (frameworks, that is)
  • They want to, because it is, remember, free to distribute (and hell to develop)
  • They want to, because addiction is goodtm

There is a special case of positive addiction present in the development world. I call it tool addiction, because it's actually not bound to a specific framework and/or platform version.

No one wants to use notepad.exe during development of a serious solution, right? We do need that Intellisense after all. Although it's just a bunch of programmatic schema definitions, one gets addicted to it. Platform vendors know this. This is the main reason tools are becoming free. The addiction flu is spreading out of the platform world, into the tool space, and as it seems to specific framework space.

Anyone who is offering anything for free has a background plan. They are not that stupid. Vendors know that once you get hooked it's not easy to be abstinent.

Categories:  Personal | Work
Wednesday, 17 May 2006 21:51:10 (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Windows Communication Foundation: negotiateServiceCredential attribute 

In WCF there's a knob which you can turn to configure service credential propagation semantics.

It's called negotiateServiceCredential and is present in bindings/<bindingOfChoice>/binding/security/message/@negotiateServiceCredential in the WCF configuration schema.

This would be a possible use of it, considering only the bindings section of the WCF configuration file:

<bindings>
   <wsHttpBinding>
      <binding name="MySecureBinding">
         <security mode ="Message">
            <message clientCredentialType="Certificate" negotiateServiceCredential="false"/>
         </security>
      </binding>
   </wsHttpBinding>
</bindings>

Due to the value of false, the specified config would mandate that the WCF clients need to obtain the service credential (in this case, an X.509 certificate) out of band. Out of band in this situation means that the client needs to have a service side certificate in one of its certificate stores.

If one would put negotiateServiceCredential="true" in the upper configuration file this would not be necessary. Indigo would start with a SPNego protocol to exchange the service credentials using startup messages. For the client side, this is good in certain situations, where you would not want to (or were unable to) store service side credentials on the client. The major drawback is that this forces you to use SPNego during initialization phase and it forces you to do it every time you start up the client.

If the clientCredentialType attribute equals to Anonymous, Username, or Certificate, setting this attribute to false implies that the client needs to define the serviceCertificate attribute. The following would be a valid config value for having clientCrendentialType="Certificate" (again, limiting it to only the behavior element:

<behavior name="MyBehavior">
   <serviceCredentials>
      <serviceCertificate
         x509FindType="FindBySubjectName"
         findValue="<My Certificate Subject>"
         storeLocation="LocalMachine"
         storeName="My"/>
   </serviceCredentials>
</behavior>

As a reminder, the default value of negotiateServiceCredential attribute is true.

To put all this into perspective, this is a possible WCF config file:

<system.serviceModel>
   
<bindings>
      <wsHttpBinding>
         <binding name="MySecureBinding">
            <security mode ="Message">
               <message clientCredentialType="Certificate" negotiateServiceCredential="false"/>
            </security>
         </binding>
      </wsHttpBinding>
   </bindings>
   
<behavior name="MyBehavior">
      <serviceCredentials>
         <serviceCertificate
            x509FindType="FindBySubjectName"
            findValue="MyCertSubject"
            storeLocation="LocalMachine"
            storeName="My"/>
      </serviceCredentials>
   </behavior>
   
<services>
      <service name="MyService" behaviorConfiguration="MyBehavior">
         <endpoint address="" binding="wsHttpBinding"
            
bindingConfiguration="MySecureBinding" contract="MyNamespace.MyContract"/>
      </service>
   </services>
</system.serviceModel>

This config file mandates that the client has service credentials available out of band. Specifically it should be available in the local machine certificate store. The certificate should have a substring of MyCertSubject inside its subject. If this is not the case, one would not be able to call the service successfully.

Categories:  .NET 3.0 - WCF
Saturday, 13 May 2006 22:56:04 (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Windows Workflow Foundation: Running Service Activated Workflows on Windows Vista and IIS7 

This post will focus on how to enable IIS7 in Windows Vista client and to use it to host a service activated Windows Workflow.

Procedure is based on the current build of Windows Vista (5381.1), which is a RC1 for Beta 2 milestone. Also, WinFX February CTP is used, which includes Windows Workflow Foundation Beta 2.

There are a couple of prerequisite steps necessary to enable hosting, first of all, installing IIS7. Go to Control Panel/Programs/Turn on or off Windows Features and enable 'Internet Information Services':

Add or remove Windows features

Installer in build 5381.1 (and 5365) is stable enough to be useful. If you're running a previous build of Vista (5308, 5342) consider installing IIS by running this monster in the command prompt:

start /w pkgmgr /l:log.etw /iu:IIS-WebServerRole;IIS-WebServer;IIS-CommonHttpFeatures;IIS-StaticContent;IIS-DefaultDocument;IIS-DirectoryBrowsing;IIS-HttpErrors;IIS-HttpRedirect;IIS-ApplicationDevelopment;IIS-ASPNET;IIS-NetFxExtensibility;IIS-ASP;IIS-CGI;IIS-ISAPIExtensions;IIS-ISAPIFilter;IIS-ServerSideIncludes;IIS-HealthAndDiagnostics;IIS-HttpLogging;IIS-LoggingLibraries;IIS-RequestMonitor;IIS-HttpTracing;IIS-CustomLogging;IIS-ODBCLogging;IIS-Security;IIS-BasicAuthentication;IIS-WindowsAuthentication;IIS-DigestAuthentication;IIS-ClientCertificateMappingAuthentication;IIS-IISCertificateMappingAuthentication;IIS-URLAuthorization;IIS-RequestFiltering;IIS-IPSecurity;IIS-Performance;IIS-HttpCompressionStatic;IIS-HttpCompressionDynamic;IIS-WebServerManagementTools;IIS-ManagementConsole;IIS-ManagementScriptingTools;IIS-ManagementService;IIS-IIS6ManagementCompatibility;IIS-Metabase;IIS-WMICompatibility;IIS-LegacyScripts;IIS-LegacySnapIn;IIS-FTPPublishingService;IIS-FTPServer;IIS-FTPManagement;WAS-WindowsActivationService;WAS-ProcessModel;WAS-NetFxEnvironment;WAS-ConfigurationAPI

Make sure you also check ASP.NET under World Wide Web Services/Application Development features, since this will install and enable ASP .NET 2.0 under all IIS7 sites. You can also do this later on using aspnet_regiis.exe, but Vista will notify you that the preferred way is using Turn on or off Windows features dialog.

Now, when you have IIS installed run the administrative console inside Administrative Tools and define a web application by right clicking on Default Web Site:

Creating application on IIS7

This will allow you to run your workflow as a service inside the default application pool. You can check and notice that default application pool uses a new integrated IIS7 mode and not ISAPI as in IIS5/6.

You're ready to deploy your workflow activated service now. use the steps described in my previous post, under Ad 1.

When you hit the service endpoint you get this:

Configuration error in IIS7

IIS7 is noticing you that your config files are not compatible with the new hosting model.

You have two options:

  • Change the configuration files
  • Change the hosting model

You can change the configuration files by running: c:\windows\system32\inetsrv\appcmd.exe migrate config "<Site name>/<VRoot name>". AppCmd.exe is a tool which automatically migrates your old config, to IIS7's new config format.

Another option is that you enable old style ISAPI hosting model inside your application pool that is running your default web site (or another site, if that's what the workflow is supposed to be running under). You can do this either by:

1. Running c:\windows\system32\inetsrv\appcmd.exe set app "<Site name>/<VRoot name>" /applicationPool: "Classic .NET AppPool". This changes the site to use another, preconfigured app pool, which uses ISAPI by default.

Here's a screenshot of the default pipeline modes for IIS7:

Application pool config in IIS7

2. Changing the hosting model on the current Default Web Site site. You can right click on Application Pools/DefaultAppPool and select Set Application Pool Defaults. Then you change the pipeline mode from Integrated to ISAPI. Here's how you do it:

Pipeline mode selection

I prefer going through route 1. Integrated mode is how you should be running your sites under IIS7, so changing the config to make IIS7 happy is the way to go. If you have specific ISAPI functionality (not limited to Workflows) you can, though run in classic mode by designing your app pool around it.

Now your service activated workflow will run and execute under IIS7. Again, beware of the caveats I described here.

Categories:  Web Services | Windows Vista | .NET 3.0 - WF
Thursday, 11 May 2006 11:15:46 (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Windows Workflow Foundation: Exposing Workflows as Services 

There are currently a couple of options to expose a Windows Workflow as as service.

  1. There is a native option to publish a developed Workflow Library project as a ASP .NET Web Service (ASMX).
  2. You can host it yourself (ASMX, WCF)
  3. William Tay is doing excellent work towards hosting a workflow inside the WCF service pipeline (WCF)
  4. Roman Kiss created a static WorkflowInvoker class, which does all the heavy liting for you, if you want to host your workflow inside the WCF service method (WCF)

I'm going to focus on Ad 1 and Ad 2 in this post.

Ad 1:

There's an option to host your workflow library inside a web service by using a "Publish as a Web Service" option inside Visual Studio 2005. This creates a separate ASP .NET Web Service project inside your current solution, which you can later manually or automatically publish as a web site to your IIS of choice.

The are two major downsides to this story. The first is that this gives you practically no control over how the web service is created. Second downside, while documented, is that the current implementation of System.Workflow.Runtime.WorkflowWebHostingModule works in particular ways with the workflow persistence story.

Let's assume we have to following interface defined for this web service:

interface IServiceInterface
{
   void SendOrder(Order order);
   Order GetOrder(Guid guidOrder);
   int GetOrderStatus(Guid guidOrder);
}

What happens is (request number 1):

  1. You publish your workflow as a web service
  2. You hit the service endpoint with a browser
  3. Workflow instance gets created, is run and returns a result
  4. At this time the workflow runtime (System.Workflow.Runtime.WorkflowRuntime instance) creates a workflow instance and runs it. Since workflow completes succesfully it destroys the instance at the end of execution.
  5. Workflow runtime returns a cookie with the workflow instance back to the browser and since IE's default setting is to accept cookies, it is written to the client's disk

All good, right?

Actually, what happens during request number 2?

  1. You hit the endpoint again
  2. IE knows that the site has a persisted cookie, so it sends it bundled with the SOAP request
  3. Workflow runtime sees it and tries to load the specified workflow instance
  4. This instance is long gone, it does not exist in memory (it has been destroyed, remember?), so workflow runtime tries to rehydrate it from a persistence store. If there is a persistence store defined it goes there (most probably WorkflowPersistenceStore in SQL Server) and correctly identifies that the workflow instance is not present, so it fails with 'Workflow with id <GUID> not found in state persistence store.'. If the persistence store is not defined for this workflow it fails with 'The workflow hosting environment does not have a persistence service as required by an operation on the workflow instance <GUID>.'.

And all this is actually the expected behavior if you think hard enough. Workaround? Hit the endpoint with a newly loaded IE window. It works every time, since a cookie with an instance ID is not present.

Another thing to mention here is that this issue does not manifest itself if you hit the endpoint programatically using the web service proxy, unless you are using a CookieContainer class to cache the returning cookies.

Ad 2:

Hosting a Windows Workflow manually is another option, which gives you more flexibility towards the service detail tweeking.

You can host it using the following code:

[WebService(Namespace = "http://webservices.gama-system.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WorkflowService : System.Web.Services.WebService
{
    // workflow runtime
    private static WorkflowRuntime workflowRuntime = new WorkflowRuntime();
    
    [WebMethod]
    public void SendOrder(Order order)
    {
        AutoResetEvent waitHandle = new AutoResetEvent(false);
        workflowRuntime.WorkflowCompleted +=
           delegate(object sender, WorkflowCompletedEventArgs e)
        {
            waitHandle.Set();
        };
       
        workflowRuntime.WorkflowTerminated +=
           delegate(object sender, WorkflowTerminatedEventArgs e)
        {
            waitHandle.Set();
        };

        // create workflow instance with the specified parameters
        WorkflowInstance instance =
           workflowRuntime.CreateWorkflow(typeof(MyWorkflow));
        instance.Start();

        waitHandle.WaitOne();
    }   
}

An important thing in the specified sample is that the System.Workflow.Runtime.WorkflowRuntime instance is static to the service implementation class. This is a requirement, since the workflow runtime can only get loaded once per appdomain. If this is not the case you will get an exception during the second invocation of the workflow.

If you are using any additional workflow runtime services, like persistence, tracking or your own communication service to communicate with the workflow you will need to track that the services get loaded once only. Here's the example:

[WebService(Namespace = "http://webservices.gama-system.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WorkflowService : System.Web.Services.WebService
{
    // workflow runtime
    private static WorkflowRuntime workflowRuntime = new WorkflowRuntime();
   
   
// services added
    private static bool booServicesAdded = false;

    // communication service
    private static CommunicationService objComm = new CommunicationService();

    [WebMethod]
    public void SendOrder(Order order)
    {
        // add communication service
        if (!booServicesAdded)
        {
            ExternalDataExchangeService externalService =
               new ExternalDataExchangeService();
            workflowRuntime.AddService(externalService);
            externalService.AddService(objComm);
            booServiceAdded = true;
        }

        AutoResetEvent waitHandle = new AutoResetEvent(false);
        workflowRuntime.WorkflowCompleted +=
           delegate(object sender, WorkflowCompletedEventArgs e)
        {
            waitHandle.Set();
        };
       
        workflowRuntime.WorkflowTerminated +=
           delegate(object sender, WorkflowTerminatedEventArgs e)
        {
            waitHandle.Set();
        };

        // create workflow instance with the specified parameters
        WorkflowInstance instance =
           workflowRuntime.CreateWorkflow(typeof(MyWorkflow));
        instance.Start();

        waitHandle.WaitOne();
    }   
}

This adds the required services only during the first invocation of a web service. Since workflow runtime is a static class the services get persisted during all subsequent service calls. A boolean variable booServicesAdded is responsible for flag storage.

Categories:  Web Services | .NET 3.0 - WCF | .NET 3.0 - WF
Wednesday, 10 May 2006 09:55:37 (Central Europe Standard Time, UTC+01:00)  #    Comments

 

Copyright © 2003-2024 , Matevž Gačnik
Recent Posts
RD / MVP
Feeds
RSS: Atom:
Archives
Categories
Blogroll
Legal

The opinions expressed herein are my own personal opinions and do not represent my company's view in any way.

My views often change.

This blog is just a collection of bytes.

Copyright © 2003-2024
Matevž Gačnik

Send mail to the author(s) E-mail