views:

2087

answers:

3

In the past I have needed to create custom SOAP headers in a C# project that was using an imported WSDL web reference. I found a way to do it but I was never happy with it and I have sense wondered if there was a better way. What I did was create a header that derives from SoapHeader:

[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://model.test.net")]
[System.Xml.Serialization.XmlRootAttribute("securitytoken", Namespace = "http://model.test.net", IsNullable = false)]
public class SpecialHeader : SoapHeader
{ 
  [System.Xml.Serialization.XmlTextAttribute()]
  public string aheadervalue;
}

I then had to modify the code that was generated from the WSDL and add a referen ce to an instance of the new header and the following before each web call that I wanted to contain the custom header:

[System.Web.Services.Protocols.SoapHeaderAttribute("instancename", Direction=System.Web.Services.Protocols.SoapHeaderDirection.InOut)]

Where "instancename" is the custom header's instance variable name in the generated class.

This works fine except that any change in the WSDL requires that it all be done over again since it regenerates the class. In other languages the headers can be added outside of the generated code so maybe I'm missing the way that is done in C#. Are there better ways of doing this?

A: 

There is a way to do it, sort of; it's not necessarily pretty, and on a very simple web service it may not be worth the effort, but it at least saves you from having to re-add the attributes in when you regenerate the code.

Since the generator generates partial classes, you can:

  1. Add a file to your project that extends the web service class (the one derived from SoapHttpClientProtocol) with another "partial" section (i.e., use the same namespace and name as the generated class, and mark it "partial").

  2. Copy the methods you want to add the headers to (i.e., the same methods you've already been adding the attributes to) out of the generated code and paste them into your extension section.

  3. Rename the methods slightly so that they don't conflict with the ones in the generated code, and change the names that get passed to Invoke to match. (You may have to also tweak the other attributes on the methods to make sure they still map to the proper calls in the WSDL.)

  4. Add the custom header attribute to the renamed methods, and the header instance field to your extension section as well.

  5. Call the renamed versions from your code instead of the original versions.

As long as the method signatures don't change in the WSDL, you won't have to change anything in your code even if you regenerate. (Since you only copy the relatively short method implementations, any other structures from the WSDL will still come out of the generated code, so if they change you'll automatically get the updated versions when you regenerate. Granted, if the WSDL doesn't have any other structures in it, the utility of this is probably somewhat limited.)

It's still not ideal, but short of trying to intercept the raw XML message and put the header in directly (which you could probably do, but it would be nasty), there aren't really any other options that I know of (without moving over to WCF anyway).

Eric Rosenberger
+3  A: 

It seems that you are using .Net 2.0 and asmx webservices. Do you know that there is a framework called WCF (Windows Communication Framework) in .Net 3.0. I know that it is not easy to migrate to a new framework, but with WCF you get so much. Furthermore WCf can be used for so much more than WebServices (remoting, msmq and more). It is the framework that Microsoft is betting on for the future. Ie. manipulation a soap header is done using MessageContracts.

So the answer is that in WCF you can do this with MessageContracts.

khebbie
@khebbie: I'm new to WCF and trying to setup custom headers as well. Message Contracts sound like the way to go. Do you know of a good resource where I can read up on this?
Adam
A: 

I ran into this problem today. I ended creating a class that derives from the autogenerated class and overriding the GetWriterForMessage method to ensure my header was always present. I would update the header value on every call to the method.

hjb417