Simple data can be returned by using the HTTP protocol. Complex data (instances of structures or classes) uses the SOAP protocol. It is worth knowing when various protocols come into play. SOAP used to mean Simple Object Access Protocol ; but now SOAP is just SOAP. However, the old expansion of the acronym describes nicely what SOAP does for us. SOAP makes it possible to transport an object across a network by using the self-describing nature of XML to include the description of the data along with the data.
Writing Web Services that return complex data types requires no special effort; however, you need to be aware of some additional things that happen with the related technologies. Thus I will show you a class and a Web Service that returns data representative of data that might be returned from a clearing corporation like the National Securities Clearing Corporation (if it were using Web Services) and the resulting impacts and opportunities for the Web Service consumers.
Implementing a Complex Type
Complex data in this context refers to structures or classes. You can define a class or structure and return it from a Web Service in much the same manner as you would return a class or structure from any DLL. The class I defined for this example (Listing 14.3) represents information that might be returned by a commissions clearing Web Service.
Listing 14.3 The CommissionsData Class
Public Class CommissionsData Private FSystemCode As String Private FRecordType As String Private FClearingSettlingFirmNumber As String Private FFundProcessingDate As DateTime Private FCommissionType As String Private FDebitCreditIndicator As String Private FDebitReasonCode As String Private FSettlementIndicator As String Private FRecordDate As DateTime Public Property SystemCode() As String Get Return FSystemCode End Get Set(ByVal Value As String) FSystemCode = Value End Set End Property Public Property RecordType() As String Get Return FRecordType End Get Set(ByVal Value As String) FRecordType = Value End Set End Property Public Property ClearingSettlingFirmNumber() As String Get Return FClearingSettlingFirmNumber End Get Set(ByVal Value As String) FClearingSettlingFirmNumber = Value End Set End Property Public Property FundProcessingDate() As DateTime Get Return FFundProcessingDate End Get Set(ByVal Value As DateTime) FFundProcessingDate = Value End Set End Property Public Property CommissionType() As String Get Return FCommissionType End Get Set(ByVal Value As String) FCommissionType = Value End Set End Property Public Property DebitCreditIndicator() As String Get Return FDebitCreditIndicator End Get Set(ByVal Value As String) FDebitCreditIndicator = Value End Set End Property Public Property DebitReasonCode() As String Get Return FDebitReasonCode End Get Set(ByVal Value As String) FDebitReasonCode = Value End Set End Property Public Property SettlementIndicator() As String Get Return FSettlementIndicator End Get Set(ByVal Value As String) FSettlementIndicator = Value End Set End Property Public Property RecordDate() As DateTime Get Return FRecordDate End Get Set(ByVal Value As DateTime) FRecordDate = Value End Set End Property End Class
There isn't anything especially noteworthy about the CommissionsData class; it is comprised of fields and properties. I used the F prefix convention for fields and dropped the F prefix for properties. (Not even Microsoft is promoting the Hungarian notation anymore.)
Note that the class contains only fields and properties. We could implement methods and events for this class. However, there would be no impact on how we would implement the Web Service.
Implementing a Web Service
The CommissionsData class represents a complex type that we want to return from a Web Service. For our illustration it doesn't matter whether or not we have any data assigned to the members of CommissionsData . If we returned an instance of CommissionsData , that data would be returned along with the definition of the type when we invoked the Web method. Listing 14.4 demonstrates a simple Web method that returns a CommissionsData object.
Listing 14.4 A Web Method That Returns a CommissionsData Object
Imports System.Web.Services <WebService(Namespace:="http://tempuri.org/")> _ Public Class Service1 Inherits System.Web.Services.WebService [ Web Services Designer generated code ] <WebMethod()> _ Public Function GetData() As CommissionsData.CommissionsData Return New CommissionsData.CommissionsData() End Function End Class
If you compare the Web Services in Listings 14.2 and 14.4, you will note that there are fundamentally no differences in the mechanics of implementing the Web Services. All the differences occur when the WSDL utility generates the proxy class and when we use the Web Service that returns a complex type.
Referencing a Web Service
When you add a Web reference to a project consuming a Web Service (see Chapter 13), WSDL generates several files. These files help bridge the gap between your Web Service and the client that consumes the Web Service. If you click on the namespace created by WSDL and then click the Show All Files toolbar button, the Reference.vb proxy file will be displayed in the Solution Explorer (Figure 14.2). You can also select the Reference.vb file from Windows Explorer by navigating to it and opening that file.
Figure 14.2. Click the Show All Files button to show the Reference.vb proxy file generated by the WSDL utility.
Exploring the Proxy Class
When you have added a Web reference to the Commissions Web Service, a proxy class is added to your project. (You can open the proxy class for the Commissions Web Service and follow along as we explore.) Listing 13.3 in the previous chapter shows an entire proxy file, and of course you can create a Web Service, reference it, and examine that proxy file too. For now we are interested only in the treatment of our CommissionsData class by the WSDL utility. Listing 14.5 shows just the generated CommissionsData proxy file.
Listing 14.5 The CommissionsData Proxy File Generated by WSDL
<System.Xml.Serialization.XmlTypeAttribute( _ [Namespace]:="http://tempuri.org/")> _ Public Class CommissionsData '<remarks/> Public RecordDate As Date '<remarks/> Public SystemCode As String '<remarks/> Public SettlementIndicator As String '<remarks/> Public RecordType As String '<remarks/> Public ClearingSettlingFirmNumber As String '<remarks/> Public FundProcessingDate As Date '<remarks/> Public CommissionType As String '<remarks/> Public DebitCreditIndicator As String '<remarks/> Public DebitReasonCode As String End Class End Namespace
Note that the generated proxy class contains only fields ”no properties, methods, or events (or anything else, for that matter). I refer to this as flattening the complex type. All our properties become public fields in the proxy class. If you think about this for a minute, it makes sense.
If Web Services returned fat objects ” with methods, events, properties, and other code ”Web Services would have to return all the assemblies and related assemblies across the wire. This means that Web Services would have to download and install binary files. For complex Web Services you might be referring to half of the CLR. Clearly, sending the CLR across the Web for each Web method invocation would not be a good thing. Even worse is that you might be dragging third-party code onto your workstation or server. (You don't know where that code has been.) Instead Web Services return a sanitary proxy class capable of containing data. So, in a way we are back to data-only data types, but just for Web Services. (Later in this chapter I will show you how to return fat objects from Web Services.)
Implementing the Web Service Consumer
The mechanism behind the WSDL tool is (probably) the CodeDOM. The CodeDOM contains classes that can generate very complex code. Thus far we know that almost everything about returning complex types in a Web Service is no more challenging than returning a simple type. The big difference is that the Web Service has the real class and the consumer gets a fields-only replica.
Clearly, you will not be able to invoke operations on the proxy class returned by the Web Service, but you can use the data. Listing 14.6 demonstrates a simple use of the proxy CommissionsData class: I use the fields information to generate a simple user input form. (This is for fun as much as for the hope that you'll find it a useful application for Reflection.)
Listing 14.6 Using Reflection to Generate a Simple User Interface
1: Imports Service = CommissionsDataApp.localhost.Service1 2: Imports System.Reflection 3: 4: Public Class Form1 5: Inherits System.Windows.Forms.Form 6: 7: [ Windows Form Designer generated code ] 8: 9: Private Sub Form1_Load(ByVal sender As System.Object, _ 10: ByVal e As System.EventArgs) Handles MyBase.Load 11: 12: Dim Service As Service = New Service() 13: 14: GenerateForm(Service.GetData().GetType()) 15: 16: End Sub 17: 18: Private Sub GenerateForm(ByVal Type As Type) 19: 20: Dim Info As FieldInfo 21: Dim Y As Integer = 0 22: Dim Label As Label 23: Dim TextBox As TextBox 24: 25: For Each Info In Type.GetFields() 26: 27: Label = New Label() 28: Label.Text = Info.Name 29: Label.Location = New Point(10, Y) 30: Label.AutoSize = True 31: Controls.Add(Label) 32: 33: TextBox = New TextBox() 34: TextBox.Location = New Point(20 + Label.Width, Y) 35: Controls.Add(TextBox) 36: Y += 25 37: 38: Next 39: 40: End Sub 41: End Class
The code in Listing 14.6 is direct. I used the aliasing trick to shorten the namespace reference in line 1. I imported the System.Reflection namespace, which contains the FieldInfo class used to reflect the CommissionsData proxy class in lines 20 and 25. When the form loads (lines 12 and 14), I create an instance of the service and a simple user interface (Figure 14.3).
Figure 14.3. A user interface dynamically generated by using Reflection.
A practical application might be a form generator. For a commercial application we might employ the CodeDOM and generate actual code, supporting programmer customization. More likely, though, you will be using the data returned by the Web Service.
Downloading binary assemblies with executable code would not be secure. However, what if you download code that you know is reliable? For example, Microsoft supports returning DataSet objects from a Web Service, and these include methods too. Because the DataSet class contains known code from Microsoft and it is assumed that you have the CLR on the machine using the DataSet Web Service, it seems reasonable to support fat DataSet objects from Web Services. Let's take a look at how that works.