Using web services locally

Aug 2, 2004 Update: Download the sample project for this post.

I am currently working on a system that utilizes asmx-based SOAP methods for inter-server communication.  While it needs to work in a distributed environment, it also have needs to be installed on a single server.  In this configuration, the web services could be installed on the server under different virtual directories.  However, this adds overhead and addition setup requirements to the web application.  While I realize that remoting already support switching between local and remote objects, I need to do it in an asmx world.

To solve this problem, I decided to build a proxy that would intercept the SOAP calls and execute the object directly.  This allows all of my existing code to remain the same in both environments.

First, I utilize a factory method for creating my web service objects.  I've found this is always a good idea since you can initialize parameters such as timeouts from a single location.  In this case, the factory uses my new class WSProxy to support local object calls.  The Run.SOAP.Locally configuration setting makes switching back and forth easy. 

Since System.Web.Services.Protocols.HttpWebClientProtocol is a decendent of MarshalByRef, we can create a proxy that intercepts the calls and directs them to the wstest.Service1 class. Note: the assembly for the web service must be referenced by our web application to support local calls.

 private wstestsvc.Service1 wstestFactory()
 {
   bool remote=bool.Parse(
System.Configuration.ConfigurationSettings.AppSettings["Run.SOAP.Locally"]); wstestsvc.Service1 wstestsvc=new wstestsvc.Service1(); if (!remote) wstestsvc=(wstestsvc.Service1) WSProxy.CreateProxy(wstestsvc,new wstest.Service1()); return wstestsvc; }


The following snippet is an example of calling the typical HelloWorld() method on our object. 

 private void button1_Click(object sender, System.EventArgs e)
 {      
   wstestsvc.Service1 wstestsvc=wstestFactory();
   label1.Text=wstestsvc.HelloWorld();
 } 


Now for the code that does all of the work!  WSProxy is an implementation of a RealProxy. For more information about proxies, there's a good article on CodeProject.  Whenever a method is called on the web service class, the WSProxy.Invoke method is passed a message with the method name and arguments.  Since web service client stubs are always decended off of System.Web.Services.Protocols.HttpWebClientProtocol, we'll just capture and redirect the methods from the decendent type with reflection.

using System;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Proxies;
using System.Web.Services.Protocols;
using System.Runtime.Remoting.Messaging;






namespace
wstest_client { public class WSProxy : RealProxy { HttpWebClientProtocol target; object targetObj; Type targetType; public WSProxy(HttpWebClientProtocol webclient, object targetObj) : base(webclient.GetType()) { target=webclient; targetType=targetObj.GetType(); this.targetObj=targetObj; } public static object CreateProxy(HttpWebClientProtocol webclient, object targetObj) { return new WSProxy(webclient, targetObj).GetTransparentProxy(); } public override IMessage Invoke(IMessage message) { // Convert to a MethodCallMessage IMethodCallMessage methodMessage = new MethodCallMessageWrapper((IMethodCallMessage)message); MethodBase method=methodMessage.MethodBase; object[] parameters=methodMessage.Args; object returnValue; // We'll redirect the web service method (which will be declared in the decendent type) if (method.DeclaringType==target.GetType()) method=targetType.GetMethod(method.Name); // Execute the method returnValue=method.Invoke(targetObj, parameters); // Create the return message (ReturnMessage) ReturnMessage returnMessage =
new ReturnMessage(returnValue, methodMessage.Args,
methodMessage.ArgCount, methodMessage.LogicalCallContext, methodMessage); return returnMessage; } } }

This seems to be a good solution to my problem and would provide other opprotunities for extensions.  Is anyone else running into a similar situation?  I can post a demo project if anyone is interesting.

 

Published Monday, August 02, 2004 11:05 PM by brian_ritchie
Filed under:

Comments

Tuesday, August 03, 2004 2:09 AM by Marc Hoeppner

# re: Using web services locally

Yepp, I am interested :) - We are planing to do exactly what you did, but I did not have time so far to do a prototype. Basically, a lot of our installations are single-server, so the additional overhead is indeed a problem. This way it seems that you have the best of both worlds!!

Cheers,

Marc
Tuesday, August 03, 2004 9:44 PM by Brian Ritchie

# re: Using web services locally

You can download the sample project for this post here:
http://www.dotnetpowered.com/download/wsproxy.zip
Tuesday, August 03, 2004 9:49 PM by TrackBack

# Local web services sample project

Wednesday, August 04, 2004 3:25 PM by Pascal Recchia

# re: Using web services locally

Great, it's a good idea.

We have not yet decided between what you present and the development of a custom transport protocol that we would use in the wsdl document. The custom transport protocol will offer the mode inproc to call web service locally (<soap:address ... or <class:address ... assembly class configuration ...).

But the second solution (custom transport) seems more expensive for a profit not obvious ...
Thursday, August 05, 2004 3:33 AM by Deyan Petrov

# re: Using web services locally

Can't the Microsoft.Web.Services2.Messaging.SoapInprocTransport and the WSE 2.0's SoapClient and SoapService be used to achieve the same goal?

Best regards,
Deyan
Thursday, August 05, 2004 4:59 AM by Pascal Recchia

# re: Using web services locally

Deyan, Good news.
We'll see it before we plan the development of our own custom transport.
We didn't see in detail the last version of WSE.

Regards.
Thursday, August 05, 2004 6:13 AM by TrackBack

# Local Web Services?

Thursday, August 05, 2004 10:14 AM by Paul Ballard

# re: Using web services locally

Congratulations, this blog has been linked to from TheServerSide.NET!

Paul Ballard, Editor
TheServerSide.NET
Thursday, August 05, 2004 3:49 PM by anon

# re: Using web services locally

any chance you could use reasonable sized margins so that this document will print correctly? Even at landscape, the righthand side of the document gets chopped off...not to mention on a 1024x768 screen you get a horizontal scrollbar two screen widths long.....
Thursday, August 05, 2004 3:59 PM by TrackBack

# Testing web services locally - faster

Thursday, August 05, 2004 11:03 PM by Brian Ritchie

# re: Using web services locally

Thanks Paul and the reset of TheServerSide.NET crew for listing my post! I've adjusted the code sample to keep it onto the smaller screens.

Deyan, I've just started looking at what was added in WSE2 and I'll take a look at Microsoft.Web.Services2.Messaging.SoapInprocTransport.

Friday, August 06, 2004 3:46 AM by Deyan Petrov

# re: Using web services locally

Hi guys,

It would be nice if you share the results of your investigation of WSE2, as I have just briefly looked at the SoapInprocTransport and haven't even created a test sample yet. Not much docs/samples on this in the SDK either ...

Best regards,
Deyan
Friday, August 06, 2004 3:49 AM by Deyan Petrov

# re: Using web services locally

Hmm, in fact I just stumbled upon this:

http://weblogs.asp.net/cschittko/articles/25901.aspx

Friday, August 06, 2004 4:21 AM by Pascal Recchia

# re: Using web services locally

Hi,

Great.

We invoke dynamically web services. We use a tool "dynamic proxy" based on C. Weyer user gotdotnet sample (http://www.gotdotnet.com/Community/UserSamples/Details.aspx?SampleGuid=35c47ebb-d806-4995-8797-a42251a8ace3)

We added at this sample the support of asynchronous calls and a first dictionnary of keys (urn) and values (wsdl).

In collaboration with C Weyer, we were to look at how to benefit from WSE2.

I want to declare the use of SoapInprocTransport in the wsdl document :

<soap:address location="assembly, class" ...

or

<class:address type="assembly, class"
property1="xxx"
property2="xxx" ...

And I want to implement another feature to gain in performance, the "dynamic proxy" could detect if the web service from its wsdl document (and its physical location - soap:address) is on the same machine and so calls the web service locally.
Friday, August 06, 2004 4:02 PM by kevin sun

# re: Using web services locally

i am wondering what kind of performance improvement you are getting from direct method invocation. if it is a db app, the performance bottle neck should be in the data access layer.

also with your local proxy, do you have to introduce direct dependency from web UI to the business logic (which was shielded by the web service before)?

could you give a quantitative comparison on the performance gain?
Tuesday, August 10, 2004 4:35 PM by TrackBack

# Weekly Links

Tuesday, August 10, 2004 4:36 PM by TrackBack

# Weekly Links

Thursday, August 19, 2004 12:29 AM by TrackBack

# [WsContractFirst] Test-First for Web Services

[WsContractFirst] Test-First for Web Services
Wednesday, August 20, 2008 4:56 AM by Webszolg??ltat??sok helyi h??v??sa - gaba feljegyz??sei

# Webszolg??ltat??sok helyi h??v??sa - gaba feljegyz??sei

Pingback from  Webszolg??ltat??sok helyi h??v??sa - gaba feljegyz??sei

Leave a Comment

(required) 
(required) 
(optional)
(required)