Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 57 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
57
Dung lượng
530,01 KB
Nội dung
17.2 Creating a Web service 485 Chapter 17 http://[ ASMX file ]/[ function name ]?[ function parameters ] It is possible to use a GET request to invoke a Web service programmati- cally, but this is ill advised. Using the query string to pass objects is only possible with primitive types, and there are better ways to use a Web service programmatically. Another HTTP GET request can be made against the Web service in the form http://[ ASMX file ]?WSDL This displays the formal definition of the Web service in Web Service Definition Language (WSDL) format. The WSDL definition allows Visual Studio .NET to determine the methods exposed by the Web service and to generate a suitable wrapper or proxy class for it. This step is generally done behind the scenes, but for the interested reader, it is possible to perform this step manually using the WSDL.EXE utility provided with .NET. The call- ing syntax is as follows: WSDL http://[ ASMX file ]?WSDL This will generate a C# proxy class in the same folder as WSDL.EXE. To generate a VB.NET class, precede the URL with /Language:VB . 17.2.1 Deploying a Web service Having a Web service running on your local machine is fine for develop- ment purposes, but in order to make the service meaningful, it should be uploaded to a publicly accessible IIS server. Web services that are deployed publicly must have a unique namespace to distinguish them from other Web services on the Internet. Coding convention dictates that the namespace should be in the form of a domain name that you control. The namespace may look like a URL, but it does not need to point to anything on the Web in particular. C# [WebService(Namespace="http://www.myserver.com/")] 486 17.3 Using a Web service VB.NET <WebService(Namespace:="http://www.myserver.com/")> _ If you want to make it easy for people to find your Web service, one of the first places you should advertise it is at http://uddi.Microsoft.com or http://test.uddi.Microsoft.com . These are public repositories for Web ser- vices and generally the first place developers go when looking for a partic- ular online service. Universal description discovery integration (UDDI) is an open standard that can be accessed programmatically by using the Microsoft.Uddi.Sdk namespace provided with the UDDI SDK. 17.3 Using a Web service As mentioned earlier, the automatically generated Web interface for a Web service is not designed for public use. Instead, you generate a proxy class that accesses the service programmatically, and you can code against the Web service as if you are using a local object. In Visual Studio .NET, you don’t need to code a proxy class yourself; it will be created for you. All you need to do is enter the URL of the Web ser- vice, and all of the behind-the-scenes work is taken care of. Start a new project in Visual Studio .NET and select Windows Forms Application. Click Project →→ →→ Add Web Reference, and then enter the URL of the ASMX file created in the previous example. Press Add Reference once you have found the Web service. In the following example, the Web service is assumed to reside on the local machine and to be named Service1 . Draw a list view on the form, and name it lvServerVariables . A but- ton named btnPopulate is also required. Click on the form and add the following code: C# private void Form1_Load(object sender, System.EventArgs e) { lvServerVariables.View=View.Details; lvServerVariables.Columns.Add("Name", lvServerVariables.Width/2, HorizontalAlignment.Left); lvServerVariables.Columns.Add("Value", 17.3 Using a Web service 487 Chapter 17 lvServerVariables.Width/2, HorizontalAlignment.Left); } VB.NET Private Sub Form1_Load(ByVal sender As Object, ByVal _ e As System.EventArgs) lvServerVariables.View=View.Details lvServerVariables.Columns.Add("Name", _ lvServerVariables.Width/2, _ HorizontalAlignment.Left) lvServerVariables.Columns.Add("Value", _ lvServerVariables.Width/2, _ HorizontalAlignment.Left) End Sub This code simply lays the list view out on the screen in a neat way, with the column headers equally spaced across the screen. Click on the Populate button, and add the following code: C# private void btnPopulate_Click(object sender, System.EventArgs e) { string[] serverVariableNames; localhost.Service1 webservice = new localhost.Service1(); serverVariableNames = webservice.getServerVariableNames(); lvServerVariables.Items.Clear(); foreach (string serverVariableName in serverVariableNames) { ListViewItem lvItem = new ListViewItem(); lvItem.Text = serverVariableName; string[] serverVariableValues; serverVariableValues = webservice.getServerVariable(serverVariableName); if (serverVariableValues!=null) { lvItem.SubItems.Add(serverVariableValues[0]); } lvServerVariables.Items.Add((ListViewItem)lvItem.Clone()); } } 488 17.3 Using a Web service VB.NET Private Sub btnPopulate_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Dim serverVariableNames() As String Dim webservice As localhost.Service1 = New _ localhost.Service1 serverVariableNames = webservice.getServerVariableNames() lvServerVariables.Items.Clear() Dim i As Integer For each serverVariableName as string in _ serverVariableNames Dim lvItem As ListViewItem = New ListViewItem lvItem.Text = serverVariableName Dim serverVariableValues() As String serverVariableValues = _ webservice.getServerVariable(serverVariableName) If Not serverVariableValues Is Nothing Then lvItem.SubItems.Add(serverVariableValues(0)) End If lvServerVariables.Items.Add(CType(lvItem.Clone(), _ ListViewItem)) Next End Sub This code would seem to have nothing to do with networking code, but in fact, it communicates extensively with the remote server via the proxy class every time a method is called on the webservice object. If you would like to view the proxy class, you can click on show all files in the Solution Explorer, and click Localhost →→ →→ Reference.map →→ →→ Refer- ence.cs. It is not advisable to edit the proxy class manually. The rest of the code above is concerned with displaying the data returned from the Web service on-screen. Only the first element in the array returned from getServerVariable is actually rendered on-screen, for the sake of simplicity. To test the Web service client, run it from Visual Studio .NET, ensure that IIS is running on the local machine, and then press Populate. You should see a list appearing on-screen, which should resemble Figure 17.2. 17.4 Asynchronous calls to Web services 489 Chapter 17 17.4 Asynchronous calls to Web services If the same Web service were deployed on several geographically separated Web servers, clients could connect to several Web services at once in order to improve performance. This may only be applicable in situations where several calls have to be made and each call takes a significant amount of time to complete. To understand the scenario, we could envisage a situation where an application displays live stock values of a large share portfolio. A Web ser- vice is hosted on a server in the United States, which is linked into the NASDAQ exchange, and another server is located in Japan, which is linked into the Nikeii exchange. A customer in question has shares in Microsoft and Toyota. If the client were to issue a request for the value of the Microsoft shares, wait for the response, and then request the value of the Toyota shares, the process would take twice as long as if both requests were made simultaneously. Several techniques can be used to manage simultaneous Web service calls. The following code examples perform the same function: They make two calls to a Web service and measure the response times to the calls. IIS is multithreaded, so it handles both of these requests in parallel. In a real- world example, the same Web service would be mirrored on more than one server, so that the two requests would be handled at exactly the same time. Figure 17.2 Web service client application. 490 17.4 Asynchronous calls to Web services Each of the following samples requires a simple user interface consist- ing of only a button and a label. To create this interface, open a new project in Visual Studio .NET, and select a Windows form application. Draw a button on the form and name it btnMakeCall and then draw a label named lblStatus . You will also require a Web reference to the Web service as described ear- lier in this chapter. This Web reference should be named localhost , for the purposes of these code examples. The Web service does not necessarily need to be hosted on the local machine. 17.4.1 Wait handles A wait handle is equivalent to a do-nothing while loop using polling, but it is less processor intensive. This should only be used in a separate thread, or the client application will be nonresponsive to the user. This technique should only be used when useful client-side processing can be performed before data is returned from any of the Web services. Click on the Make Call button and enter the following code: C# private void btnMakeCall_Click(object sender, System.EventArgs e) { long timeStart = DateTime.UtcNow.Ticks; localhost.Service1 svc = new localhost.Service1(); IAsyncResult result1; IAsyncResult result2; result1 = svc.BegingetServerVariableNames(null,null); result2 = svc.BegingetServerVariable("REMOTE_ADDR",null,null); result1.AsyncWaitHandle.WaitOne(); result2.AsyncWaitHandle.WaitOne(); string[] varNames = svc.EndgetServerVariableNames(result1); string[] response = svc.EndgetServerVariable(result2); lblStatus.Text = "Time elapsed:" + (DateTime.UtcNow.Ticks - timeStart); lblStatus.Text += " ticks"; } VB.NET Private Sub btnMakeCall_Click(ByVal sender As Object, _ 17.4 Asynchronous calls to Web services 491 Chapter 17 ByVal e As System.EventArgs) Dim timeStart As Long = DateTime.UtcNow.Ticks Dim svc As localhost.Service1 = New localhost.Service1() Dim result1 As IAsyncResult Dim result2 As IAsyncResult result1 = svc.BegingetServerVariableNames( _ Nothing,Nothing) result2 = _ svc.BegingetServerVariable( _ "REMOTE_ADDR",Nothing,Nothing) result1.AsyncWaitHandle.WaitOne() result2.AsyncWaitHandle.WaitOne() Dim varNames() As String = _ svc.EndgetServerVariableNames(result1) Dim response() As String = _ svc.EndgetServerVariable(result2) lblStatus.Text = "Time elapsed:" & _ (DateTime.UtcNow.Ticks - timeStart) lblStatus.Text += " ticks" End Sub To test this code, run the application from Visual Studio .NET, and press the make Call Button. The user interface will become unresponsive until the call is received. In a production environment, the code detailed above should be contained within a separate thread. 17.4.2 Callbacks Callbacks produce the least amount of processor overhead while waiting for Web service calls to return. They are ideal in situations where no useful cli- ent-side processing can be performed before all of the data is received; how- ever, it could be difficult to determine when the last call has returned successfully or erroneously. Click on the Make Call button and enter the following code: C# public localhost.Service1 svc; public long timeStart; private void btnMakeCall_Click(object sender, System.EventArgs e) { 492 17.4 Asynchronous calls to Web services timeStart = DateTime.UtcNow.Ticks; svc = new localhost.Service1(); svc.BegingetServerVariableNames(new AsyncCallback(ServiceCallback1),null); svc.BegingetServerVariable("REMOTE_ADDR",new AsyncCallback(ServiceCallback2),null); } private void ServiceCallback1(IAsyncResult result) { string[] response = svc.EndgetServerVariableNames(result); lblStatus.Text = "Time elapsed:" + (DateTime.UtcNow.Ticks - timeStart); lblStatus.Text += " ticks"; } private void ServiceCallback2(IAsyncResult result) { string[] response = svc.EndgetServerVariable(result); lblStatus.Text = "Time elapsed:" + (DateTime.UtcNow.Ticks - timeStart); lblStatus.Text += " ticks"; } VB.NET Public svc As localhost.Service1 Public timeStart As Long Private Sub btnMakeCall_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) timeStart = DateTime.UtcNow.Ticks svc = New localhost.Service1() svc.BegingetServerVariableNames(New _ AsyncCallback(AddressOf ServiceCallback1),Nothing) svc.BegingetServerVariable("REMOTE_ADDR",New _ AsyncCallback(AddressOf ServiceCallback2),Nothing) End Sub Private Sub ServiceCallback1(ByVal result As IAsyncResult) Dim response() As String = _ svc.EndgetServerVariableNames(result) lblStatus.Text = "Time elapsed:" & _ 17.5 Interoperability 493 Chapter 17 (DateTime.UtcNow.Ticks - timeStart) lblStatus.Text += " ticks" End Sub Private Sub ServiceCallback2(ByVal result As IAsyncResult) Dim response() As String = _ svc.EndgetServerVariable(result) lblStatus.Text = "Time elapsed:" & _ (DateTime.UtcNow.Ticks - timeStart) lblStatus.Text += " ticks" End Sub To test this code, run the application from Visual Studio .NET, and press the Make Call button. The time displayed is the time that has elapsed between the issuing of the Web methods and the last response received. A more robust solution would be to use a global array that would track the progress of each call. The BeginGetServerVariableNames function takes two parameters; the first parameter indicates the procedure to be called once the web-method returns, and the second, as shown in the code example above, is set to null or Nothing. This parameter can optionally contain objects that can be passed to the callback via the result object. 17.5 Interoperability When developing a Web service, it should be straightforward for any devel- oper working on any platform to implement a client. The previous example should demonstrate that it is easy to implement a Web service client in .NET, but if your service is to be made available to third-party Web site developers, you have to make sure that you do not needlessly complicate their job simply for the sake of using this new buzzword in Web technology. Although it may not seem like your responsibility to support third- party developers that integrate into your software, it would be lunacy (and bad for business!) to provide a service that was so difficult to use from plat- forms other than .NET that developers would simply give up and find a different supplier. Most languages now support XML. With this, it is easy to extract prim- itive types such as strings, numbers, and arrays from SOAP responses; however, if complex objects such as datasets and nested classes are rendered 494 17.6 Performance as SOAP, it is likely that the average PHP Web developer will throw his hands up in despair. Therefore, if it is envisaged that there may be a user base that may not use Microsoft scripting languages to run their Web sites, then the clarity of XML returned from Web service methods should be closely examined. If the third party wishing to access your Web service is running a Microsoft platform and does not intend to use .NET (e.g., if he or she are using classic ASP or Visual Basic 6), then you cannot force these people to migrate to .NET in order to use your Web service; however, you could mention the SOAP toolkit from Microsoft (msdn.microsoft.com/webservices/ building/soaptk/), which can greatly simplify the task of adding Web service support to a legacy Windows application. 17.6 Performance The first thing that may strike you when running the code sample above is that it can take several seconds to populate a short list of information. Web services are slow on first access because of background .NET compilations. It may look as if Web services were designed more for interoperability than speed. In chapter 4, remoting was discussed. This technology is similar to Web services. With remoting, there were many ways to improve performance by using more simplistic protocols. With Web services, there is no easy way to use anything other than SOAP. Having said this, the one-protocol-only way of doing things makes life easier for system integrators who are working on different platforms. The trade-off between interoperability and performance has to be decided on a case-by-case basis. It should be clear enough that SOAP is more interoperable than Microsoft’s proprietary binary format. In benchmarking tests, a Web service and remoting object both made queries to a database in response to client requests. Under high-load condi- tions (60 requests per second for a single database entry), a remoting object hosted on a Windows service using a binary formatter over TCP outper- formed the Web service by 50%. Although remoting objects can be configured for higher performance than Web services, when a remoting object communicates with SOAP over HTTP, it is actually slower than a Windows service by about 25% under the same load as stated above. Furthermore, it is more difficult to use a remoting object than a Web service because there is no automatic mecha- nism to discover the interface of a remoting object, whereas Web services [...]... "RemotingServer", _ WellKnownObjectMode.Singleton) End Sub Certain things can be immediately ascertained by looking at this code The communications will take place on port 8085, using SOAP over HTTP The object is to be created as a Singleton, which means that it is state-full, and the value of LastID will be maintained between calls You will also require the supporting namespaces: C# using using using... also require the supporting namespaces: C# using using using using System.Runtime.Remoting; System.Runtime.Remoting.Channels; System.Runtime.Remoting.Channels.Http; RemoteObject; VB.NET Imports System.Runtime.Remoting Imports System.Runtime.Remoting.Channels Chapter 17 506 17.9 NET remoting Figure 17.3 Remoting Client and remoting Server Imports System.Runtime.Remoting.Channels.Http Imports RemoteObject... DLL, using ILDASM or MSIL-to -C# (www.saurik.com) An interface to the RemoteObject.IDGenerator class above is as follows: C# using System; public Interface IIDGenerator { public int getID(); } VB.NET Imports System Public Interface IIDGenerator Public Function NextOrder() As int End Interface Using shared interfaces is not the only way to provide clients with access to remote objects The two main drawbacks... to LocalSystem Finally, you need to add three references: one to System.Configuraone to System.Runtime.Remoting, and another that points at the compiled DLL for the IDGenerator assembly Then add the required namespaces as shown: tion.Install.dll, C# using using using using using System.Configuration.Install; System.Runtime.Remoting; System.Runtime.Remoting.Channels; System.Runtime.Remoting.Channels.Http;... method you wish to call For instance, a remote method named getDetails() returning string would have a corresponding delegate such as the following: C# delegate String GetDetailsDelegate(); VB.NET Delegate Function GetDetailsDelegate() as string 17.9 NET remoting 507 Assuming the remote object is already instantiated and named obj, the getDetails() method can be called thus: C# GetDetailsDelegate gdDelegate... client was in fact a paying customer HTTPS provides for client authentication, so there is no need to reinvent the wheel here In an intranet environment, a Windows authentication system will undoubtedly already be in place on the network To provide credentials with a Web service call, it is a matter of setting the Credentials property of the Web service, such as in the following code snippet: C# localhost.Service1... Chapter 17 510 17.9 NET remoting Assuming this file is saved as MyApp.exe.config, you can instantiate the remote object from the client using the following code: C# String filename = "MyApp.exe.config"; RemotingConfiguration.Configure(filename); MyClass obj = new MyClass(); VB.NET Dim filename as string Filename = "MyApp.exe.config" RemotingConfiguration.Configure(filename)... using System.Runtime.Remoting; System.Runtime.Remoting.Channels; System.Runtime.Remoting.Channels.Http; RemoteObject; VB.NET Imports Imports Imports Imports System.Runtime.Remoting System.Runtime.Remoting.Channels System.Runtime.Remoting.Channels.Http RemoteObject Create a new Windows forms project in Visual Studio NET Click → Project→Add References, click Browse, and then click on the DLL created in. .. can instruct the server to create an object and to keep it in memory for a specified time before destroying it Client-side activation occurs when the object is created using Activator.CreateInstance() In order to modify the lease parameters of a remote object, you simply override the InitializeLifetimeService method and change the properties of the ILease interface (Table 17.1) C# using System; using... personal address book, NET Inbox to store your email, and NET Wallet to store your credit card details would be available through MyServices The idea is technically sound, but many people and companies balked at the idea of Microsoft being in control of so much personal information 17.9 NET remoting Remoting is NET’s equivalent of Java remote method invocation (RMI) and Visual Basic s Distributed Common . people and companies balked at the idea of Microsoft being in control of so much personal information. 17.9 .NET remoting Remoting is .NET s equivalent of Java remote method invocation (RMI) and Visual. the following samples requires a simple user interface consist- ing of only a button and a label. To create this interface, open a new project in Visual Studio .NET, and select a Windows form. make a call to customer.terminateLease() and let .NET handle the network transmission. 17.9.1 How remoting works When using remoting, you still need to create a client and server. You also need