Wednesday, May 20, 2009

How The BizTalk WCF Send Adapter works under the hood.

This posting is a follow up to the WCF Receive Adapter article posted last year. (Sorry it took so long, I wrote it at the same time of the Receive adapter article however it was never posted...)

The WCF Adapter can send messages to a WCF Service, effectively making the WCF Send Adapter a client or "Proxy" that can communicate with WCF Dispatchers “services”.

Normally in a WCF Client - Server solution, a client, or "Proxy" must match the Endpoint configuration for a Service. This means that the client must have an address, binding and contract that match the service. The client must use the same binding that the service expects. The same binding means that the client must use the same binding elements such as Security options, transactions, reliable sessions, and etc. The client must also match the same encoder-formatter and transport.

Client applications such as windows forms applications and asp.net web sites, can easily use the Add service reference utility that comes with the .Net 3.5 framework SDK. When using this utility, a proxy class is generated where a developer can simply include this auto-generated proxy class inside their solution. The WCF Send Adapter supports something similar. The WCF Send Adapter can import a WCF Configuration from a app.config-web.config file. The way to get a hold of this file is to use the SvcUtil.exe tool (Add Service Reference) and generate a proxy like normal, and uses the App.config-web.config that is generated from this tool. The way in which you use this configuration is to simply import the configuration using a WCF-Custom adapter. When you import the configuration file, the WCF creates a WCF Send Adapter configuration that matches what the Dispathcing Service expects.

But what happens to the contract attributes, such as the operation attributes like IsOneWay, TransactionFlow, and etc. These are not inside the configuration file, they are declared as attributes inside the service contract, and these must match too?

Alas, the WCF Send Adapter uses the binging configuration and options specified in each binding element to infer which attributes need to be applied to a generic contract.

The WCF Send adapters uses 2 generic service contracts- IOutputChannel, and IRequestChannel. These two interfaces are not actually marked up with the [ServiceContract] attribute however the WCF Framework at a very low level uses these interfaces to send messages. The difference between these two interfaces is simply IOutputChannel is used for One Way operations, while IRequestChannel is used for two way operations. Thus if you need to send a message and wait for a return acknowledgement from the service, the IRequestChannel will be used. This is the default for WCF Send adapters because BizTalk needs confirmation as to when to delete the message from the MessageBox DB. One way operations, a special case within BizTalk, would be used with transports such as the net.msmq bindings, where the underlying transport can send back a control message acknowledging its receipt. This control message is internal within the protocol, effectively keeping the pattern “One Way”, even though technically it’s not. Another point to add here with one way operations, WCF Send adapters do not support a Service whose contract specifies “IsOneWay=true”. The reason is quite simple, the BizTalk adapter framework needs to transactionally “commit” if the message is successfully send. Services marked with “IsOneWay=true” operations do not yield a way to determine this. No control message is sent back, thus the WCF Send adapter doesn’t support this option.

When these channels-contracts are being created, the binding configration is taken and read from SSO DB and the various attributes are dynamically added to the descriptions of either Operations or message contracts.

What's important to overstand here is that in a normal client application the client would match the contract by either sharing the service contract, or making a contract that is effectively wire compatible. Wire compatible simply means that any option can set, as long as the final output of the message contains all the appropiate elements and attributes that the Dispather can successfully process. Because the WCF Send adapters infer these settings from your configuration of a WCF Send Adapter, there is no room for error, the slightest property that is set incorrectly will cause the WCF send adapter to not successfully send data to a WCF Service. For example, just recently a colleague was trying to flow transactions across to a WCF service which was configured to NotAllow transactions to flow into it. Techinically this means that there should be no OLE or WSAT header propogating any transaction ID. The application was first tested using a Windows Client application. Within the Windows application, a transaction scope was created, and the binding used had transactions being flowed. The client proxy was sharing the Service contract of the service which specified that TransactionFlow set to NotAllowed. The Default behavior of the windows proxy client was to strip off the OLE-WSAT header information within the message, thus making it wire compatible, even though the binding options were otherwise specified. The windows client application worked successfully because the TransactionFlow attribute specified inside the proxy class caused the proxy stripped the transaction header information before being sent.

Now let's take the above scenario with BizTalk Server's WCF Send Adapters. If you use a WCF Send Adapter, there is no place where you specify how to share a contract, nor manually create a wire compatible one. It's left up to the dynamic creation of the Send Adapter using the configuration, to create a matching contract. If a binding contained the option to flow transactions, as outlined above, the WCF Send adapter would add the appropriate TransactionFlow attribute dynamically at run time. This would cause the dynamic proxy to create the OLE/WSAT transaction headers, and send them to the service. At this point, the service would yield an exception saying that the ServiceContract specified that TransactionFlow is not allowed, while the same binding options used inside a Windows Forms application works, simply because the ServiceContract is shared. To solve this challenge, the binding should specify not to flow transactions.

To be continued…

No comments: