Answered ASMX to WCF SOAP Headers

  • Tuesday, February 20, 2007 3:14 PM
     
     

    Hi,

    I've written a large asmx web service and now i want to port it to WCF service. What is the easiest way to do that by having in mind that i have applied [SoapHeader] attribute to almost every web method in the asmx service. This attribute always points to one SoapHeader inheritor class. Can I add some attribute ( like SoapHeaderAttribute in asmx ) along with the OperationContractAttribute to add this SOAP header. Or I have to create a wrapper classes for all types I use in order to add the header by using MeassgeHeader.

    Thanks in advance,

    Kostadin

Answers

  • Tuesday, February 20, 2007 9:16 PM
     
     Answered

    As you say, you could use the [MessageContract] model and use [MessageHeader] to do this.  Another possibility is to just programmatically add and find headers, as in the sample code below.  I have bolded the parts where the client adds a header to the outbound message and the server reads it off the inbound message. 


    using System;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Channels;

    [ServiceContract]
    public interface IMyContract
    {
        [OperationContract]
        string echo(string s);
    }

    public class MyService : IMyContract
    {
        public string echo(string s)
        {
            // fyi, here is what the wire message looked like
            Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage.ToString());
            // find the header we are interested in (lookup by name/namespace)
            int index = OperationContext.Current.IncomingMessageHeaders.FindHeader("foo", "");
            string h = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(index);
            Console.WriteLine(h);

            return s;
        }
    }

    public class Repro
    {
        public static void Main()
        {
            Uri address = new Uri("http://localhost:8004/Service");
            Binding binding = new BasicHttpBinding();

            ServiceHost service = new ServiceHost(typeof(MyService), address);
            service.AddServiceEndpoint(typeof(IMyContract), binding, address);
            service.Open();

            ChannelFactory<IMyContract> cf = new ChannelFactory<IMyContract>(binding, new EndpointAddress(address));
            IMyContract proxy = cf.CreateChannel();
            using (OperationContextScope ocs = new OperationContextScope((IContextChannel)proxy))
            {
                // add header with name "foo", namespace "", value "val" to this operation call
                OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("foo", "", "val"));
                Console.WriteLine(proxy.echo("Hello World"));
            }
            ((IClientChannel)proxy).Close();
            service.Close();
            Console.WriteLine("Done, press a key");
            Console.ReadKey();
        }
    }

     

All Replies

  • Tuesday, February 20, 2007 9:16 PM
     
     Answered

    As you say, you could use the [MessageContract] model and use [MessageHeader] to do this.  Another possibility is to just programmatically add and find headers, as in the sample code below.  I have bolded the parts where the client adds a header to the outbound message and the server reads it off the inbound message. 


    using System;
    using System.ServiceModel;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;
    using System.ServiceModel.Channels;

    [ServiceContract]
    public interface IMyContract
    {
        [OperationContract]
        string echo(string s);
    }

    public class MyService : IMyContract
    {
        public string echo(string s)
        {
            // fyi, here is what the wire message looked like
            Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage.ToString());
            // find the header we are interested in (lookup by name/namespace)
            int index = OperationContext.Current.IncomingMessageHeaders.FindHeader("foo", "");
            string h = OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(index);
            Console.WriteLine(h);

            return s;
        }
    }

    public class Repro
    {
        public static void Main()
        {
            Uri address = new Uri("http://localhost:8004/Service");
            Binding binding = new BasicHttpBinding();

            ServiceHost service = new ServiceHost(typeof(MyService), address);
            service.AddServiceEndpoint(typeof(IMyContract), binding, address);
            service.Open();

            ChannelFactory<IMyContract> cf = new ChannelFactory<IMyContract>(binding, new EndpointAddress(address));
            IMyContract proxy = cf.CreateChannel();
            using (OperationContextScope ocs = new OperationContextScope((IContextChannel)proxy))
            {
                // add header with name "foo", namespace "", value "val" to this operation call
                OperationContext.Current.OutgoingMessageHeaders.Add(MessageHeader.CreateHeader("foo", "", "val"));
                Console.WriteLine(proxy.echo("Hello World"));
            }
            ((IClientChannel)proxy).Close();
            service.Close();
            Console.WriteLine("Done, press a key");
            Console.ReadKey();
        }
    }

     

  • Wednesday, February 21, 2007 9:42 AM
     
     

    Thanks Brian,

    This solution is fine, but I wish that the soap header which has always the same structure for all messages to be described in the WSDL for all this messages. Is [MessageContract] the only solution for that scenario and can I somehow add [MessageHeader] to the method so it won't be necessary to create new MessgaeContract types for all methods.

    So if I have this:

    [OperationContract]

    ReturnType methodOne( type1 param1, type2 param2, ..... , typeN paramN )

    Can i add some attribute to this signature to tell that the incomming ant outgoing messages are decorated with some header - "MySoapHeader"

    Or I have to do the following:

    [OperationContract]

    OutgoingMessage methodOne( IncommingMessage incommingMessage )

     

    [MessageContract]

    public class IncommingMessage

    {

    [MessageHeader]

    MySoapHeader mySoapHeader;

    [MessageBodyMember]

    type1 param1;

    .....

    [MessageBodyMember]

    typeN paramN;

    }

    [MessageContract]

    public class OutgoingMessage

    {

    [MessageHeader]

    MySoapHeader mySoapHeader;

    [MessageBodyMember]

    ReturnType returnParam;

    }

  • Wednesday, February 21, 2007 3:09 PM
     
     

    There is one problem with the MessageContract approach. There are asp.net clients using the service I am porting to WCF one. And these clients must remain unchanged when consuming the new WCF service. But when returning a MessageContract ( header + method return value ) from a method the asp.net clients generate proxy method returning void ( not the actual return type specified in the MessageContract ) and having the retrurn value as out parameter + one more out parameter of type bool starting with the same name as the first one and ending with "Specified".

    So instead of generating the proper one:

    returnType methodOne( ... )

    it generates something like:

    void methdOne( out returnType returnValue, out bool returnValueSpecified, ... )

    Is there any way to make it generate the first one?

     

    Thanks

  • Thursday, January 17, 2008 10:22 AM
     
     
    Try this:
    ...


    [MessageContract (IsWrapped = false)]

    public class OutgoingMessage

    {

    [MessageHeader]

    MySoapHeader mySoapHeader;

    [MessageBodyMember]

    ReturnType returnParam;

    }

    ...

    Bye
    Marco
  • Friday, June 13, 2008 6:51 AM