Bleeding Edge 2008: Postback 

I'm sorry it took a week, but here we go.

This is my exit content from Bleeding Edge 2008. I'm also posting complete conference contents, just in case.

Thanks go out to Dušan, Dejan, Miha, Miha and Miha.

Downloads:

Remark: PPT in Slovene only. Code international.

Thank you for attending. Hope to see you next year!

Categories:  CLR | Conferences | Microsoft | Web Services | Work
Thursday, October 09, 2008 8:42:02 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 WCF: Passing Collections Through Service Boundaries, Why and How 

In WCF, collection data that is passed through the service boundary goes through a type filter - meaning you will not necessarily get the intrinsic service side type on the client, even if you're expecting it.

No matter if you throw back an int[] or List<int>, you will get the int[] by default on the client.

The main reason is that there is no representation for System.Collections.Generic.List or System.Collection.Generic.LinkedList in service metadata. The concept of System.Collection.Generic.List<int> for example, actually does not have a different semantic meaning from an integer array - it's still a list of ints - but will allow you to program against it with ease.

Though, if one asks nicely, it is possible to guarantee the preferred collection type on the client proxy in certain scenarios.

Unidimensional collections, like List<T>, LinkedList<T> or SortedList<T> are always exposed as T arrays in the client proxy. Dictionary<K, V>, though, is regened on the client via an annotation hint in WSDL (XSD if we are precise). More on that later.

Let's look into it.

WCF infrastructure bends over backwards to simplify client development. If the service side contains a really serializable collection (marked with [Serializable], not [DataContract]) that is also concrete (not an interface), and has an Add method with the following signatures...

public void Add(object obj);
public void Add(T item);

... then WCF will serialize the data to an array of the collections type.

Too complicated? Consider the following:

[ServiceContract]
interface ICollect
{
   [OperationContract]
   public void AddCoin(Coin coin);

   [OperationContract]
   public List<Coin> GetCoins();
}

Since the List<T> supports a void Add<T> method and is marked with [Serializable], the following wire representation will be passed to the client:

[ServiceContract]
interface ICollect
{
  [OperationContract]
  void AddCoin(Coin coin);

  [OperationContract]
  Coin[] GetCoins();
}

Note: Coin class should be marked either with a [DataContract] or [Serializable] in this case.

So what happens if one wants the same contract on the client proxy and the service? There is an option in the WCF proxy generator, svcutil.exe to force generation of class definitions with a specific collection type.

Use the following for List<T>:

svcutil.exe http://service/metadata/address
  /collectionType:System.Collections.Generic.List`1

Note: List`1 uses back quote, not normal single quote character.

What the /collectionType (short /ct) does, is forces generation of strongly typed collection types. It will generate the holy grail on the client:

[ServiceContract]
interface ICollect
{
  [OperationContract]
  void AddCoin(Coin coin);

  [OperationContract]
  List<Coin> GetCoins();
}

In Visual Studio 2008, you will even have an option to specify which types you want to use as collection types and dictionary collection types, as in the following picture:

On the other hand, dictionary collections, as in System.Collections.Generic.Dictionary<K, V> collections, will go through to the client no matter what you specify as a /ct parameter (or don't at all).

If you define the following on the service side...

[OperationContract]
Dictionary<string, int> GetFoo();

... this will get generated on the client:

[OperationContract]
Dictionary<string, int> GetFoo();

Why?

Because using System.Collections.Generic.Dictionary probably means you know there is no guarantee that client side representation will be possible if you are using an alternative platform. There is no way to meaningfully convey the semantics of a .NET dictionary class using WSDL/XSD.

So, how does the client know?

In fact, the values are serialized as joined name value pair elements as the following schema says:

<xs:complexType name="ArrayOfKeyValueOfstringint">
  <xs:annotation>
    <xs:appinfo>
      <IsDictionary
        xmlns="
http://schemas.microsoft.com/2003/10/Serialization/">
        true
      </IsDictionary>
    </xs:appinfo>
  </xs:annotation>
  <xs:sequence>
    <xs:element minOccurs="0" maxOccurs="unbounded"
      name="KeyValueOfstringint">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="Key" nillable="true" type="xs:string" />
          <xs:element name="Value" type="xs:int" />
        </xs:sequence>
      </xs:complexType>
    </xs:element>
  </xs:sequence>
</xs:complexType>
<xs:element name="ArrayOfKeyValueOfstringint"
  nillable="true" type="tns:ArrayOfKeyValueOfstringint" />

Note: You can find this schema under types definition of the metadata endpoint. Usually ?xsd=xsd2, instead of ?wsdl will suffice.

As in:

<GetFooResponse>
  <KeyValueOfstringint>
    <Key>one</Key>
    <Value>1</Value>

    <Key>two</Key>
    <Value>2</Value>

    <!-- ... -->

    <Key>N</Key>
    <Value>N</Value>
  </KeyValueOfstringint>
<GetFooResponse>

The meaningful part of type service-to-client-transportation resides in <xs:annotation> element, specifically in /xs:annotation/xs:appinfo/IsDictionary element, which defines that this complex type represents a System.Collections.Generic.Dictionary class. Annotation elements in XML Schema are parser specific and do not convey any structure/data type semantics, but are there for the receiver to interpret.

This must be one of the most excellent school cases of using XML Schema annotations. It allows the well-informed client (as in .NET client, VS 2008 or svcutil.exe) to utilize the semantic meaning if it understands it. If not, no harm is done since the best possible representation, in a form of joined name value pairs still goes through to the client.

Categories:  .NET 3.0 - WCF | .NET 3.5 - WCF | Web Services
Thursday, September 27, 2007 10:04:47 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Approaches to Document Style Parameter Models 

I'm a huge fan of document style parameter models when implementing a public, programmatic façade to a business functionality that often changes.

public interface IDocumentParameterModel
{
   [OperationContract]
   [FaultContract(typeof(XmlInvalidException))]
   XmlDocument Process(XmlDocument doc);
}

This contract defines a simple method, called Process, which processes the input document. The idea is to define the document schema and validate inbound XML documents, while throwing exceptions on validation errors. The processing semantics is arbitrary and can support any kind of action, depending on the defined invoke document schema.

A simple instance document which validates against a version 1.0 processing schema could look like this:

<?xml version="1.0?>
<Process xmlns="http://www.gama-system.com/process10.xsd" version="1.0">
   <Instruction>Add</Instruction>
   <Parameter1>10</Parameter1>
   <Parameter2>21</Parameter2>
</Process>

Another processing instruction, supported in version 1.1 of the processing schema, with different semantics could be:

<?xml version="1.0?>
<Process xmlns="http://www.gama-system.com/process11.xsd" version="1.1">
   <Instruction>Store</Instruction>
   <Content>77u/PEFwcGxpY2F0aW9uIHhtbG5zPSJod...mdVcCI</Content>
</Process>

Note that the default XML namespace changed, but that is not a norm. It only allows you to automate schema retrieval using the schema repository (think System.Xml.Schema.XmlSchemaSet), load all supported schemas and validate automatically.

public class ProcessService : IDocumentParameterModel
{
   public XmlDocument Process(XmlDocument doc)
   {
      XmlReaderSettings sett = new XmlReaderSettings();

      sett.Schemas.Add(<document namespace 1>, <schema uri 1>);
      ...
      sett.Schemas.Add(<document namespace n>, <schema uri n>);

      sett.ValidationType = ValidationType.Schema;
      sett.ValidationEventHandler += new
         ValidationEventHandler(XmlInvalidHandler);
      XmlReader books = XmlReader.Create(doc.OuterXml, sett);
      while (books.Read()) { }

      // processing goes here
      ...
   }

   static void XmlInvalidHandler(object sender, ValidationEventArgs e)
   {
      if (e.Severity == XmlSeverityType.Error)
         throw new XmlInvalidException(e.Message);
   }
}

The main benefit of this approach is decoupling the parameter model and method processing version from the communication contract. A service maintainer has an option to change the terms of processing over time, while supporting older version-aware document instances.

This notion is of course most beneficial in situations where your processing syntax changes frequently and has complex validation schemas. A simple case presented here is informational only.

So, how do we validate?

  • We need to check the instance document version first. This is especially true in cases where the document is not qualified with a different namespace when the version changes.
  • We grab the appropriate schema or schema set
  • We validate the inbound XML document, throw a typed XmlInvalidException if invalid
  • We process the call

The service side is quite straightforward.

Let's look at the client and what are the options for painless generation of service calls using this mechanism.

Generally, one can always produce an instance invoke document by hand on the client. By hand meaning using System.Xml classes and DOM concepts. Since this is higly error prone and gets tedious with increasing complexity, there is a notion of a schema compiler, which automatically translates your XML Schema into the CLR type system. Xsd.exe and XmlSerializer are your friends.

If your schema requires parts of the instance document to be digitally signed or encrypted, you will need to adorn the serializer output with some manual DOM work. This might also be a reason to use the third option.

The third, and easiest option for the general developer, is to provide a local object model, which serializes the requests on the client. This is an example:

ProcessInstruction pi = new ProcessInstruction();
pi.Instruction = "Add";
pi.Parameter1 = 10;
pi.Parameter2 = 21;
pi.Sign(cert); // pi.Encrypt(cert);
pi.Serialize();
proxy.Process(pi.SerializedForm);

The main benefit of this approach comes down to having an option on the server and the client. Client developers have three different levels of complexity for generating service calls. The model allows them to be as close to the wire as they see fit. Or they can be abstracted completely from the wire representation if you provide a local object model to access your services.

Categories:  .NET 3.0 - WCF | .NET 3.5 - WCF | Architecture | Web Services | XML
Monday, September 24, 2007 11:19:10 AM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Managed TxF: Distributed Transactions and Transactional NTFS 

Based on my previous post, I managed to get distributed transaction scenario working using WCF, MTOM and WS-AtomicTransactions.

This means that you have the option to transport arbitrary files, using transactional ACID semantics, from the client, over HTTP and MTOM.

The idea is to integrate a distributed transaction with TxF, or NTFS file system transaction. This only works on Windows Server 2008 (Longhorn Server) and Windows Vista.

Download: Sample code

If the client starts a transaction then all files within it should be stored on the server. If something fails or client does not commit, no harm is done. The beauty of this is that it's all seamlessly integrated into the current communication/OS stack.

This is shipping technology; you just have to dive a little deeper to use it.

Here's the scenario:

There are a couple of issues that need to be addressed before we move to the implementation:

  • You should use the managed wrapper included here
    There is support for TransactedFile and TransactedDirectory built it. Next version of VistaBridge samples will include an updated version of this wrapper.

  • Limited distributed transactions support on a system drive
    There is no way to get DTC a superior access coordinator role for TxF on the system drive (think c:\ system drive). This is a major downside in the current implementation of TxF, since I would prefer that system/boot files would be transaction-locked anyway. You have two options if you want to run the following sample:

    • Define secondary resource manager for your directory
      This allows system drive resource manager to still protect system files, but creates a secondary resource manager for the specified directory. Do this:
      • fsutil resource create c:\txf
      • fsutil resource start c:\txf
        You can query your new secondary resource manager by fsutil resource info c:\txf.

    • Use another partition
      Any partition outside the system partition is ok. You cannot use network shares, but USB keys will work. Plug it in and change the paths as defined at the end of this post.

OK, here we go.

Here's the service contract:

[ServiceContract(SessionMode = SessionMode.Allowed)]
interface ITransportFiles
{
   [OperationContract]
   [TransactionFlow(TransactionFlowOption.Allowed)]
   byte[] GetFile(string name);

   [OperationContract]
   [TransactionFlow(TransactionFlowOption.Allowed)]
   void PutFile(byte[] data, string name);

We allow the sessionful binding (it's not required, though) and allow transactions to flow from the client side. Again, transactions are not mandatory, since client may opt-out of using them and just transport files without a transaction.

The provided transport mechanism uses MTOM, since the contract's parameter model is appropriate for it and because it's much more effective transferring binary data.

So here's the service config:

<system.serviceModel>
  <bindings>
    <wsHttpBinding>
      <binding name="MTOMBinding"
          transactionFlow="true"
          messageEncoding="Mtom"
          maxReceivedMessageSize="10485760">
        <readerQuotas maxArrayLength="10485760"/>

      </binding>
    </wsHttpBinding>
  </bindings>
  <services>
    <service name="WCFService.TransportService">
      <host>
        <baseAddresses>
          <add baseAddress="
http://localhost:555/transportservice">
        </baseAddresses>
      </host>
      <endpoint address=""
          binding="wsHttpBinding"
          bindingConfiguration="MTOMBinding"
          contract="WCFService.ITransportFiles"/>
    </service>
  </services>
</system.serviceModel>

Here, MTOMBinding is being used to specify MTOM wire encoding. Also, quotas and maxReceivedMessageSize attribute is being adjusted to 10 MB, since we are probably transferring larger binary files.

Service implementation is straight forward:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class TransportService : ITransportFiles
{
    [OperationBehavior(TransactionScopeRequired = true)]
    public byte[] GetFile(string name)
    {
        Console.WriteLine("GetFile: {0}", name);
        Console.WriteLine("Distributed Tx ID: {0}",
          Transaction.Current.TransactionInformation.DistributedIdentifier);
        return ReadFully(TransactedFile.Open(@"C:\TxF\Service\" + name,
          FileMode.Open, FileAccess.Read, FileShare.Read), 0);
    }

    [OperationBehavior(TransactionScopeRequired = true)]
    public void PutFile(byte[] data, string filename)
    {
        Console.WriteLine("PutFile: {0}", filename);
        Console.WriteLine("Distributed Tx ID: {0}",
          Transaction.Current.TransactionInformation.DistributedIdentifier);

        using (BinaryWriter bw = new BinaryWriter(
            TransactedFile.Open(@"C:\TxF\Service\" + filename,
              FileMode.Create, FileAccess.Write, FileShare.Write)))
        {
            bw.Write(data, 0, data.Length);
           
            // clean up
            bw.Flush();
        }
    }
}

Client does four things:

  1. Sends three files (client - server) - no transaction
  2. Gets three files (server - client) - no transaction
  3. Sends three files (client - server) - distributed transaction, all or nothing
  4. Gets three files (server - client) - distributed transaction, all or nothing

Before you run:

  • Decide on the secondary resource manager option (system drive, enable it using fsutil.exe) or use another partition (USB key)
  • Change the paths to your scenario. The sample uses C:\TxF, C:\TxF\Service and C:\TxF\Client and a secondary resource manager. Create these directories before running the sample.

Download: Sample code

This sample is provided without any warranty. It's a sample, so don't use it in production environments.

Monday, July 23, 2007 9:54:13 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 WS-Management: Windows Vista and Windows Server 2008 

I dived into WS-Management support in Vista / Longhorn Server Windows Server 2008 this weekend. There are a couple of caveats if you want to enable remote WS-Management based access to these machines. Support for remote management is also built into Windows Server 2003 R2.

WS-Management specification allows remote access to any resource that implements the specification. Everything accessed in a WS-Management world is a resource, which is identifiable by a URI. The spec uses WS-Eventing, WS-Enumeration, WS-Transfer and SOAP 1.2 via HTTP.

Since remote management implementation in Windows acknowledges all the work done in the WMI space, you can simply issue commands in terms of URIs that incorporate WMI namespaces.

For example, the WMI class or action (method) is identified by a URI, just as any other WS-Management based resource. You can construct access to any WMI class / action using the following semantics:

  • http://schemas.microsoft.com/wbem/wsman/1/wmi denotes a default WMI namespace accessible via WS-Management
  • http://schemas.microsoft.com/wbem/wsman/1/wmi/root/default denotes access to root/default namespace

Since the majority of WMI classes are in root/cimv2 namespace, you should use the following URI to access those:

http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2

OK, back to WS-Management and its implementation in Vista / Windows Server 2008.

First, Windows Server 2008 has the Windows Remote Management service started up by default. Vista doesn't. So start it up, if you're on a Vista box.

Second, depending on your network configuration, if you're in a workgroup environment (not joined to a domain) you should tell your client to trust the server side.

Trusting the server side involves executing a command on a client. Remote management tools included in Windows Server 2008 / Windows Vista are capable of configuring the local machine and issuing commands to remote machine. There are basically two tools which allow you to setup the infrastructure and issue remote commands to the destination. They are:

  • winrm.cmd (uses winrm.vbs), defines configuration of local machine
  • winrs.exe (winrscmd.dll and friends), Windows Remote Shell client, issues commands to a remote machine

As said, WS-Management support is enabled by default in Windows Server 2008. This means that the appropriate service is running, but one should still define basic configuration on it. Nothing is enabled by default; you have to opt-in.

Since Microsoft is progressing to a more admin friendly environment, this is done by issuing the following command (server command):

winrm quickconfig (or winrm qc)

This enables the obvious:

  • Starts the Windows Remote Management service (if not stared; in Windows Vista case)
  • Enables autostart on the Windows Remote Management service
  • Starts up a listener for all machine's IP addresses
  • Configures appropriate firewall exceptions

You should get the following output:

[c:\windows\system32]winrm quickconfig

WinRM is not set up to allow remote access to this machine for management.
The following changes must be made:

Create a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.
Enable the WinRM firewall exception.

Make these changes [y/n]? y

WinRM has been updated for remote management.
Created a WinRM listener on HTTP://* to accept WS-Man requests to any IP on this machine.
WinRM firewall exception enabled.

There are options in winrm.cmd to fine tune anything, including the listening ports and / or SSL (HTTPS) support. In a trusted environment you probably don't want to issue commands using HTTP based mechanism, since you are located behind the trust boundary and have complete control over available (allowed) TCP ports.

You can now issue remote management commands against the configured server, but only if the communication is trusted. So in case you are in a workgroup environment (client and server in a workgroup), this should get you started (client command):

winrm set winrm/config/client @{TrustedHosts="<server ip or hostname>"}

You can specify multiple trusted servers using a comma:

winrm set winrm/config/client @{TrustedHosts="10.10.10.108, 10.10.10.109"}

This trusts the server(s) no matter what. Even over HTTP only.

Enumerating the configured listeners - remember, listener is located on the destination side - is done via:

winrm enumerate winrm/config/listener

OK, now we're able to issue commands to the remote side using WS-Management infrastructure. You can, for example, try this (client command):

winrs -r:http://<server ip> -u:<username> -p:<password> <shell command>, ie.
winrs -r:http://10.10.10.108 -u:administrator -p:p$38E0jjW! dir -s

or

winrs -r:http://10.10.10.108 -u:administrator -p:p$38E0jjW! hostname

You can even explose HTTP based approach through your firewall if you're crazy enough. But using HTTPS would be the smart way out. What you need is a valid certificate with server authentication capability and a matching CN. Self-signed certs won't work.

Simple and effective.

Categories:  Web Services | Windows Vista
Sunday, July 22, 2007 7:48:22 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Out the Door: WS-ReliableMessaging 1.1 

WS-RM 1.1 is finished. GoodTimestm.

OASIS published two specs:

WCF, as it turns out, will have support for WS-RM 1.1 implementation in Orcas. On this note, there is a new CTP out this week.

Categories:  .NET 3.5 - WCF | Microsoft | Web Services
Tuesday, July 03, 2007 3:58:29 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Durable Messaging Continues 

Nick is continuing my discussion on durable messaging support in modern WS-* based stacks, especially WCF.

I agree that having a simple channel configuration support to direct messages into permanent information source (like SQL) would be beneficial.

A simple idea that begs for an alternative implementation of a WCF extensibility point, has some questions:

  • What happens when messages are (or should be) exchanged in terms of a transaction context?
  • How can we demand transaction support from the underlying datastore, if we don't want to put limitations onto anything residing beind the service boundary?
  • What about security contexts? How can we acknowledge a secure, durable sessionful channel after two weeks of service hibernation down time? Should security keys still be valid just because service was down and not responding all this time?
  • Is durability limited to client/service recycling or non-memory message storage? What about both?

Is [DurableService]enough?

Wednesday, May 30, 2007 9:46:14 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 WS-* has an Open Specification Promise 

Microsoft has issued a statement that all (jointly) developed web services specifications have an "open use" policy attached to them.

Legal talk:

"Microsoft irrevocably promises not to assert any Microsoft Necessary Claims against you for making, using, selling, offering for sale, importing or distributing any implementation to the extent it conforms to a Covered Specification (“Covered Implementation”), subject to the following. This is a personal promise directly from Microsoft to you, and you acknowledge as a condition of benefiting from it that no Microsoft rights are received from suppliers, distributors, or otherwise in connection with this promise. If you file, maintain or voluntarily participate in a patent infringement lawsuit against a Microsoft implementation of such Covered Specification, then this personal promise does not apply with respect to any Covered Implementation of the same Covered Specification made or used by you. To clarify, “Microsoft Necessary Claims” are those claims of Microsoft-owned or Microsoft-controlled patents that are necessary to implement only the required portions of the Covered Specification that are described in detail and not merely referenced in such Specification. “Covered Specifications” are listed below."

The promise is given to the specifications themselves and, of course, not to implementations.

GoodMovetm.

Categories:  Web Services
Wednesday, September 13, 2006 10:13:14 AM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Article: Transactional Semantics in Loosely Coupled Distributed Systems 

Last article finished my XML series. This one focuses on transactional semantics in a service oriented universe.

Language: Slovenian


Naslov:

Transakcijska semantika v šibko sklopljenih, porazdeljenih sistemih

Transakcijska semantika v šibko sklopljenih, porazdeljenih sistemih

Tuesday, June 06, 2006 9:59:30 AM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Article: Concepts and Semantics of Service Contracts 

The second article is about concepts and semantics of service contracts. It deals with WCF (Windows Communication Foundation) contract definition and its behavioral aspects.

Language: Slovenian


Naslov:

Koncepti in semantike storitvenih pogodb

Koncepti in semantike storitvenih pogodb

Categories:  .NET 3.0 - WCF | Architecture | Articles | Web Services
Friday, June 02, 2006 7:19:45 PM (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, May 27, 2006 11:07:39 AM (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, May 11, 2006 11:15:46 AM (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, May 10, 2006 9:55:37 AM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 WinFX January 2006 CTP Released 

WinFX January 2006 CTP just went live.

With it, a Go-Live licence is available for WCF and WF. Sign and deploy.

Update: They managed to screw up the download links again. Beware: If you're downloading complete installs (and not the setup manager) use the lower links for x86 and x64 images (there are two on the get the beta page).

The following links will lead to correct complete download of WinFX January 2006 CTP Runtime Components:

You can also check the file version. The correct WinFX RTC version for January 2006 CTP is 3.0.50727.154 (file size 44.488 KB [x86] and 84.464 KB [x64]). December CTP was 3.0.50727.151 and file sizes were a couple KB lower.

Categories:  Web Services | .NET 3.0 - General | Work
Wednesday, January 18, 2006 9:34:01 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Indigo - Multiple endpoints 

There is a common functionality request in modern distributed applications that clients and services (client also being other services) could communicate using different communication protocols and/or different transport semantics inside a single protocol.

For this sample we're going to limit ourselves to HTTP messaging, but this does not limit the scope of the article. Other transport protocols can be added without obstructing other transport mechanisms.

To allow this scenario Indigo (Windows Communication Foundation, WCF) allows multiple endpoints per service to be setup. Service side bindings can, for example, be the following:

<bindings>
  <wsProfileBinding>
    <binding
      configurationName="Secure"
      orderedSession="false"
      reliableSessionEnabled="false"
      securityMode="WSSecurityOverHttp"
      maxMessageSize="134217728"
      flowTransactions="NotAllowed"
      messageEncoding="Text">
      <wsSecurity
        authenticationMode="Certificate"
        useNegotiation="true" />
    </binding>
    <binding
      configurationName="SecureReliable"
      orderedSession="true"
      reliableSessionEnabled="true"
      securityMode="WSSecurityOverHttp"
      maxMessageSize="134217728"
      flowTransactions="NotAllowed"
      messageEncoding="Text">
      <wsSecurity
        authenticationMode="Certificate"
        useNegotiation="true" />
    </binding>
    <binding
      configurationName="SecureReliableTransacted"
      orderedSession="true"
      reliableSessionEnabled="true"
      securityMode="WSSecurityOverHttp"
      maxMessageSize="134217728"
      flowTransactions="Allowed"
      messageEncoding="Text">
      <wsSecurity
        authenticationMode="Certificate"
        useNegotiation="true" />
    </binding>
    <binding
      configurationName="SecureMTOM"
      orderedSession="false"
      reliableSessionEnabled="false"
      securityMode="WSSecurityOverHttp"
      maxMessageSize="134217728"
      flowTransactions="NotAllowed"
      messageEncoding="Mtom">
      <wsSecurity
        authenticationMode="Certificate"
        useNegotiation="true" />
    </binding>
    <binding
      configurationName="SecureReliableMTOM"
      orderedSession="true"
      reliableSessionEnabled="true"
      securityMode="WSSecurityOverHttp"
      maxMessageSize="134217728"
      flowTransactions="NotAllowed"
      messageEncoding="Mtom">
      <wsSecurity
        authenticationMode="Certificate"
        useNegotiation="true" />
    </binding>
   <binding
      configurationName="SecureReliableTransactedMTOM"
      orderedSession="true"
      reliableSessionEnabled="true"
      securityMode="WSSecurityOverHttp"
      maxMessageSize="134217728"
      flowTransactions="Allowed"
      messageEncoding="Mtom">
      <wsSecurity
        authenticationMode="Certificate"
        useNegotiation="true" />
    </binding>
  </wsProfileBinding>
</bindings>

There are six different bindings setup:

  • Secure
  • SecureReliable
  • SecureReliableTransacted
  • SecureMTOM
  • SecureReliableMTOM
  • SecureReliableTransactedMTOM

The Secure binding uses WS-Security/WS-SecureConversation pair to secure all outgoing and incoming messages using encryption and digital signing. We’re not going to drill down into it in this post. I’m going to write another one where different certificate mechanisms will be discussed.

The SecureReliable binding also uses WS-ReliableMessaging with message ordering and guaranteed delivery turned on.

The SecureReliableTransacted endpoint also uses WS-AtomicTransaction to implement transaction semantics between the client and the service.
There are another three bindings (*MTOM) with the same transport semantics as well as MTOM encoding turned on. Since services can support transfer of large binary files, we have also set the maxMessageSize attribute in every binding to 128 MB.

Endpoint configuration for this service is:

<services>
  <service
    serviceType="Indigo.Demos.TransferService"
    behaviorConfiguration="DefaultBehavior">
    <endpoint
      address="
http://localhost:666/TransferService.svc/"
      bindingNamespace=
        "
http://webservices.gama-system.com/bindings"
      bindingName="Default"
      bindingSectionName="wsProfileBinding"
      bindingConfiguration="SecureReliableMTOM"
      contractType="Indigo.Demos.ITransferContract"/>
    <endpoint
      address=
        "
http://localhost:666/TransferService.svc/secure"
      bindingNamespace=
        "
http://webservices.gama-system.com/bindings"
      bindingName="Secure"
      bindingSectionName="wsProfileBinding"
      bindingConfiguration="Secure"
      contractType="Indigo.Demos.ITransferContract"/>
    <endpoint
      address=
        "
http://localhost:666/TransferService.svc/
        securereliable"
      bindingNamespace=
        "
http://webservices.gama-system.com/bindings"
      bindingName="SecureReliable"
      bindingSectionName="wsProfileBinding"
      bindingConfiguration="SecureReliable"
      contractType="Indigo.Demos.ITransferContract"/>
    <endpoint
      address=
        "
http://localhost:666/TransferService.svc/
          securereliabletransacted"
      bindingNamespace=
        "
http://webservices.gama-system.com/bindings"
      bindingName="SecureReliableTransacted"
      bindingSectionName="wsProfileBinding"
      bindingConfiguration="SecureReliableTransacted"
      contractType="Indigo.Demos.ITransferContract"/>
    <endpoint
      address=
        "
http://localhost:666/TransferService.svc/
          secureMTOM"
      bindingNamespace=
        "
http://webservices.gama-system.com/bindings"
      bindingName="SecureMTOM"
      bindingSectionName="wsProfileBinding"
      bindingConfiguration="SecureMTOM"
      contractType="Indigo.Demos.ITransferContract"/>
    <endpoint
      address=
      "
http://localhost:666/TransferService.svc/
        securereliableMTOM"
      bindingNamespace=
       "
http://webservices.gama-system.com/bindings"
      bindingName="SecureReliableMTOM"
      bindingSectionName="wsProfileBinding"
      bindingConfiguration="SecureReliableMTOM"
      contractType="Indigo.Demos.ITransferContract"/>
    <endpoint
      address=
        "
http://localhost:666/TransferService.svc/
          securereliabletransactedMTOM"
      bindingNamespace=
        "
http://webservices.gama-system.com/bindings"
      bindingName="SecureReliableTransactedMTOM"
      bindingSectionName="wsProfileBinding"
      bindingConfiguration="SecureReliableTransactedMTOM"
      contractType="Indigo.Demos.ITransferContract"/>
  </service>
</services>

It can be seen that almost every endpoint (two are bound to the same binding) is bound to a different binding.
The service is therefore exposing its functionality using the following endpoints:

The first endpoint in this example is considered a default one and is mapped to the same binding as the *securereliableMTOM endpoint.

The problem with this is that current WCF’s WSDLExporter maps different endpoints into WSDL ports, but WSDL ports have a requirement to be uniquely named. The default port name construction is done by using the binding type name (i.e. wsProfileBinding, basicProfileBinding), which is concatenated with the contract name.

Therefore, when one has defined multiple bindings from the same binding type a name collision occurs (six WSDL ports with the same QName (namespace and local name)). This can be avoided using a bindingNamespace attribute as well as bindingName attribute. In this example we set the bindingNamespace to http://webservices.gama-system.com/bindings for all bindings and made the name unique by using the bindingName attribute.

There are currently two weird design decisions in multiple binding support of WCF. First bindingNamespace and bindingName attributes are bound to endpoints and not bindings. It would probably be more appropriate to put binding’s namespace and name into the <binding> element. Second, binding name is not propagated into WSDL properly when the same binding is used in two or more endpoints (in this example http://localhost:666/TransferService.svc/ and http://localhost:666/TransferService.svc/securereliableMTOM endpoints are bound to Default binding. If you look closely into the generated WSDL, you will see that the matching WSDL ports are not named after bindingName attribute.

These are non-breaking issues and will probably be addressed for beta 2 of WCF.

In the next Indigo series post, we’ll look closely into the security characteristics of certificate based authentication.

Categories:  .NET 3.0 - WCF | Transactions | Web Services
Friday, August 12, 2005 12:02:47 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 Indigo CTP 

Microsoft “Indigo” is now available for general public.

If you are an MSDN Subscriber, you can download the bits from here.

Categories:  Web Services | Work | XML
Sunday, March 20, 2005 6:34:26 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 SCT in WSE2 and WS-SecureConversation 

If you get this error [1], while working on a WS-SecureConversation based application, consider the following:

  • Look at the timeout period for you SCTs, issued by the server-side service.
  • If this is all ok, then you're probably in my situation. Check if you enabled web garden support in your IIS's application pool. If so, go back to Performance tab in application pool's properties and enter 1 (one) as a 'Maximum number of worker processes'.

Things can get really rough in a state WSE2 is in if you create a web garden and use WS-SecureConversation at the same time.

A few weeks ago I was designing an architecture for a complex B2B secure communication channel and parts of it wanted to use some form of a web garden. Since I forgot to disable it, there were trouble.

The strangest thing I noticed when investigating this was that everything worked the first time after IIS restart happened - sure - a session is pinned to the first worker process, the second one just gets created.

Of course, there are workarounds regarding this, but they include some non-trivial WSE2 subclassing.

[1] Additional information: Microsoft.Web.Services2.Security.SecurityFault: The specified source for the derivation is unknown ---> System.Exception: WSE512: This derived key token's parent cannot be found. The reference to the parent token follows: <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"><wsse:Reference URI="uuid:227d47ac-1b2b-45a5-92f0-d7d630aeda86" ValueType="http://schemas.xmlsoap.org/ws/2004/04/security/sc/sct" /></wsse:SecurityTokenReference>.

Categories:  Web Services | Work
Tuesday, October 26, 2004 12:56:29 PM (Central Europe Standard Time, UTC+01:00)  #    Comments

 

 New WS-* and Related Specs 

Lots of new stuff happening lately:

Lots of (un)pleasant reading coming up.

Categories:  Web Services
Thursday, September 16, 2004 10:38:34 AM (Central Europe Standard Time, UTC+01:00)