Message.ToString() returning "…stream…"
A month ago, I wrote an post on how to send an XmlDocument trough Windows Communication Foundation. At the end of the post, I noted that the ToString method on the Message object might return "…stream…" instead of the SOAP content.
A recent development needed to have the full message content in a string, and we hit the "…stream…" issue again. The development was a message inspector which was to be used on various BizTalk WCF Adapters. The inspector had to work on service side and on client side, implementing AfterReceiveRequest of IDispatchMessageInspector and BeforeSendRequest of IClientMessageInspector. Strangely, Message.ToString returned the full message on the server side adapter, but returned "…stream…" in client side adapter.
Searching on that matter on Google didn’t provided me with lots of results, but I found a post with a solution to that particular issue: "Accessing the message inside of your WCF service operations". So, it seems that this is due to the fact that the Message object is streamed (yeah I know it’s quite obvious…) on the client side.
So, to get the message’s content as a string, here is what is needed:
StringWriter stringWriter = new StringWriter(); XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter); message.WriteMessage(xmlTextWriter); xmlTextWriter.Flush(); xmlTextWriter.Close(); String messageContent = stringWriter.ToString();
However, there is an issue with that code in a message inspector. Using it like this will throw some exception about the message already been written. According to the documentation, a Message object can only be read or written once:
As the body of a Message object is a stream, it can only be read or written once. This is enforced by maintaining the current state of the Message object. A Message object can be read/written/copied when in the Created state. Other states are Read, Written and Copied, which means that the respective action has been performed already once.
So, when calling Message.WriteMessage to write the message in the XmlTextWriter, the state of the message changes from "Created" to "Written". This operation is irreversible, and the message is pretty useless afterward.
To avoid this, we can make use of the MessageBuffer class. This can be used to make a copy of a message in a buffer, then you can use the object created to create as many Messages as you want. Here is how to use it:
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue); Message message = buffer.CreateMessage();
After that, the message variable holds a reference to a copy of the initial request, and the buffer object can be used to produce copies of the initial Message.
There is one last trick. When creating a MessageBuffer out of the request, we actually read the request Message object, changing its state to Read and making it unusable afterward (in this case, the application!). Notice that in IDispatchMessageInspector and IClientMessageInspector methods, Messages parameters are always given using the ref keyword, meaning that the reference can be modified.
Solution to that last issue is to make another copy of the initial request and assign it to the variable. Here’s how to do it:
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue); Message message = buffer.CreateMessage(); //Assign a copy to the ref received request = buffer.CreateMessage();
Now the request variable hold a reference to a fresh Message, and the message variable holds a reference to another copy of the Message object that you can mess with in your inspector.
Send XmlDocument using Windows Communication Foundation
Today, I was stuck with a very simple problem. I wanted to send some XML to as service, using WCF. The proxy generated using svcutil left me with a function accepting an object parameter and returning an object parameter. The service is some kind of echo, simply returning what you sent. I had a sample XML file that contained the message that the service expects to test it with.
I first tested the service by sending the sample as a String. The response object was an XmlDocument. I figured that if what I received was an XmlDocument, I could simply send one as input. Wrong. XmlDocument happens to be not serializable, great. I tired to use XmlDocument.DocumentElement instead, which is of type XmlElement, without any success either.
I then tested XDocument and XElement, but that didn’t work either. Gosh.
Next attempt. I created a schema out of the XML sample file (using Visual Studio 2008, it is very easy. Open the XML file then in the menu, go for "XML" then "Create Schema"). I then used xsd tool to generate a serializable class that could hold the XML, and deserialized the sample file into an instance of that class to give it as to the client’s function. Failure again.
Now, I was left with only one option, at least from what I could see: directly use the Message class from WCF.
I (re) found an article about Message on the excellent Service Station web site (written by Aaron Skonnard who writes amazing articles and records amazing Webcasts). I didn’t want to go that way at the beginning, but I was left with only that.
First, the code has to be changed. Let’s see how to invoke a service using the generic IRequestChannel interface.
var doc = new XmlDocument(); doc.LoadXml(xmlContent); var message = Message.CreateMessage(MessageVersion.Soap11, "urn:someRequest", new XmlNodeReader(doc)); var factory = new ChannelFactory<IRequestChannel>("serviceHttpSoap11Endpoint"); var channel = factory.CreateChannel(); var response = channel.Request(message); channel.Close();
So, that’s pretty easy. Basically, the first step is to create a Message object, and give it a reader to the XmlDocument. I then created a ChannelFactory using the endpoint configuration generated by the previous client. Then I create a channel and use it to sent the message a built with the XmlDocument. Nifty!
But wait, there is one thing that is left to do. In the configuration file, the endpoint still uses the client generated contract, while in the code we use IRequestChannel as the contract. Also, the contract needs the full namespace of the contract. Here is the endpoint’s configuration:
<endpoint address="http://foo.com/bar/service" binding="basicHttpBinding" contract="System.ServiceModel.Channels.IRequestChannel" name="serviceHttpSoap11Endpoint" />
This one worked, at last. I received a nice reply from the service, however not the one I expected, but this another story and is for my Java colleague to solve… heh
One last remark: strangely, if you set up your client to log the messages sent, the message logged at Service Level will not display the content of the Message body element. Instead, it will simply contain a "… stream …" text.
However, if you log the message at Transport Level, you will see the full content.
Calling One Way WCF Service with BizTalk WCF Adapter, Part 2
As promised, here is the follow up of my previous post: Calling One Way WCF Service with BizTalk WCF Adapter, Part 1.
Let’s do a qui recap: we want to create a WCF proxy that will call a one way web service and return an empty message to BizTalk so the messaging process doesn’t timeout waiting for an answer.
The Interface
The first step is to create a service interface that is able to receive any message. This is done by
[ServiceContract(Namespace = "http://pvle.be/WcfOneWayProxy", Name = "OneWayProxy")] public interface IOneWayProxy { [OperationContract(Action = "*", ReplyAction = "*")] Message ProcessOneWayMessage(Message message); }
The operation has to be defined as a request/reply operation, as this is what BizTalk expects. Note the Action property of the OperationContractAttribute, its value is set to "*" meaning that all received messages will be dispatched to that operation.
The Implementation
Now that the interface is ready, let’s implement it.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ValidateMustUnderstand = false)] public class OneWayProxy : IOneWayProxy { private const String ConfigurationName = "ServiceEndpoint"; private static ChannelFactory<IOutputChannel> Factory; static OneWayProxy() { BasicHttpBinding binding = new BasicHttpBinding(); OneWayProxy.Factory = new ChannelFactory<IOutputChannel>(OneWayProxy.ConfigurationName); OneWayProxy.Factory.Open(); } #region IOneWayProxy Members public Message ProcessOneWayMessage(Message message) { IOutputChannel channel = OneWayProxy.Factory.CreateChannel(); try { //Send the one way message channel.Open(); channel.Send(message); channel.Close(); } catch (Exception) { //Do something, log the exception, whatever return Message.CreateMessage(message.Version, "http://pvle.be/EmptyReplyAction"); } //Whatever happens, send a response as this was "Fire and Forget" for the caller //Empty response send back to BizTalk so messaging process can terminate gracefully return Message.CreateMessage(message.Version, "http://pvle.be/EmptyReplyAction"); } #endregion }
A couple of remarks here:
- You need to have a valid client endpoint configuration in you App.config/Web.config that has the name defined in the ConfigurationName const.
- To create the message that is to be returned, we use the received message’s Version property, ensuring that there is no version mismatch.
Now that we have our service implementation, we can host it. I hosted it in IIS, as this is probably the best way to host WCF services and as this proxy should be running all the time.
BizTalk WCF Adapter Configuration
The last step is to make sure that your one way WCF adapter is calling the proxy. This is done by changing the address in the WCF Adapter configuration in your BizTalk project.
Once this is set (the SOAP Action header remains the same), you are ready to run you orchestration. You will see that the messaging process will no longer timeout, as our home made proxy will send an empty response message.
Improvements
Let’s face it, this implementation is fine for a proof of concept, but it’s hardly enough for a real application as only one endpoint is defined in the configuration.
If you have multiple one way endpoints and you want to reuse that proxy, you’ll probably need to have some lookup table in the proxy code to define the destination of the call, based on the To property of the Message object. This way, you can use a single one way proxy for all the actions needed in your orchestrations, as long as you are not using the same action for more than one endpoint.
Calling One Way WCF Service with BizTalk WCF Adapter, Part 1
If you ever tried to call a One Way web service operation with BizTalk, you might have read that BizTalk’s WCF Adapters don’t support One Way calls. The orchestration calls the service as expected and carries on running, but some Messaging process still hangs in the BizTalk Server Administration Console. Simply put: BizTalk 2006 R2 WCF Adapters don’t support "Fire and Forget" communication pattern (message is sent and reception is acknowledged by the listening service, but no response is sent back).
In the "Using the Windows Communication Foundation (WCF) Adapters in BizTalk Server" paper by Aaron Skonnard, it is clearly stated at page 22:
An important consideration is that the WCF send adapter is only compatible with request-reply operations even when used on one-way send ports. WCF service operations can return void but they shouldn’t be marked with IsOneWay=true if you want to call them from a WCF send port.
This means that if an orchestration calls a service operation marked as One Way (hence not returning anything), the orchestration will continue and terminate fine, but the port that called the service will hang there for a response.
With default settings, the process will try to call the service three more times before giving up raising some CommunicationException:
System.ServiceModel.CommunicationException: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error.
This is not very nice.
This issues has been discussed many times on blogs. One of the solution proposed by Sonu Arora is to remove the IsOneWay operation attribute at the service, so the service sends an empty SOAP message as a response. This is the same solution that what is advised by Aaron. However, this means that you have some control on the service itself, which might not be the case. On top of this, if you have other client calling that service, should they all change because of BizTalk? I don’t think so.
An other solution proposed by Josh Twist, who’s encountering the same issue but the other way round, is to either modify the orchestration (hence making it aware of the post used, which is not good), or to build an intermediary service that will support the One Way operation (whichever direction you choose) and adapt to make sure BizTalk gets what it needs.
This is what I did and I will explain it is part 2 of this post, as I’m running out of time today
WCF Services hosted in IIS
Lately, I had some trouble hosting WCF Services on IIS on a remote VM running Windows Server 2003. Nothing really serious, but I figured out that it wouldn’t be bad to write down these errors for future reference.
Enabling hosting of WCF Services on IIS
This is probably trivial, but I’m still writing it here so I can refer to it later.
First, if the .NET 2.0 Framework was installed prior to IIS, IIS is not registered with ASP.NET. It can be done manually running the following command:
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>aspnet_regiis -i
Then, you have to register .svc files in IIS, which is done by running the following command:
C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Communication Foundation>ServiceModelReg.exe -i
Once this is done, simply run iisreset in any command line window to restart IIS.
That’s it, IIS can now host WCF services.
Please note that if you deploy your website directly from Visual Studio, you’ll have to make sure that Frontpage Server Extensions are installed on the server.
Hosting a WCF Service in IIS
Anyway, hosting the service is very easy. The service endpoint will be a .svc file in which you’ll have to write only one line, referencing the service that is to be hosted.
<%@ ServiceHost Service="MyService" %>
On top of that, the Web.config file has to be modified in order to contain the service’s information like you would do in any WCF host application (ABC, except that you leave the address empty as the endpoint is the uri of the .svc file).
Permission Issues
If the service you are hosting interacts in some way with the file system or any other operation that might require permissions, don’t forget that once they are in IIS, they will have the limited rights of ASPNET (XP) or NETWORK SERVICE (WS2003) account, depending on the operating system.
I’m highlighting this because prior to host my services in IIS, I hosted them in a command line window. This is handy for debugging, and very simple to set up. However, this means that the WCF services are running with lots of privileges (power user or administrator, as you are developing). Once in IIS, you can still debug easily by attaching to the IIS process (Ctrl + Alt + P in Visual Studio).
