Overloading Web Services
Prologue
My last few posts have dealt with my re-purposing of an XML data feed to a web service. Honestly, this is the first time that I have created a web service that I put into production, so I haven't had a lot of experience with it. In creating the web service, I found some interesting things regarding overloading of functions in a web service.
Problem
We want to create a web service that has a function that can take any number of a few parameters. We'll continue with the train schedule example from the last post. We want to expose a function to return the Schedule i.e. GetSchedule. But, we may or may not want to pare that down using parameters for things like date, time or rail company.
Solution
We'll need to do some setup for our application, so start a new ASP.Net project. We'll need to create a web service and then consume the same service. For this article, I'm still using VS.Net 2005 so I'll actually be creating a web service rather than a WCF component. Right click and select Add New Item and add a web service. Name it OverloadingWS. We'll also create a page to consume our web service, so add a web form and name it OverloadWSTest.aspx. We will need to create a web reference to the web service so that VS will create the stubs for us. Right click and select add web reference. Select 'Web services in this solution' and select our 'OverloadingWS' service. You should notice that there is one function, and it is named 'HelloWorld'. Give the Web Reference the name of OverLoadingWSStub, and add the reference.
Ok, let's go back to our web form drop on a textbox and name it txtResults. Let's delve into some code. Go to the code behind for the web form and create global variable for our OverLoadingWSStub object. Add the following as a global variable:
Dim objOLWSStub As New OverloadingWSStub.OverloadedWS
Ok, now we're ready to start creating our web service. Let's go to the code behind for the web service and create two functions to get our schedule, one will be an overload of the other. Add the following:
<WebMethod()> _
Public Function GetSchedule() As String
Return GetSchedule(DateTime.Now().ToString())
End Function
<WebMethod()> _
Public Function GetSchedule(ByVal aDate As String) As String
Return "Retrieved a schedule for: " & aDate
End Function
You'll see that one function just passes an argument to the other. If you don't specify a time, then we just figure you mean right now, and pass that to the other function that does the real "processing," pretty typical for overloaded functions.
Now let's go back to our web form and call our GetSchedule() function and see what happens. Attempt to build our solution and see what happens. An error, "There was an error downloading "http:..../OverloadedWS.asmx?disco." etc. What's going on?
Well, overloading functions in a web service is more complicated than it sounds. Basically, web services name the soap messages based on the function name, and according to standards, you cannot have two soap messages with the name but have them represent different things. I found a great description of the problem in an article:
The problem is that the VisualStudio.NET tries to name the two SOAP messages the same since the SOAP message name comes from the function name. The SOAP message is the name used in the XML SOAP protocol for the method. The problem is that SOAP cannot handle duplicate message names representing different things.
So, is there a solution? Sort of. Again from the article referenced above:
However, the VS.NET IDE provides a nice workaround through the use of the "MessageName" parameter on the WebMethod attribute. This parameter changes the message name in the SOAP XML protocol so there is no conflict. We suggest that you make this name verbose so that other people that are not using VS.NET can interpret it correctly.
So, if we WANT to use overloaded functions we can, HOWEVER there is a major caveat, this only works if you only consume the web service using .Net. From another good article by Julie Lerman on the subject:
Three years ago, it seemed like a good idea to me...
However, now my perception has changed and it's important to note that just because you can do it, doesn't mean it's a good thing. It's the OO way, for sure, but it just does not jibe with messaging and contracts and it does not conform to WSI Basic profile which demands unique names for operations (web methods). So if you have any intentions of going outside of .NET with your messaging, don't do it. A contract needs to be clearly defined and by providing overloads, that just blows the contract away.
SOoooo, we can do it, but it isn't a good idea. If you want to do it, you just need to add some information to our web method declaration as follows:
<WebMethod(MessageName:="GetScheduleByDate")> _
We also need to change our WebServiceBinding at the top of our web service class so that it knows we are breaking the rules. Change the WebServiceBinding line as follows:
<WebServiceBinding(ConformsTo:=WsiProfiles.None)> _
Effectively we are just saying that our web service doesn't conform to any kind of standard. Now if we build our application, it should build and if we go back to our web form, and add a line in the page_load we can see that our function is overloaded, type the following and notice when you hit ( after GetSchedule, you get an overloaded function:
txtResults.Text = objOLWSStub.GetSchedule()
Epilogue
I was going to overload my function about a dozen different ways when I created our web service, but I found that really I wasn't following standards to do it, and like Julie mentioned in her post, then we (and anyone who takes over my coding) has to know that I broke the rules. And, who knows, perhaps we'd access the web service it from something other than .Net (such as Adobe Flash). In the end, I decided to be a good boy and NOT overload, but rather use distinct function names instead.