WCF service interaoperability with a Webservice

hi

I am making an application using WCF

The scenario is like this:

There is a webservice which is connecting to some external database, the methods and WSDL exposed by the web service is available to me but the source code is not available.

So what I have to do is I have to build the application in such a way that I have to initiate or connect or expose web service address thru WCF service (means: I want to build a WCF wrapper on web service).

Is this is possible without the availability of source code of webservice, If yes, then how to achieve it

[976 byte] By [ramkasarla] at [2008-1-4]
# 1

I'm not so sure you even have to wrap the service. If the service is publishing a WSDL, you can generate client code and config with svcutil. It shouldn't matter what kind of framework the webservice was built on. Here's how I would try to do this:

- Generate config and client proxy code with svcutil. I like to have a local copy of the WSDL to look at, so I do this:

svcutil.exe /t:metadata <WSDL URL>

Now that I have a local copy of a .wsdl and .xsd file, I generate code and config using:

svcutil.exe <wsdl file> <xsd file>

- Rename that .config file from Output.config to App.config. Look at the generated client code. This makes some kind of client class that can be instanciated and service methods can be called, just like the vast majority of WCF samples.

Now, if you still want to wrap the service, I think the way to do it would be to wrap the WCF client above in a WCF service. Basically, each WCF service operation would end up being a WCF client proxy which is used to call methods on the outside service. But, if everything is wrapped correctly, the WSDL from your WCF service would be pretty much identical to the one from the outside service, so why do all that extra work?

HTH,

-James

JamesOsborne-MSFT at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 2

Thanks, for the reply

yes,

i used add servicereference,(where i mentioned the web service address) at client side to generate proxy code for client, This is useful for client. But my intention is not this

My intention is i had a existing web service address (no source code,nothing available to me), i want to use a WCF service to wrap up this webservice or i want to expose asmx endpoints in my WCF service or simply to say i want to call that asmx webservice from my wcf service .

i am not at all concerned with the client just i want to initiate the webservice through my wcf service, how it is possible?

ramkasarla at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 3

To clarify things, I'll try to describe your scenario like this:

Given an "external" service A, you want to wrap service A with a WCF service B. WCF clients will access only service B. So, the WCF service B can be viewed as an intermediary. Sometimes people would do this because service B adds new functionality while still providing everything that service A provides.

Here's how I would create service B:

1. Figure out what service operations (maybe all of them) from service A that I need to provide with service B.

2. Define the contract (interfaces) for these operations. I could do this with svcutil or I could do it manually by hand. If there are only a couple operations, it might be faster just to write them by hand.

3. Define a service class which implements this contract.

4. Implement the methods (operations) in the service class. The implementation looks a lot like a WCF client. I would have the address for the endpoint at service A, the binding that the endpoint requires, and I would use those and the contract for service A's method that I'm wrapping to create a channel factory, then a channel. Then I would call that method at service A and return whatever is needed.

In code, it would look something like this: (note: I didn't build this or anything, but this should illustrate the 4 steps above)

// Contract for Service A

[ServiceContract]

public interface IServiceAContract

{

[OperationContract]

int AddTwoNumbers(int x, int y);

}

// So, I have to wrap one operation. I'll define the service contract for this. It is pretty much identical, but if I wanted to implement new methods as well, I would do that here.

// Contract for Service B

[ServiceContract]

public interface IServiceBContract

{

[OperationContract]

int AddTwoNumbers(int x, int y);

}

// Now my WCF service class (service B) which uses IServiceBContract

[ServiceBehavior]

public class ServiceB : IServiceBContract

{

private string serviceAEndpointAddress = http://serviceA;'

// The binding used for serviceA is defined in config. I could do it in code here, too, but it depends on the requirements of serviceA.

private BasicHttpBinding binding = new BasicHttpBinding("ServiceABindingConfiguration");

[OperationBehavior]

public int AddTwoNumbers(int x, int y);

{

ChannelFactory<IServiceAContract> factory = new ChannelFactory<IServiceAContract>(this.binding, this.serviceAEndpointAddress);

IServiceAContract channel = factory.CreateChannel();

((IChannel)channel).Open();

return channel.AddTwoNumbers(x, y);

}

}

Does this help at all? Or am I not understanding your scenario?

Thanks,

James

JamesOsborne-MSFT at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 4

Thanks, James

ya you understood my problem and i tried to practice the sample what you have explained above.

ie. i am having an external webservice with method AddTwoNumbers() and i did wvery thing as u explained above as follows:

[ServiceContract]
public interface IServiceAContract
{
[OperationContract]
double AddTwoNumbers(double A, double B);
}


[ServiceContract]
public interface IServiceBContract
{
[OperationContract]
double Add(double A, double B);
}

and my class is:

public class ServiceB : IServiceBContract
{
private string serviceAEndpointAddress = " http://localhost:50171/Service1.asmx";
private BasicHttpBinding binding = new BasicHttpBinding();
[OperationBehavior]
public double Add(double x, double y)
{
ChannelFactory<IServiceAContract> factory = new ChannelFactory<IServiceAContract>(this.binding, this.serviceAEndpointAddress);
IServiceAContract channel = factory.CreateChannel();

((IChannel)channel).Open();
return channel.AddTwoNumbers(x, y);

}

}

i am able to build the application succefully and my client is console application as follows:

static void Main(string[] args)
{

ServiceBContractClient client=new ServiceBContractClient();
//client.Add();

Console.WriteLine("Result :", client.Add(5,5).ToString());
Console.ReadLine();
}

here at client side i could see only the IServiceBContract method, here my qustion is how can i call or pass values to my webservice AddTwoNumbers() method.

and when i run the client application i am getting the following Exception:

System.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: http://tempuri.org/IServiceAContract/AddTwoNumbers.
at System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest()
at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message)
at System.Web.Services.Protocols.SoapServerProtocol.Initialize()
at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)

ramkasarla at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 5

Minor detail: the operation defined on IServiceBContract is Add2Numbers, but in the service implementation of that contract, the method is named AddTwoNumbers. I'm assuming the latter is just a typo that doesn't exist in your actual code?

Your Client won't be calling your webservice Add() method. It only can access the methods at ServiceB, which above is Add2Numbers.

You are getting exceptions because the Client hasn't created a channel to the ServiceB. You need to do the same thing you did in the ServiceB methods, but for the client:

// This will make a factory for creating channels to the service, using the binding and address specified in the constructor.

ChannelFactory<IServiceBContract> factory = new ChannelFactory<IServiceBContract>(binding, serviceBEndpointAddress);

IServiceBContract clientChannel = factory.CreateChannel();

((IChannel)clientChannel).Open();

double addedNumbers = clientChannel.Add2Numbers(5, 5);

((IChannel)clientChannel).Close();

Console.Writeline(addedNumbers);

If you are interested, I can post a working sample that has a WCF serviceA, WCF serviceB, and Client which only communicates directly with serviceB. It works nicely, but it's all WCF; I haven't done anything with asmx.

-James

JamesOsborne-MSFT at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 6

After looking at your stack trace a bit further, David Kreutz and I think something else is going on here. The stack is from ServiceA. ServiceA can only accept messages with action ending in Add, not Add2Numbers; that's the ServiceB method. I think you might have generated the client proxy from metadata from ServiceA instead of serviceB, but still called the serviceB method? The stack trace indicates that the method call to Add2Numbers gets dispatched at the ServiceA location, but it really should get dispatched at serviceB.

HTH,

James

JamesOsborne-MSFT at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 7

Thanks for the reply,

I created the channel factory at client side also but still i am getting tha same error, as follows:

The exception Message is:

System.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: http://tempuri.org/IServiceAContract/AddTwoNumbers.

at System.Web.Services.Protocols.Soap11ServerProtocolHelper.RouteRequest()

at System.Web.Services.Protocols.SoapServerProtocol.RouteRequest(SoapServerMessage message)

at System.Web.Services.Protocols.SoapServerProtocol.Initialize()

at System.Web.Services.Protocols.ServerProtocolFactory.Create(Type type, HttpContext context, HttpRequest request, HttpResponse response, Boolean& abortProcessing)

and the Stack Trace for the Method is:

System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc&amp; rpc)
System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData&amp; msgData, Int32 type)
IServiceBContract.Add(Double A, Double B)
client.Program.Main(String[] args)
System.AppDomain.nExecuteAssembly(Assembly assembly, String[] args)
System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
System.Threading.ThreadHelper.ThreadStart_Context(Object state)
System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
System.Threading.ThreadHelper.ThreadStart()

ramkasarla at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 8

Where are these exceptions coming from? The first one you list, with the System.Web stack, is that appearing at ServiceB? Or is that at the Client? If it's at the Client, I'm wondering what endpoint address you passed to the channel factory.

-James

JamesOsborne-MSFT at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 9

i used SvcTraceViewer tool to trace the exception details which i posted above and when I examine the trace log files of both service and client i could see the same exception details which i posted above in SvcTraceViewer tool. and i am once again posting my whole application,which is as follows:

my asmx service :

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ToolboxItem(false)]
public class Service1 : System.Web.Services.WebService
{

[WebMethod]
public double AddTwoNumbers(double A, double B)
{
return A + B;
}
}

MY WCF service :

[ServiceContract]
public interface IServiceAContract
{
[OperationContract]
double AddTwoNumbers(double A, double B);
}


[ServiceContract]
public interface IServiceBContract
{
[OperationContract]
double Add(double A, double B);
}

public class ServiceB : IServiceBContract
{

private string serviceAEndpointAddress = " http://localhost:50171/Service1.asmx";
private BasicHttpBinding binding = new BasicHttpBinding();
[OperationBehavior]
public double Add(double A, double B)
{

ChannelFactory<IServiceAContract> factory = new ChannelFactory<IServiceAContract>(this.binding, this.serviceAEndpointAddress);
IServiceAContract channel = factory.CreateChannel();

((IChannel)channel).Open();
return channel.AddTwoNumbers(A, B);

}

}

My Client Application :

static void Main(string[] args)
{
EndpointAddress address = new EndpointAddress("http://localhost/MYWCFServiceLibrary/service.svc");
BasicHttpBinding binding = new BasicHttpBinding();
ChannelFactory<IServiceBContract> factory = new ChannelFactory<IServiceBContract>(binding,address);
IServiceBContract cchannel = factory.CreateChannel();
((IChannel)cchannel).Open();
double Result = cchannel.Add(7, 8);
((IChannel)cchannel).Close();
Console.WriteLine("Result is :", Result);
Console.ReadLine();
}

and James could you post the working sample as service A and Service B as you said above.

ramkasarla at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 10

Okay, the problem lies between the WCF service and the asmx service. Could you post the wsdl from the asmx service?

Here's my example. Note that serviceA (analog of your asmx service) implements Add not AddTwoNumbers. I mixed up these two.

Client:

Code Snippet

using System;

using System.Collections.Generic;

using System.Text;

using System.ServiceModel;

using System.ServiceModel.Channels;

namespace Client

{

classClient

{

staticvoid Main(string[] args)

{

string serviceBEndpointAddress = "http://localhost/ServiceB";

BasicHttpBinding binding = newBasicHttpBinding();

ChannelFactory<IServiceBContract> factory = newChannelFactory<IServiceBContract>(binding, serviceBEndpointAddress);

IServiceBContract clientChannel = factory.CreateChannel();

((IChannel)clientChannel).Open();

Console.WriteLine("Result: {0}", clientChannel.AddTwoNumbers(5, 6.3));

((IChannel)clientChannel).Close();

Console.WriteLine("Press Enter to quit.");

Console.ReadLine();

}

}

[ServiceContract]

publicinterfaceIServiceBContract

{

[OperationContract]

double AddTwoNumbers(double x, double y);

}

}

ServiceB:

Code Snippet

using System;

using System.Collections.Generic;

using System.Text;

using System.ServiceModel;

using System.ServiceModel.Channels;

namespace ServiceB

{

classServiceBDriver

{

staticvoid Main(string[] args)

{

string serviceBEndpointAddress = "http://localhost/ServiceB";

BasicHttpBinding binding = newBasicHttpBinding();

ServiceHost serviceBHost = newServiceHost(typeof(ServiceB), newUri(serviceBEndpointAddress));

serviceBHost.AddServiceEndpoint(typeof(IServiceBContract), binding, serviceBEndpointAddress);

try

{

serviceBHost.Open();

Console.WriteLine("ServiceB host is {0}. Press Enter to close and quit.", serviceBHost.State);

}

catch (Exception e)

{

Console.WriteLine(e);

}

Console.ReadLine();

}

}

[ServiceContract]

publicinterfaceIServiceBContract

{

[OperationContract]

double AddTwoNumbers(double x, double y);

}

[ServiceBehavior]

publicclassServiceB : IServiceBContract

{

privatestring serviceAEndpointAddress = "http://localhost/ServiceA";

privateBasicHttpBinding binding = newBasicHttpBinding();

[OperationBehavior]

publicdouble AddTwoNumbers(double x, double y)

{

Console.WriteLine("ServiceB method: AddTwoNumbers({0}, {1}) invoked.", x, y);

// I use the contract defined in the ServiceA namespace.

ChannelFactory<SERVICEA.< FONT>IServiceAContract> factory = newChannelFactory<SERVICEA.< FONT>IServiceAContract>(this.binding, this.serviceAEndpointAddress);

ServiceA.IServiceAContract channel = factory.CreateChannel();

((IChannel)channel).Open();

double returnValue = channel.Add(x, y);

((IChannel)channel).Close();

return returnValue;

}

}

}

ServiceA:

Code Snippet

using System;

using System.Collections.Generic;

using System.Text;

using System.ServiceModel;

namespace ServiceA

{

classServiceADriver

{

staticvoid Main(string[] args)

{

string serviceAEndpointAddress = "http://localhost/ServiceA";

BasicHttpBinding binding = newBasicHttpBinding();

ServiceHost serviceAHost = newServiceHost(typeof(ServiceA), newUri(serviceAEndpointAddress));

serviceAHost.AddServiceEndpoint(typeof(IServiceAContract), binding, serviceAEndpointAddress);

try

{

serviceAHost.Open();

Console.WriteLine("ServiceA host is {0}. Press Enter to close and quit.", serviceAHost.State);

}

catch (Exception e)

{

Console.WriteLine(e);

}

Console.ReadLine();

}

}

[ServiceContract]

publicinterfaceIServiceAContract

{

[OperationContract]

double Add(double a, double b);

}

[ServiceBehavior]

publicclassServiceA : IServiceAContract

{

[OperationBehavior]

publicdouble Add(double a, double b)

{

Console.WriteLine("ServiceA method: Add({0}, {1}) invoked.", a, b);

return a + b;

}

}

}

-James

JamesOsborne-MSFT at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 11

My WebService WSDL is as follows:

Code Snippet

<?xml version="1.0" encoding="utf-8" ?>

- <wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tns="http://tempuri.org/" xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
- <wsdl:types>
- <s:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/">
- <s:element name="HelloWorld">
<s:complexType />
</s:element>
- <s:element name="HelloWorldResponse">
- <s:complexType>
- <s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="HelloWorldResult" type="s:string" />
</s:sequence>
</s:complexType>
</s:element>
- <s:element name="AddTwoNumbers">
- <s:complexType>
- <s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="A" type="s:double" />
<s:element minOccurs="1" maxOccurs="1" name="B" type="s:double" />
</s:sequence>
</s:complexType>
</s:element>
- <s:element name="AddTwoNumbersResponse">
- <s:complexType>
- <s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="AddTwoNumbersResult" type="s:double" />
</s:sequence>
</s:complexType>
</s:element>
</s:schema>
</wsdl:types>
- <wsdl:message name="HelloWorldSoapIn">
<wsdl:part name="parameters" element="tns:HelloWorld" />
</wsdl:message>
- <wsdl:message name="HelloWorldSoapOut">
<wsdl:part name="parameters" element="tns:HelloWorldResponse" />
</wsdl:message>
- <wsdl:message name="AddTwoNumbersSoapIn">
<wsdl:part name="parameters" element="tns:AddTwoNumbers" />
</wsdl:message>
- <wsdl:message name="AddTwoNumbersSoapOut">
<wsdl:part name="parameters" element="tns:AddTwoNumbersResponse" />
</wsdl:message>
- <wsdl:portType name="Service1Soap">
- <wsdl:operation name="HelloWorld">
<wsdl:input message="tns:HelloWorldSoapIn" />
<wsdl:output message="tns:HelloWorldSoapOut" />
</wsdl:operation>
- <wsdl:operation name="AddTwoNumbers">
<wsdl:input message="tns:AddTwoNumbersSoapIn" />
<wsdl:output message="tns:AddTwoNumbersSoapOut" />
</wsdl:operation>
</wsdl:portType>
- <wsdl:binding name="Service1Soap" type="tns:Service1Soap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
- <wsdl:operation name="HelloWorld">
<soap:operation soapAction="http://tempuri.org/HelloWorld" style="document" />
- <wsdl:input>
<soap:body use="literal" />
</wsdl:input>
- <wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
- <wsdl:operation name="AddTwoNumbers">
<soap:operation soapAction="http://tempuri.org/AddTwoNumbers" style="document" />
- <wsdl:input>
<soap:body use="literal" />
</wsdl:input>
- <wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
- <wsdl:binding name="Service1Soap12" type="tns:Service1Soap">
<soap12:binding transport="http://schemas.xmlsoap.org/soap/http" />
- <wsdl:operation name="HelloWorld">
<soap12:operation soapAction="http://tempuri.org/HelloWorld" style="document" />
- <wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
- <wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
- <wsdl:operation name="AddTwoNumbers">
<soap12:operation soapAction="http://tempuri.org/AddTwoNumbers" style="document" />
- <wsdl:input>
<soap12:body use="literal" />
</wsdl:input>
- <wsdl:output>
<soap12:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
- <wsdl:service name="Service1">
- <wsdl:port name="Service1Soap" binding="tns:Service1Soap">
<soap:address location="http://localhost:50171/Service1.asmx" />
</wsdl:port>
- <wsdl:port name="Service1Soap12" binding="tns:Service1Soap12">
<soap12:address location="http://localhost:50171/Service1.asmx" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
ramkasarla at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 12

Try renaming every reference of IServiceAContract to Service1Soap. "Service1Soap" is the name of the contract which defines the AddTwoNumbers operation. I think when the asmx service received the message with action ending in IServiceAContract\AddTwoNumbers, it didn't know what to do with it. I think it's expecting something more like ...\Service1Soap\AddTwoNumbers, based on the wsdl.

HTH,

James

JamesOsborne-MSFT at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 13

Hi James,

I did changes in my code as you suggested but and I am getting the exception as follows:

System.Web.Services.Protocols.SoapException: Server did not recognize the value of HTTP Header SOAPAction: http://tempuri.org/Service1Soap/AddTwoNumbers.

Is there any other way to achieve my task, as I said above .

ramkasarla at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...
# 14

I think the underlying issue here is that your intermediary service sends a message with the soap action: http://tempuri.org/IServiceAContract/AddTwoNumbers, or, more recently, http://tempuri.org/Service1Soap/AddTwoNumbers. I think I was totally wrong with my last posting. We need to make your intermediary service send messages with the soap action: http://tempuri.org/AddTwoNumbers, based on this line in the WSDL:

Code Snippet

<soap12:operationsoapAction="http://tempuri.org/AddTwoNumbers" style="document" />

To do this, we can modify the OperationContract on the intermediary service.

Code Snippet

[ServiceContract]

public interface IServiceAContract

{

[OperationContract(Action = "http://tempuri.org/AddTwoNumbers")]

double AddTwoNumbers(double A, double B);

}

Basically, this will define the action created on the SOAP messages using this Operation.

That should take care of the Soap Action mismatch. Let me know if it doesn't work.

-James

JamesOsborne-MSFT at 2007-10-3 > top of Msdn Tech,Visual Studio Orcas,Windows Communication Foundation (Indigo)...

Visual Studio Orcas

Site Classified