Web services provide access to application logic, and application logic can take many forms “ from stored procedures to components . Although ASP.NET Web services take the form of classes with implementations , this should not imply that normalized object-oriented design principles must be adhered to. A web service should never be considered as a class, method, or property. Rather, a web service is an end-point that data is sent to and received from.
While designing ASP.NET Web services, you need to compare many aspects (we've presented a few here). Let's start the discussion of building ASP.NET Web services by discussing API design, namely chunky versus chatty patterns.
Web services rely on XML and HTTP to encode and transmit serialized application data. The combination of these two technologies provides a powerful means to create cross-platform distributed applications, as well as the ability to push application requests through proxies.
However, there are some downsides to this technology as well. It's an extremely verbose way of describing what should be a simple exchange of data between two applications, and it's stateless. Thus, the design recommendation is that you should try to reduce the number of network calls to the web service wherever possible.
HTTP is a stateless protocol, and this doesn't change for ASP.NET Web services that use HTTP to transport SOAP messages. ASP.NET provides workarounds for this stateless barrier , but the workarounds rely on the use of a session token which can either be stored in an HTTP cookie, or embedded within the URL.
The stateless nature of HTTP should definitely be taken into account when building web applications. If that stateless problem needs to be solved with a solution such as Session state, it's worth considering the implications to the web service “ using HTTP cookies builds reliance on the protocol rather than the SOAP message.
The discussion of stateless versus stateful applies most to discussions of design “ how will your application logic be exposed (for example, will you use methods or properties ) and how would end-users interact with that logic?
ASP.NET Web services use a class with methods marked with the WebMethod attribute to enable us to send and receive SOAP messages. Not only can the WebMethod attribute be applied to methods within the class, but it can also be applied to properties:
<%@ WebService Class="MethodsAndProperties" %> Imports System.Web.Services Public Class MethodsAndProperties Private Dim _name As String Public Property YourName() As String <WebMethod()> Get Return _name End Get <WebMethod()> Set _name = value End Set End Property End Class
In this VB.NET code ( StatelessMethodsAndProperties.asmx ), the WebMethod attribute has been applied to Get and Set of the YourName property.
It's best not to mark properties with the WebMethod attribute. Although functionally the behavior can be made to work, it requires that the class instance hold state. For example, running the last code example, (using the handy web service description Help page) you can call set_YourName and pass it Rob . If you then call get_YourName , you get no result. Since ASP.NET Web services are stateless, the instance of our MethodsAndProperties class is created and destroyed for each request.
This can be fixed by enabling session state ( StatefulMethodsAndProperties.asmx ):
<%@ WebService Class="MethodsAndProperties" %> Imports System.Web.Services Public Class MethodsAndProperties Private Dim _name As String Public Property YourName() As String <WebMethod(EnableSession:=true)> Get Return _name End Get <WebMethod(EnableSession:=true)> Set _name = value End Set End Property End Class
However, this solution will only work when you use a proxy to access this web service (which you will in the next chapter). Even with these changes, you still won't get the desired behavior when you test this code in your web service help page. The recommendation is that web services should be designed to be stateless, and that consequently using properties in web services is usually bad design.
Caching is another feature that can really help build great ASP.NET Web services. It's ideal for data that is requested often and doesn't change frequently. As you've already seen, caching can be controlled using the CacheDuration property of the WebMethod attribute.
The CacheDuration property allows you to specify a time duration within which the response to the request should be served from the ASP.NET cache. Serving requests from the cache can dramatically improve the performance of your application, since you no longer need to execute code on each request. Requests to a Web-callable method using the CacheDuration property will vary the entries for the response in the cache based upon the request. So, for a SOAP request, the contents of the cache will depend on the POST data.
In some cases, caching the entire response simply doesn't make sense. In such cases, you can use the cache API to cache interesting data within your web service. Consider the following VB.NET code ( DataCaching.asmx ):
<%@ WebService class="DataCachingExample" %> Imports System.Web.Services Imports System.Data Imports System.Data.SqlClient Imports System.Web.Caching Imports System.Web Public Class DataCachingExample <WebMethod()> Public Function GetDataSet(column As String) As DataSet Dim AppCache As Cache AppCache = HttpContext.Current.Cache If (AppCache(column) Is Nothing) Then AppCache(column) = LoadDataSet(column) End If Return CType(AppCache(column), DataSet) End Function Private Function LoadDataSet(column As String) As DataSet Dim myConnection As SqlConnection Dim myCommand As SqlDataAdapter Dim products As DataSet myConnection = _ New SqlConnection("server=localhost;uid=sa;pwd=;database=pubs") myCommand = _ New SqlDataAdapter("select " + column + " from Authors", myConnection) products = New DataSet() myCommand.Fill(products, "products") Return products End Function End Class
Although this code would work equally well with the WebMethod attribute's CacheDuration property set, it does illustrate how you can use the lower-level cache API to save work. In this example, we accept a single parameter: the name of a column in a database. This parameter is then used to create a DataSet , which is then added to the cache. On subsequent requests, they can be serviced dynamically, but it results in a serious performance benefit since the data does not need to be fetched from the database.
Although caching is recommended when (and where) possible for performance enhancements, you need to understand how the resources within the web service are being cached. For example, in the previous database example, we had a fairly simple matrix of items to be served from the cache (limited by the number of columns in a particular database table). However, caching is useless for application logic that might have an unlimited amount of variations “ for example, Add(a As Integer, b As Integer) .
Web services can be designed to be synchronous or asynchronous:
A synchronous design allows the ASP.NET thread of execution to run until it's complete. If an action within the code has the potential to block, such as network I/O or an extensive database lookup, this can stall the ASP.NET worker thread. Since a thread is a resource, and there are only a limited number of resources available on the system, this can force other requests to be queued. This all translates to an impact on the performance and scalability of the system.
An asynchronous design, on the other hand, allows the ASP.NET thread of execution to start up another thread, which can call back onto an ASP.NET thread when the work is complete. Thus, the ASP.NET application is not stalled because of resource constraints on available threads.