Actually creating an XML Web service class is easy, which is one of the reasons XML Web services have gained far more exposure than .NET Remoting. All you need to do is import the System.Web.Services namespace and add a <WebMethod> attribute to each method that you want to expose in the XML Web service. <WebMethod()> _ Public Sub AddCustomer(ByVal customer As CustomerDetails) ' (Code omitted.) End Sub Optionally, you can set additional <WebMethod> properties to configure a text description for each Web method, which can help the client understand how your service works. You can also add a <WebService> attribute to your class declaration (although this isn't strictly required) to set a description for the entire service and associate an XML namespace with your service. Note XML Web service methods can't be overloaded. That means you can't create two versions of a single method that accept different parameters. Instead, you need to use distinct names (such as GetCustomerByID and GetCustomerByName). This is just one of the ways that XML Web service methods are not as fully featured as a local .NET class. Listing 5-1 shows an example of the CustomerDB service provider component redesigned as an XML Web service. The differences are highlighted. Listing 5-1 The CustomerDB service provider as an XML Web serviceImports System.Web.Services Imports System.Data.SqlClient Imports System.Data ' The XML namespace is not related to a .NET namespace. ' It simply associates a unique identifier that can be used ' to describe your service in the WSDL document. ' Typically, you will use a URL that you control. <WebService(Namespace:="http://www.prosetech.com/", _ Description:="Methods to interact with the Customers table.")> _ Public Class CustomerDB Private ConnectionString As String = _ "Data Source=localhost;Initial Catalog=Store;" & _ "Integrated Security=SSPI" <WebMethod(Description:="Add a new customer record.")> _ Public Sub AddCustomer(ByVal customer As CustomerDetails) Dim Sql As String = "INSERT INTO Customers " Sql &= "(FullName, EmailAddress, Password) VALUES ('" Sql &= customer.Name & "', '" Sql &= customer.Email & "', '" Sql &= customer.Password & "')" ExecuteNonQuery(Sql) End Sub <WebMethod(Description:="Modify an existing customer record.")> _ Public Sub UpdateCustomer(ByVal customer As CustomerDetails) Dim Sql As String = "INSERT INTO Customers " Sql &= "(FullName, EmailAddress, Password) VALUES ('" Sql &= customer.Name & "', '" Sql &= customer.Email & "', '" Sql &= customer.Password & "')" ExecuteNonQuery(Sql) End Sub <WebMethod(Description:="Modify an existing customer record.")> _ Public Sub UpdateCustomer(ByVal customer As CustomerDetails) Dim Sql As String = "UPDATE Customers SET " Sql &= "FullName='" & customer.Name Sql &= "', EmailAddress='" & customer.Email Sql &= "', Password='" & customer.Password Sql &= "' WHERE CustomerdocEmphStrong"><WebMethod(Description:="Delete an existing customer record.")> _ Public Sub DeleteCustomer(ByVal customerID As Integer) Dim Sql As String = "DELETE FROM Customers WHERE CustomerdocEmphStrong">' This private method is not remotely callable. Private Sub ExecuteNonQuery(ByVal sql As String) Dim con As New SqlConnection(ConnectionString) Dim cmd As New SqlCommand(sql, con) Try con.Open() cmd.ExecuteNonQuery() Catch Err As Exception Throw New ApplicationException( _ "Exception encountered when executing command.", Err) Finally con.Close() End Try End Sub <WebMethod(Description:="Retrieve a single customer record.")> _ Public Function GetCustomer(ByVal customerID As Integer) _ As CustomerDetails Dim Sql As String = "SELECT * FROM Customers Where CustomerCustomerID") Customer.Name = reader("FullName") Customer.Email = reader("EmailAddress") Customer.Password = reader("Password") Catch Err As Exception Throw New ApplicationException( _ "Exception encountered when executing command.", Err) Finally con.Close() End Try Return Customer End Function <WebMethod(Description:="Retrieve all customers in an array.")> _ Public Function GetCustomers() As CustomerDetails() Dim Sql As String = "SELECT * FROM Customers" Dim con As New SqlConnection(ConnectionString) Dim cmd As New SqlCommand(Sql, con) Dim reader As SqlDataReader Dim Customers As New ArrayList() Try con.Open() reader = cmd.ExecuteReader() Do While reader.Read() Dim Customer As New CustomerDetails() Customer.ID = reader("CustomerID") Customer.Name = reader("FullName") Customer.Email = reader("EmailAddress") Customer.Password = reader("Password") Customers.Add(Customer) Loop Catch Err As Exception Throw New ApplicationException( _ "Exception encountered when executing command.", Err) Finally con.Close() End Try ' Now we convert the ArrayList to a strongly-typed array, ' which is easier fo the client to deal with. ' The ArrayList makes this possible through a handy ' ToArray() method. Return CType(Customers.ToArray(GetType(CustomerDetails)), _ CustomerDetails()) End Function <WebMethod(Description:="Retrieve all customers in a DataSet.")> _ Public Function GetCustomersDS() As DataSet Dim Sql As String = "SELECT * FROM Customers" Dim con As New SqlConnection(ConnectionString) Dim cmd As New SqlCommand(Sql, con) Dim Adapter As New SqlDataAdapter(cmd) Dim ds As New DataSet Try con.Open() Adapter.Fill(ds, "Customers") Catch Err As Exception ' Use caller inform pattern. Throw New ApplicationException( _ "Exception encountered when executing command.", Err) Finally con.Close() End Try Return ds End Function End Class Public Class CustomerDetails Public ID As Integer Public Name As String Public Email As String Public Password As String End Class Note Although the code in Listing 5-1 throws an ApplicationException if a problem occurs, the client will actually receive a SoapException object, which contains a text message that describes the original application class. This is an inherent limitation with how .NET handles SOAP, and it reduces the efficiency of a client to check for specific error conditions. One way around this problem is for the XML Web service to throw a SoapException directly and store extra XML information in the SoapException.Detail property. Remember, if you aren't designing your class with Visual Studio .NET, you also need to create the .asmx file that "advertises" the XML Web service to ASP.NET and move both files to a virtual directory. Visual Studio .NET performs these minor steps automatically. Data SerializationOne difference you might have noticed in the XML Web service example is the downgraded CustomerDetails class. In the .NET Remoting example, the CustomerDetails class included full property procedures, several constructors, and a <Serializable> attribute. In an XML Web service, none of these is necessary. In fact, you could create property procedures and constructors, but the client wouldn't be able to use them. Instead, the client would receive a stripped-down version of the class that only uses public member variables (like the version shown in the preceding example). Why the discrepancy? Even though XML Web services and .NET Remoting components can both send SOAP messages, they use different formatters. Components exposed through .NET Remoting or saved to disk require the <Serializable> attribute and use the SoapFormatter class from the System.Runtime.Serialization.Formatters.Soap namespace. Web services, on the other hand, make use of the XmlSerializer class in the System.Xml.Serialization namespace. Both of these classes transform .NET objects into XML messages. The difference is that the SoapFormatter can exactly reproduce any serializable .NET object, provided it has the assembly metadata for the class. This involves some proprietary logic. The XmlSerializer, in contrast, is designed to support third-party clients who might understand little about .NET classes and assemblies. It uses predetermined rules to convert common data types. These rules, which are based on XSD and the SOAP standard, allow for the encoding of common data types, arrays, and custom structures but don't provide any standard way to represent code (such as constructors and property procedures). Therefore, these details are ignored. Table 5-2 lists the data types supported in XML Web services and the SOAP standard.
|