Migrating from the Microsoft SOAP Toolkit to the .NET Framework
Microsoft® .NET Framework
Microsoft® SOAP Toolkit
Microsoft® Visual Basic®
Web Services Enhancements for Microsoft .NET
Summary: Take the next step in developing Web services by using managed code in the .NET Framework, and see how to incrementally transition your SOAP Toolkit-based Web service applications. (14 printed pages)
The Basics... Your First Managed Web Service for SOAP Toolkit Developers
The Basics... Calling Web Services in Managed Code for SOAP Toolkit Developers
The Migration Approach
The Natural Solution: Drawing the Migration Line at the Wire
Another Migration Option: Doing as Little Work as Possible to Remove the SOAP Toolkit
What About SOAP Toolkit Feature 'X'?
Since the Microsoft SOAP Toolkit released in June 2000, a number of advances in developing for the Windows platform—most notably the release of the .NET Framework—raised Web services to the status of a "first class citizen" in terms of application development. This article discusses the features of the SOAP Toolkit, the first Microsoft tool for building and calling Web services, and how to migrate SOAP Toolkit applications to use the .NET Framework.
The Microsoft SOAP Toolkit provides a mechanism for wrapping a COM object in a Web service and exposing its methods via SOAP messages. Similarly, you can take a WSDL file for an existing Web service and bind a proxy object to it, which allows the methods from the Web service to be called as if they are methods on the proxy object. While this functionality is useful, it can also be developed more easily using the .NET Framework, often with better results. I will show you how to accomplish the same thing in managed code using the .NET Framework, as well as look at ways to incrementally transition your Web service applications to the .NET Framework.
Why are we doing this?
There are a number of very good reasons why you would want to move your Web services to the .NET Framework if you have code that uses the SOAP Toolkit or you are developing with the SOAP Toolkit.
First and foremost, Web services have come quite a ways since the SOAP Toolkit was released. Probably the single biggest advance is in interoperability. This is due in large part to the WS-I Basic Profile, which outlines rules for making Web services that will interoperate easily. Arguably the biggest rule in the Basic Profile is that RPC-encoded Web services should not be used. RPC encoding was quite popular with first-generation Web services, but interoperability problems are more likely to occur with RPC encoding. Therefore, the Basic Profile solution to avoid these problems is to use document/literal Web services. The SOAP Toolkit can communicate with document/literal Web services, but by default it creates RPC-encoded Web services.
In addition, the SOAP Toolkit uses a late-bound approach to consuming Web services. This means, among other things, that errors are not found until runtime that, in an early-bound scenario, are found at compile time. This might not seem like a big problem, but it could have disastrous results if the runtime error is not detectable to the application. There are many scenarios where changes to a Web service interface could be undetected in a late-bound scenario and would generate faults in an early-bound scenario. Obviously, these sorts of problems are rife with security vulnerabilities.
By the way, if you haven't heard this already, the .NET Framework rocks! The .NET Framework is much easier to program in than programming in the unmanaged world; it is less susceptible to buffer overruns, memory leaks, and security privilege elevations; and it ultimately provides Web service support as an integral part of the platform. Also, if you compare the performance of a typical scenario where a Visual Basic® 6.0 object is being exposed as a Web service, a completely managed solution is significantly faster.
The world of Web services continues to burst with innovation. For Microsoft®, that means improving upon the .NET Framework implementation of Web services. For instance, Web Services Enhancements for Microsoft .NET provides higher-level services for the .NET Framework that save developers lots of time when it comes to implementing security in their Web service applications. The Microsoft long-term vision for Web services and Indigo ("Indigo" is a set of .NET technologies for building and running connected systems, and is a feature of the next version of Windows code-named "Longhorn") still rests on and in the .NET Framework. So if you want to take advantage of Web service innovation from Microsoft in the future, you should be running the .NET Framework.
Finally, as an inverse corollary to "all innovation at Microsoft is going into .NET Framework Web services," it is true that "no innovation at Microsoft is going into the SOAP Toolkit." Specifically, full support for the SOAP Toolkit is ending in April 2005. If you have been using the SOAP Toolkit, I cannot imagine that you haven't already considered moving to the .NET Framework. Hopefully this information serves as a subtle nudge in that direction.
If you learned to program Web services using the SOAP Toolkit, then you are probably well aware of a typical first Web service written to add two numbers together. The process using the SOAP Toolkit is to write a COM DLL that exposes an IDispatch method. I did this using Visual Basic 6.0 and my method looked like this:
Function Add(x As Integer, y As Integer) As Integer Add = x + y End Function
Then I ran the generated COM object through the SOAP Toolkit WSDL Generator utility that created a Project1.WSDL and a Project1.WSML file. The WSDL file describes the Web service interface and the WSML file maps the calls to the proper COM server—in this case, the just-built DLL. Next I used the SOAPVDIR.CMD batch file to configure the virtual directory to use the SOAP Toolkit ISAPI listener, and the Web service was created.
It is important to note that, by default, the SOAP Toolkit WSDL Generator tool creates a WSDL file that uses an RPC/Encoded option that is not WS-I Basic Profile-compliant.
It becomes a little simpler to create a similar, integer-adding Web service using managed code (i.e., using the .NET Framework). The single biggest hurdle may be to get the .NET Framework installed on the machine. Once this is done, all there is to do is create a virtual directory on the IIS server and create a file such as MATH.ASMX listed below.
<%@ WebService Language="VB" Class="MathService" %> Imports System Imports System.Web.Services Public Class MathService : Inherits WebService <WebMethod> _ Public Function Add(A As Integer, B As Integer) As Integer Return A + B End Function End Class
At first glance, this may seem more complicated than the previous Visual Basic code, but realize that the method I wrote in Visual Basic 6.0 was wrapped in a rather large Class1.cls file that was part of a Visual Basic 6.0 project, and so forth. This ASMX file is all that is needed. There is no need to register a COM object. Also, by default, the .NET Framework creates Web services using the Document/Literal approach instead of the RPC/Encoded approach. Document/Literal is WS-I Basic Profile-compliant.
Using Visual Basic .NET, the job is even easier and the project creation wizards do all the work. Just insert the code for the Add function and flag it with the <WebMethod> attribute.
For more information on attributes and migrating Visual Basic 6.0 code to Visual Basic .NET, see Language Changes in Visual Basic.
Writing a Web service is one thing, but you also want to know how to call a Web service. The SOAP Toolkit would use the following code to call the Add method created by the earlier Web service.
set soapClient3 = CreateObject("MSSOAP.SoapClient30") Call soapClient3.mssoapinit("Project1.wsdl", "", "", "") Ret = soapClient3.Add(3, 2)
This requires that Project1.wsdl be in the current directory, although you can also specify a URL for the first parameter to the mssoapinit function call.
The SOAP Toolkit uses a late-bound approach to calling a Web service. That means that it figures out at runtime what the details of the WSDL Web service definition are and how to call the Web service based on that information. This is opposed to an early-bound approach where the interface described by the WSDL is determined at design time when the programmer is writing the code. The problem with the late-bound approach is that you really don't know if the interface you are calling has changed. In the best-case scenario, the code written fails due to the method name changing, or because the function description has drastically changed. In the worst-case scenario, the SOAP Toolkit could coerce the method call into a valid SOAP request to the Web service, which could then cause unforeseen results that neither the client or server would be aware of.
An early-bound approach sends an XML message that is fully defined with the namespace the developer intended to send. If the Web service interface has changed, then a SOAP fault would be returned indicating the inconsistency in the message with what was expected. There is less of a chance that you might execute code on the server that was not intended and the vulnerabilities for data corruption and security breaches are reduced.
Making a call to the Web service from managed code requires binding the code to the Web service interface at design time. To do this, you supply the WSDL to the .NET Framework WSDL.EXE tool. WSDL.EXE takes the WSDL for a Web service as input and generates a class that can then be used to call the Web service from within your managed code.
In the case of the sample Web service, you could run WSDL.EXE with the following command line:
WSDL /l:VB http://mattpo/STKMigration/Math.ASMX?WSDL
For my particular example, this created a Visual Basic .NET file called MathService.vb. This class can now be used in the code for a console application. The entire listing for the file AddThem.vb is listed below.
Class AddThem Public Shared Sub Main() Dim proxy as MathService = new MathService() Dim ret as Integer = proxy.Add(2, 3) System.Console.WriteLine(ret) End Sub End Class
Next, compile this code with the .NET Framework Visual Basic compiler. The following command line will do the trick:
vbc /r:System.dll /r:System.Web.Services.dll /r:System.Xml.dll /out:AddThem.exe MathService.vb addthem.vb
The /r parameters indicate reference DLLs needed to compile the Web service proxy class. What's created is an application called ADDTHEM.EXE that invokes the Web service and returns the result.
Of course, with Visual Studio .NET this task is even easier. To create an application like the ADDTHEM.EXE program just created, start by choosing to create a Visual Basic .NET Console Application. In the Solution Explorer, right-click the project and choose Add Web Reference... A wizard launches that allows us to browse to the WSDL for the Web service and then it creates the proxy class just as the WSDL.EXE utility did from the .NET Framework SDK. Finally, add the same 3 lines of code to the Main function as in the previous example and the application is written. Choose Build from the menu and get a working .NET-based application that calls the Web service and reports the results.
The .NET Framework supports all the SOAP Toolkit features for doing things such as adding HTTP authentication, adding an HTTP proxy server, and sending the request over SSL. The following code calls the Web service again, but this time, sets HTTP authentication information, changes the URL where the Web service is located, and sets an HTTP proxy server.
Class AddThem Public Shared Sub Main() Dim proxy As MathService = New MathService() proxy.Url = "http://mattpo/STKMigration/Math.asmx" proxy.Credentials _ = New System.Net.NetworkCredential("Joe", "Password") proxy.Proxy _ = New System.Net.WebProxy("http://svcproxy:8080") Dim ret As Integer = proxy.Add(2, 3) System.Console.WriteLine(ret) End Sub End Class
When moving from the SOAP Toolkit to .NET, one option is to rewrite your entire Web service application using managed code. In certain circumstances where there is not a lot of code to move or where a .NET approach is mandated for other reasons, this might be a viable solution. However, a lot of business scenarios will require that migration of a Web service application occur incrementally. Let's look at several approaches for transitioning from a SOAP Toolkit Web service application to a .NET Web service application.
As a starting point for my discussion, I will take a typical Web service application scenario where the SOAP Toolkit was used to expose a COM object as a Web service. Consuming this Web service is a smart client application written in Visual Basic 6.0. Also consuming this Web service is an ASP Web application. Both the smart client Visual Basic 6.0 application and the ASP Web application are using the SOAP Toolkit to communicate with the Web service. Figure 1 shows how these applications work using the SOAP Toolkit.
Figure 1. A smart client and a Web application consume a Web service. All Web service communication is accomplished using the SOAP Toolkit.
The Web service shown in Figure 1 is created using a COM object written with Visual Basic 6.0. It is exposed as a Web service using the SOAP Toolkit and the ASP listener. Consuming this Web service are two clients—the Visual Basic 6.0 client and an ASP client—both using the SOAP Toolkit to create a proxy in order to call the Web service. Notice that communication happens over RPC-encoded SOAP that is not WS-I Basic Profile-compliant.
Migrating the ASP client to ASP.NET
Migrating this SOAP Toolkit solution to a .NET Framework solution can be done in a number of ways. We will look at some of these shortly. One migration option is to migrate the ASP client to an ASP.NET client. You could conceivably do this by 1) using the .NET Framework Web service client capabilities, or 2) using a SOAP Toolkit proxy from within your ASP.NET code. This second option is ridiculous, because if you are running ASP.NET pages, then you already are using the .NET Framework so you can use the .NET Framework to call your Web service. This avoids the COM interoperability layer and is a much more consistent and integrated approach. Working under the assumption that if you are migrating an ASP Web service client application to managed code, you will use the .NET Framework to talk to the Web service. My discussion will focus on other migration options shown on the bottom client in Figure 1: the smart Visual Basic 6.0 client accessing the ASP service, both using the SOAP Toolkit.
If you take the various pieces of the application: the client application, the SOAP Toolkit proxy object, the Web service, and the wrapped COM object, any and all of these portions of the unmanaged solution could be incrementally migrated to a managed solution using the .NET Framework. Figure 2 illustrates some of the permutations of options that you could use to migrate from an unmanaged solution to a managed solution.
Figure 2. Migration options for moving from a SOAP Toolkit solution to a .NET Framework solution
This is not an exhaustive list, but it does illustrate how there should be a migration path that fits your particular business needs.
Next I will be talking about something I call migration lines. A migration line is where the boundary occurs between managed and unmanaged code. This line can be placed at several places for any particular migration point in time and is represented in Figure 2 as the boundary between the yellow (unmanaged) portions in the diagram and the green (managed) portions of the diagram.
Web services are about interoperability, and this is possible because the wire protocol is concretely defined. This is true for Web services talking between different platforms, and is also true for Web services that communicate between different toolkits, such as the .NET Framework and the SOAP Toolkit. Figure 3 shows a managed/unmanaged Web service application in mid-migration where the migration line is drawn on the wire.
Figure 3. Migrating to the .NET Framework with migration line at the wire protocol
If you are going to pick a two-step approach from migrating from a complete SOAP Toolkit application to a complete .NET Framework application, this is probably your best approach. If the .NET Framework is not widely distributed on your network, then installing it on the server where your Web service lives is probably a much simpler task then installing it on a potentially large number of clients. Also, you will get the performance benefits of having a complete .NET Framework solution on the server where performance counts the most.
Notice that the communication from the client to the server is happening over RPC-encoded SOAP. While this is not WS-I Basic Profile-compliant, it does mean that your Web service clients will not have to be updated. And yes, the .NET Framework is flexible enough to do RPC encoded Web services even though it does document literal Web services by default.
Steps for replacing a SOAP Toolkit Web service with a completely managed Web service
In order to implement the migration option shown in Figure 3, two main tasks need to be performed. First, the business logic of the Web service must be migrated; next, the Web service that exposes this logic must be migrated. For this example, the business logic originally lived in a COM object written with Visual Basic 6.0. I am not going to go into all, or any, of the details of migrating the business logic to a .NET managed class since this code could entail using any number of back-end technologies, but I will say that basic design practices and architectures still apply to the managed code world as they did to the unmanaged code world before.
This leaves the second task in reaching the migration point: take the business logic in the newly created managed class and expose it as a Web service. The catch is that in order for the clients to continue working unmodified, the Web service must expose the exact same interface as the previous SOAP Toolkit Web service. Luckily the WSDL.EXE tool used earlier to create the .NET Framework client proxy can also be used to build a Web service that implements the interface defined in a WSDL. A typical use of it for this kind of scenario would look like this:
WSDL /server /l:vb /out:project1.vb project1.wsdl
The result of this command would be that WSDL.EXE would take the project1.wsdl and create the project1.vb file that contains the class requested. The /l flag indicates the language (in this case Visual Basic .NET) and /server generates server-useable code instead of client-useable code. The class generated will look something like this:
<System.Web.Services.WebServiceBindingAttribute( _ Name:="Class1SoapBinding", _ [Namespace]:="http://tempuri.org/Project1/wsdl/")> _ Public MustInherit Class Project1 Inherits System.Web.Services.WebService ... End Class
This class will have a lot of attributes scattered throughout it mostly because the WSDL was not for a document/literal Web service. For the most part, these can be ignored. The main problem is with the initial class declaration that uses the MustInherit keyword.
MustInherit means that this class cannot be used directly. Instead it is telling you that another class should be created that inherits from this class and then it can override the functions that are declared and get all the benefits of the code in the original class. The problem is that .NET Framework attribution does not get passed down to any classes that inherit from this class. This means that you can override the declared functions, but you will have to recopy the complex attribution to your own function declarations in order for the expected results to occur.
It is generally accepted that with the .NET Framework version 1.1, if you use WSDL.EXE to create a server class as shown here, you should take the generated code and modify it so that it is no longer a MustInherit class, and put your code into the auto-generated class instead of creating another class that inherits from the generated class. The modifications involve 1) removing the MustInherit from the class declaration, and 2) removing the MustOverride from the specific function definitions. Once you have done this, all that remains is to insert the code into the functions that call into your business logic class.
This process should work for most SOAP Toolkit implementations, but be sure and test your new Web service well with your existing client application(s) in case there are some subtleties that get lost in translation.
Migrating the clients to managed code
Now that the first milestone in the migration path has been reached, the next job is to migrate the client applications to the .NET Framework. The details are not discussed since the Web service portion is a trivial extension of the section, The Basics... Calling Web Services in Managed Code for SOAP Toolkit Developers, earlier in this article. But there are a few items to consider when deploying a widely used .NET Framework application.
Deploying the .NET Framework. This is probably the single biggest hurdle to overcome for legacy systems that want to run managed applications. If you already have the .NET Framework on your client machines, great! If not, this can be a fairly large task. See Redistributing the .NET Framework for a good discussion of all your options for distributing the .NET Framework runtime.
Consider deploying with no-touch deployment. Since you are migrating to the .NET Framework, you might as well take advantage of some of its features that makes deploying client applications easier. No-touch deployment allows .NET Framework applications to be downloaded from a Web server. This means updates can reach all clients by updating the application image on the Web server. There are restrictions on what Web services you will be allowed to access from a no-touch application. See No-Touch Deployment in the .NET Framework for information on creating a no-touch application.
Consider moving your Web service to document literal. If you used the default SOAP Toolkit settings which create an RPC-encoded Web service, you should eventually move the Web service to use document/literal instead. Keeping it as RPC-encoded before you update your clients is probably a smart idea, but when you update your clients it is probably time to also update your Web service. This will insure better interoperability in the future and will promote more agile application development using your Web service.
Drawing the migration line down the middle at the wire protocol is one particularly nice migration strategy, but your migration strategy may be more focused on doing as little work as possible to remove the SOAP Toolkit code from your Web service application. Such a migration is illustrated in Figure 4 below.
Figure 4. Migrating only the SOAP Toolkit portions of your application
In this case, the migration line lives in two different places: on the client at the point where the application is calling into the Web service proxy, and on the server where the .NET Framework Web service uses the pre-existing Visual Basic 6.0 COM object that implemented the business logic wrapped by the Web service. Both the SOAP Toolkit proxy on the client and the SOAP Toolkit Web service running on ASP have been replaced with .NET Framework versions of the same thing.
The ability to mix and match managed and unmanaged code like this is possible via the .NET Framework support for COM interoperability. The .NET Framework allows a piece of managed code to look like a COM object using a COM Callable Wrapper (CCW). A CCW is used on the client to allow the unmanaged application the ability to call into the managed Web service proxy. The .NET Framework also allows managed code to call COM objects using a Runtime Callable Wrapper (RCW). An RCW is used on the server when the .NET Framework Web service invokes the business logic in the pre-existing COM object. For more information on using this sort of approach, see Integrating Web Services and COM Components.
There are a couple of things to note if you are using this approach. First, you might have noticed that Figure 3 uses document/literal SOAP to communicate between the client and the server. This follows the general guideline that you should move to document/literal and become WS-I Basic Profile compliant if you can. Second, one of the reasons why a completely managed Web service is faster than an unmanaged Web service is that it uses multi-threaded apartments, and the business logic class will run in a multi-threaded apartment (MTA). However, Visual Basic 6.0 creates COM objects that are apartment model, which means that they must run in single-threaded apartments (STAs). When the Visual Basic 6.0 COM object is called from the .NET Framework Web service, the call must be marshaled to a thread in an STA. This can create a performance hit compared to the previous SOAP Toolkit Web service, because the SOAP Toolkit threads were initialized as STAs and marshalling was avoided. If performance is a problem for your Web service, avoid this migration path and migrate your entire Web service to managed code, where you will experience a significant performance increase. Even though you are using only managed code on the server, you can still perform incremental migration by using the wire protocol migration line approach discussed earlier.
The SOAP Toolkit is a relatively full-featured product that has not been significantly explored in this article. Therefore, there may be some feature, X, that has not been described in terms of migrating to the .NET Framework. Rest assured that nearly every feature of the SOAP Toolkit has a similar feature in the .NET Framework. A few that come to mind:
DIME Support, while not in the .NET Framework directly, is supported by Web Services Enhancements (WSE) 1.0 and WSE 2.0, which are free downloads that integrate into the .NET Framework support for Web services.
Type Mappers is a SOAP Toolkit feature that is functionally akin to XML serialization, a fundamental part of the .NET Framework. Most scenarios that used Type Mappers in the SOAP Toolkit will be non-issues with the .NET Framework.
The Trace Utility in the SOAP Toolkit is a nice little tool to have around for looking at the SOAP messages being passed on the wire. There is no trace utility in the .NET Framework SDK, but my personal experience is that Web services have reached a level of maturity such that I rarely have to look at on-the-wire messages anymore. There are tracing capabilities built into the .NET Framework and WSE that are based off of configuration settings, but a smart-looking windows application is not included. There are, however, a number of solutions, free and for purchase, that are available from third parties.
The .NET Framework is the current and future Web services technology for Microsoft, and SOAP Toolkit-based Web service applications should be moved to the .NET Framework in order to improve interoperability, performance, and future innovation. A number of migration strategies are available that can help you find the approach to best meets your business needs. If you are new to the .NET Framework, I think you will be pleasantly surprised at its feature set and ease of use. Happy coding!