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.

 

8 Comments

  • 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

  • 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 ...

  • 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

  • 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.

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



    Paul Ballard, Editor

    TheServerSide.NET

  • 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.



  • 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

  • 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?

Comments have been disabled for this content.