Chapter 12



Writing and Working with Services

Creating and Consuming XML Web Services

Web services have become the common programming model for implementing interoperability between systems that have little or no real connectivity. Prior to Web services, connecting applications in meaningful ways was a difficult challenge. This challenge was exacerbated by having to cross application domains, servers, networks, data structures, security boundaries, and the like. However, with the prominence of the Internet, Web services have become a common model for accessing data, performing distributed transactions, and exposing business process workflow.

The Internet and its supported standards around HTTP and XML make Web services possible.

However, having to program directly against HTTP, XML, and Simple Object Access Protocol (SOAP) is a challenging (and time-consuming) proposition. Thankfully, ASP.NET provides a model for building and consuming XML Web services. With it, you can define a Web service as code (an .asmx file and related class). ASP.NET will then wrap this code as a Web service object.

This object will know how to expose your Web service. This includes deserializing SOAP requests, executing your .NET Framework code, and serializing your response to be sent back to the requesting client as a SOAP message.

Additionally, ASP.NET provides a simple client model for consuming Web services. A proxy object is generated for you when you reference a Web service. You can then program against this proxy object as if you were calling in-process code. The proxy object takes care of serialization, SOAP messaging, and the related processes.

 

Creating an ASP.NET Web Service 

An ASP.NET XML Web service is a class you write that inherits from the class System.Web.Services.WebService. This class provides a wrapper for your service code. In this way, you are free to write a Web service in pretty much the same way you would write any other class and method. The WebService class and ASP.NET take care of the rest.

Each of these classes controls how your Web service works and how ASP.NET and the compiler view your service code. You can inherit from the WebService class to get access to standard ASP.NET features. The attribute classes allow you to mark parts of your code as related to XML Web services. Items marked for use as Web services are identified by ASP.NET. It then knows how to deserialize requests for your service, call your service, and serialize the response. It also handles working with SOAP, XML, Web Services Description Language (WSDL), and the related Web service standards. You will see how each of these classes is used for creating Web services in the coming sections.

 ASP.NET Web Service Projects

Web services in ASP.NET are defined by an .asmx file. This file can be added directly to an existing Web site. This is useful if you intend for your Web site to expose Web services in addition to Web pages. Of course, you can also create a simple Web site and use it for only .asmx Web service files.

Another scenario is to create a Web service project through the Add New Project dialog box. Here, you select the ASP.NET Web service application. This generates a separate project for your Web service application. This project has a structure similar to a Web site. This includes a folder for App_Data, a Web.config file, and related elements. It is important to note that in both scenarios, your Web service is hosted within ASP.NET and therefore has access to its features (session state, security model, configuration, and so on).

Like a Web page, Web services are exposed through Uniform Resource Locators (URLs). This means your domain name followed by a page name, as in http://MyDomain/MyService.asmx. The page for an XML Web service is defined by the .asmx file. This file is nothing more than a simple text file that is used as a pointer to the code of your Web service.

You add an .asmx file to your site for each Web service you wish to expose. In this case, think of a Web service like a class that only exposes methods. Therefore, each Web service can expose multiple methods. As an example, suppose you wish to write an XML Web service that exposes methods related to working with the author data in Microsoft’s sample database called Pubs. You might start by creating an .asmx file called Authors.asmx. This file would contain an @ WebService directive that points to the actual code for the Web service. The following is an example:

//C#

<%@ WebService Language="C#" CodeBehind="Authors.asmx.cs" Class="PubsServices.Authors" %>

 

This markup is similar to what you would see for a Web page. However, there is no additional markup included inside a Web service. Instead, the Web service is defined entirely in code.

 

The WebServiceAttribute Class

Creating an .asmx file and exposing public methods marked as WebMethod (see later) is sufficient for defining Web services in ASP.NET. However, there are a number of other classes that can be used to provide additional functionality. One such class is the WebServiceAttribute class (recall that attribute classes can be used with or without the Attribute suffix). This class can be used to provide information about your Web service. This information is used by clients that wish to reference the Web service.

You can provide both a namespace and a description of your Web service by applying the WebService attribute and parameters to your class. The description parameter is simply text you write to identify the high-level intent of your Web service. The namespace parameter sets the namespace of your Web service. This should be a domain name under your control. Visual Studio uses the tempuri.org namespace as filler until you define your actual namespace.

As an example, imagine again that you are creating a Web service centered on exposing author information. You would define your class inside the code-behind file for the .asmx file. You could then add the WebService attribute to the class, as shown here:

//C#

[WebService(Description = "Services related to published authors",

Namespace = "http://tempuri.org/")]

public class Authors

 

The WebService Class

The WebService class represents a base class for creating XML Web services in ASP.NET. This class is similar to the Page class for Web pages. It provides access to ASP.NET objects like Application and Session.

It is important to note that this class is optional: You do not need to inherit from this class to create XML Web services. Instead, you use this class as a base class only when you wish to access and use the features of an ASP.NET application. You might, for example, need to leverage session state between service calls. You could do so easily by first inheriting from this class and then accessing the session object as if you were coding an ASP.NET Web page.

The following code shows the authors example Web service. Here, the Authors class inherits directly from the WebService base class.

//C#

[WebService(Description = "Services related to published authors",

Namespace = "http://tempuri.org/")]

public class Authors : System.Web.Services.WebService

 

The WebMethodAttribute Class

Your Web service exposes Web methods. Each of these methods provides some sort of functionality encapsulated by the Web service. Your class can identify these methods through the use of the WebMethod attribute. You apply this attribute to any public method in your Web service class you wish to expose as part of your service.

You can assign the WebMethod attribute to a class without setting any of the parameters of the WebMethod class. This simply identifies your method as a Web service method.

However, the WebMethod attribute class also has a number of constructors used for various groups of parameter values. The parameters include the following:

- enableSessionState This parameter is used to indicate whether the given method should be able to work with session state. You set this value to false to disable the use of session state for the given Web method.

- transactionOption This parameter is used to indicate if your Web method supports transactions. The parameter is of the type System.EnterpriseServices.TransactionOption. Web services are stateless HTTP calls. Therefore, you can only use an XML Web service as the root of a transaction. This means the TransactionOptions that indicate a root transaction are equivalent (Required, RequiresNew). All other transaction options indicate no transaction support for the given service (Disabled, NotSupported, Supported).

- cacheDuration This parameter is used to define the number of seconds for which the response should be cached by the server. This is useful if your service has a high volume of access and the data is relatively static. In this case, you can cache results between calls to increase performance.

- bufferResponse This parameter is used to indicate whether the Web service response should be buffered back to the client.

Consider the author example. Imagine you have a public method called GetAuthorTitles that returns a list of titles based on the ID of a given author. Because this data only changes when an author publishes a new book, it is also a good candidate for caching. The following shows the WebMethod attribute applied to this method. Here, the cacheDuration parameter is set to 5 minutes (300 seconds).

//C#

[WebMethod(CacheDuration=300)]

public DataTable GetAuthorTitles(string authorId)

{ ... }

 

Web Services and Data Types

Notice that the previous example method, GetAuthorTitles, returns a DataTable object. This is possible as the object supports serialization. It can serialize itself into XML. Provided the calling client is also .NET, it can pick this XML up and deserialize it back into a strongly typed DataTable. In this case, ASP.NET does this for you without you having to really think about it.

You might also wish to provide instances of your own classes as return values of your functions or as parameters. You can do so by creating the object inside the Web service application. In this case, the compiler will take care of making the objects you expose serializable. These objects simply need a default constructor that does not accept parameters. You can also use the Serializable attribute class to tag class outside your Web service. This ensures any public members of the class can be serialized by ASP.NET. 


Consuming an ASP.NET Web Service

You can consume an ASP.NET Web service in any application capable of making an HTTP call. This means most .NET application types can call Web services, including console applications, Windows-based applications, and ASP.NET. This section focuses on calling an XML Web service from an ASP.NET application. 

Referencing a Web Service

To get started, you need to have access to a published Web service. This Web service can be published on a server or can be another project in your same solution. Either way, as long as it is a valid Web service, you will be able to subscribe to it.

The first step is setting a Web reference from your Web site to the given service. You do this by right-clicking your project file and choosing Set Web Reference. This opens the Add Web Reference dialog box. Here, you define the URL of your service, select the given service (.asmx file), and set a name for the reference. This name will be used by the generated proxy class to define the namespace for accessing your service. Once you have set the reference, Visual Studio and ASP.NET work to generate a Proxy class for working with the service. This allows you to write code against the service as if the service was just another class in your application. This proxy class does all the work of communicating to and from the Web service, serializing and deserializing data, and more.

As an example, the Authors.asmx file has two Web methods: GetAuthor and GetAuthorTitles. The first returns an instance of the Author class as embedded in the service class. The second returns a DataTable. To work with the Author class (of which the Web application knows nothing), Visual Studio generates this type from the WSDL of the Web service and puts it inside the proxy’s namespace. 

Calling a Web Service

You call the Web service through the proxy. This is as simple as writing code to call a method. For example, the following code shows how you call the GetAuthor method from the Authors .asmx Web service discussed previously:

//C#

PubServices.Authors pubs = new PubServices.Authors();

PubServices.Author auth = pubs.GetAuthor();

Label1.Text = auth.FirstName + " " + auth.LastName;

 

Note that in the preceding example code, only the call to GetAuthor actually hits the Web service. The object creation and property gets are simply calls to the proxy object and are thus in-process calls.

You can also bind to the Web service call. Remember, the Web service is exposed through a local proxy object. Therefore, you can use object data binding to bind to the given Web service (via the proxy). As an example, recall the GetAuthorTitles Web service. This service is accessed via a proxy object method of the same name. You can set this method as the SelectMethod of an object data source as shown in the following code. You can then bind the results to a GridView control.

 

<asp:ObjectDataSource runat="server" ID="ObjectDataSourceAuthors"

TypeName="PubsServices.Authors" SelectMethod="GetAuthorTitles">

<SelectParameters>

    <asp:QueryStringParameter Name="authorId" QueryStringField="auId" Type="String" />

</SelectParameters>

</asp:ObjectDataSource>

<asp:GridView ID="GridView1" runat="server" DataSourceID="ObjectDataSourceAuthors">

</asp:GridView>

 

Calling a Web Service from Client Script Using AJAX

You can use the AJAX functionality built into ASP.NET to call a Web service directly from client-side JavaScript. This is useful if you wish to kick off an operation on the server from the user’s browser. When the results are returned, the user’s browser can then be updated.

Of course, this all takes place asynchronously and without a browser refresh. There are a number of steps required to set up your Web service and an AJAX client page. First, your client-side page and your XML Web service (.asmx file) must be in the same domain.

From there, you should be sure to perform all of the following steps:

- Mark your Web service class with the ScriptServiceAttribute class. This indicates that the Web methods inside the Web service can be called from client script.

- Register the HTTP handler ScriptHandlerFactory with your Web site. You can do so inside the <system.Web><httpHandlers> element in the Web.config file. Depending on your project, this handler might already be set.

- Add a ScriptManager control to your client page. The ScriptManager control is required for all AJAX-enabled pages. However, inside it you need to set a ServiceReference. This reference should point to the XML Web service (.asmx file). Doing so will tell ASP.NET to generate a JavaScript client proxy to your Web service.

- Add a JavaScript method to your page. You can add code inside this method to call your Web service through the client-side proxy generated by ASP.NET. Your Web method can be referenced through the class name of the Web service and the method name of the Web method. Of course, you can, optionally, pass parameters to the Web service.

- Optionally, you can add another JavaScript method to your client page to serve as the callback handler. This method will be called by the JavaScript proxy and receive the results of the Web service call. You can use this method to update the user.

As an example, imagine you have a simple Web service that takes a Fahrenheit value and returns its Celsius equivalent. For starters, you would mark the Web service with the ScriptService attribute.

//C#

[System.Web.Script.Services.ScriptService]

[WebService(Namespace = "http://tempuri.org/")]

public class TempConversion : System.Web.Services.WebService

{

[WebMethod]

public float GetCelsius(float temperature)

{

return (5 / 9) * (temperature - 32);

}

}

 

Next, you need to make sure the HTTP handler ScriptHandlerFactory is registered with your site inside the Web.config file. The following shows an example:

<httpHandlers>

<remove verb="*" path="*.asmx" />

<add verb="*" path="*.asmx" validate="false"

type="System.Web.Script.Services.ScriptHandlerFactory" />

</httpHandlers>

 

You now need to create a client Web page. Remember, this Web page must be in the same domain as the Web service. Inside the Web page markup, you add a ScriptManager control. This control should be set to reference the actual Web service (.asmx file). The following shows an example of this markup:

<asp:ScriptManager runat="server" ID="ScriptManager1">

<Services>

<asp:ServiceReference path="TempConversion.asmx" />

</Services>

</asp:ScriptManager>

 

The next step would be to add JavaScript functionality to call the Web service, passing the user’s input. In addition, you need to write a simple callback function that takes the results and displays them to the user. The following code demonstrates this. The user input is inside the TextBoxTemp and the results are shown using LabelResults.

 

<script type="text/javascript">

function GetCelsius()

{

var val = document.getElementById("TextBoxTemp");

TempConversion.GetCelsius(val.value, FinishCallback);

}

function FinishCallback(result)

{

var results = document.getElementById("LabelResults");

results.innerHTML = result;

}

</script>

Finally, you need to add the remaining markup to the page. This simply consists of the user input information. The following shows an example:

Enter Fahrenheit Temperature:<br />

<asp:TextBox ID="TextBoxTemp" runat="server"></asp:TextBox>&nbsp;

<asp:Label ID="LabelResults" runat="server" Text=""></asp:Label>

<br />

<input id="Button1" type="button" value="Calculate" onclick="GetCelsius()" />

Notice that in this markup, the GetCelsius JavaScript method defined earlier is called when the user clicks the Calculate button. This triggers the Web service. The results are returned to the FinishCallback JavaScript method and then displayed in the Label control. 

Security and XML Web Services

You have two primary options for securing XML Web services written as .asmx files and hosted by ASP.NET. The first is to use one of the standard ASP.NET security methods to authenticate and authorize users. This option is similar to securing any ASP.NET resources such as a Web page, directory, or other file. The second approach is to write a custom security model using SOAP headers. This option can be useful if your calling clients cannot participate in the standard, Windows-based security models used by ASP.NET. 

ASP.NET Security

There a number of ways you can use the authentication and authorization methods of ASP.NET to secure your XML Web services. Thankfully, these options are not much different from securing other ASP.NET resources. This is a result of the Web service working much like a Web page. They both have a URL that points to a file. You can therefore lock down this file like you would any ASP.NET resource.

Each ASP.NET security option comes with performance versus security trade-offs. As an example, if you are processing sensitive information such as social security numbers, credit cards, and the like, you will want to encrypt this data as it travels over the network. However, this encryption will decrease performance as the calls have to be encrypted and decrypted, and the messages themselves will be larger. On the other hand, if you are sending basic information to and from the Web service (such as a part numbers, category identifiers, or similar details), you can relax the need for encryption and focus instead on authenticating and authorizing a user. This will help increase your performance and throughput. If your Web service is meant to be public (either inside or outside the firewall), you can always provide anonymous access to your Web service.

The first step in setting up your security model is determining a method for authentication. This means determining who the user actually is. The second is to decide if the user has authorization rights to actually access the Web service or the features the Web service exposes. The following list describes the basic ASP.NET security methods and a brief description of how they can be applied to Web services for both authentication and authorization.

- Windows Basic Authentication You use basic authentication to restrict rights to authorized users. In this case, the users are defined on the Web server and are given filebased access rights to the site or the service. When a user hits your service, they are challenged to provide credentials. Of course, these credentials can be provided by the calling client (and not the actual user). However, basic authentication sends the user and password information from the client to the server in clear text. This can be helpful

if your clients are non-Windows clients. However, as the information is encoded (and not encrypted), it can be intercepted by network monitoring tools and compromised.

- Windows Basic Authentication over SSL This version of basic authentication encrypts the calls over Secure Sockets Layer (SSL). This adds additional security to this type of authentication as the name and password are encrypted. However, the entire communication, in this scenario, is also encrypted. Therefore, while you gain in security, you lose in performance.

- Client certificates You can use client certificates to identify both the caller and the Web service. Certificates, in this case, are obtained from a trusted, third-party certificate authority. The client’s certificate is presented with the service call and verified as trusted. You can then use Windows to map the certificate to an actual user account. You then use the user account to define access to the given service resource.

- Windows digest This is similar to Windows Basic. However, digest sends the user’s password in a hashed, encrypted format so it cannot be compromised. This option does not require SSL and will often work through default firewalls. However, platforms outside of Windows do not support Windows digest security.

- Forms-based authentication Forms-based authentication is not supported for Web service scenarios.

- Windows integrated You can use Windows Integrated security to securely pass encrypted credentials from the client to the server. However, this option requires that both the client and the server are running Windows.

If you are accessing a secured service from the user’s browser, it can pass the credentials on to the Web server where they will be evaluated for authentication. In the case of Windows Integrated security, you must be using Microsoft Internet Explorer on the client. That said, user client calls to a Web service is an unlikely scenario with Web services. It is more likely that you will be calling a Web service from code inside your Web site (running server-side). To pass basic authentication credentials from your Web server to a Web service, you first create a NetworkCredentials class. This class contains the user name, password, and domain information. You can then create a CredentialCache object to which you add the NetworkCredentials instance. You then set the Web service’s generated client proxy’s Credentials property to the newly created CredentialCache object. If you are using integrated security between the Web server and the Web service, you set the Credentials property of the Web service proxy class to System.Net.CredentialCache .DefaultCredentials. The Web server running your Web page will then pass credentials to the Web service.

 Custom Security with SOAP Headers

You can also use SOAP headers to write a custom mechanism for passing user information into a Web service. Because this option uses Web service standards and not Windows, you can use it to work in scenarios where you require access to your service from other platforms besides Windows.

Custom SOAP headers can be used in a secure, encrypted manner. However, the encryption is optional and up to you to write (using the .NET Framework, of course). You can also use SOAP headers to send information to the service as plaintext (unencrypted). This is useful if you need to pass along information or you are behind a trusted firewall. It is not, however, a best practice to send unencrypted user information (name and password) using a SOAP header.

There are no default, built-in features for working with custom SOAP headers in authentication scenarios. Instead, both the client and the service need to be aware of how to format and pass the header information. In addition, on the server, you need to implement the IHttpModule interface to intercept the SOAP request, get the SOAP header, and parse (and decrypt) the user information. If the operation fails, you throw a SoapException instance.

 

Shembull 1:

1. Open Visual Studio and create a new ASP.NET Web Service Application project using C#. Name the project pubsservices.

2. Delete Service.asmx (and its code-behind fi le) from your project. Add a new service file called authors.asmx by right-clicking the project and choosing Add New Item. Select the Web Service template from the Add New Item dialog box.

3. Open the code-behind fi le for Authors.asmx in the code editor. Delete the default code in the service fi le template. Add a new class definition for the Authors service. There is no need to inherit from the WebService class as this service does not use the features of ASP.NET. Tag the class with the WebServiceAttribute class and pass a default namespace. Your class definition should look similar to the following:

//C#

namespace PubsServices

{

[WebService(Namespace = "http://tempuri.org/")]

public class Authors

{

}

}

4. Open the Web.config file. Find the <connectionStrings /> element.

<connectionStrings>

<add name="PubsConnectionString" connectionString="Data Source=.\SQLEXPRESS;

AttachDbFilename=|DataDirectory|\pubs.mdf;Integrated Security=True;

User Instance=True" providerName="System.Data.SqlClient"/>

</connectionStrings>

 

5. Return to the .asmx service file. Add using statements to the class file for System.Data, System.Data.SqlClient, and System.Configuration.

6. Add a private variable at the class level to store the connection string to the Pubs database. Name this variable _cnnString, as shown in the following code: 

private string _cnnString =

ConfigurationManager.ConnectionStrings["PubsConnectionString"].ToString(); 

7. Add a method to the class to return all titles for a given author based on their authorId. These authors can be returned as a DataTable instance. Name this method GetAuthorTitles.

8. Tag the GetAuthorTitles method with the WebMethodAttribute class. Set the CacheDuration to 300 seconds. Your method should look as follows:

 [WebMethod(CacheDuration = 300)]

public DataTable GetAuthorTitles(string authorId)

{

string sql = "SELECT titles.title, titles.type, titles.price, " +

"titles.pubdate FROM titleauthor INNER JOIN titles ON " +

"titleauthor.title_id = titles.title_id ";

if(authorId != "0")

sql = sql + " WHERE (titleauthor.au_id = @AuthorId) ";

SqlConnection cnn = new SqlConnection(_cnnString);

SqlCommand cmd = new SqlCommand(sql, cnn);

cmd.Parameters.Add("AuthorId", SqlDbType.VarChar, 11).Value = authorId;

SqlDataAdapter adp = new SqlDataAdapter(cmd);

DataSet ds = new DataSet();

adp.Fill(ds);

return ds.Tables[0];

} 

9. Compile your application

 Shembull 2:

1.      Continue editing the project you created in the previous exercise.

2.      Add a new Web site to the solution: Right-click the solution and choose Add | New Web Site. Select the ASP.NET Web Site template. Name the Web site pubsclient. Right-click the Web site and choose Set As StartUp Project.

3.      Add a Web reference to the Web service created in Exercise 1. Start by rightclicking  the Web site; choose Add Web Reference. In the Add Web Reference dialog box, select Web Services In This Solution. This should display the Authors service; click it. On the right side of the dialog box, change the Web reference name to pubsservice. Finish by clicking Add Reference.

4.      Open the Default.aspx page in your Web site. Add an object data source control to the page. Configure it to use the Web service proxy class. Set the authorId parameter to be set via the query string value auId.

Add a GridView control to the page and set its DataSourceId property to the object data source. Your markup should look as follows:

<asp:ObjectDataSource runat="server" ID="ObjectDataSourceAuthors"

TypeName="PubsService.Authors" SelectMethod="GetAuthorTitles">

<SelectParameters>

<asp:QueryStringParameter Name="authorId"

QueryStringField="auId" Type="String" DefaultValue="0" />

</SelectParameters>

</asp:ObjectDataSource>

<asp:GridView ID="GridView1" runat="server"

DataSourceID="ObjectDataSourceAuthors">

</asp:GridView>

5.      Run the application to see the results. 

Creating and consuming Wcf services 

You learned about creating XML Web services with ASP.NET. This is a very useful, straightforward way to create Web services that you intend to host in IIS and call over HTTP. However, the service model can be extended beyond HTTP. For example, you might want to write a service that is accessed inside the firewall over Transmission Control Protocol (TCP) instead of HTTP. This can provide increased performance in this scenario. In earlier versions of the .NET Framework, this meant you wrote the service using Remoting. However, if that same service code needed to be called over both HTTP and TCP, you had to write and host it twice. This is one of the many problems WCF is meant to solve.

WCF is a unifying programming model. It is meant to define a singular way for writing services and thereby unify things like Web services (.asmx), .NET Remoting, Message Queue (MSMQ), Enterprise Services (COM+), and Web Services Enhancements (WSE). It does not replace these technologies on an individual basis. Instead, it provides a single programming model that you can use to take advantage of all of these items at once. With WCF, you can create a single service that can be exposed as HTTP, TCP, named pipes, and so on. You also have multiple hosting options.

This lesson covers the basics of WCF to give you a solid footing when working with this technology. This lesson is not all-encompassing on WCF. Rather, it focuses on those areas inside WCF that are specific to an ASP.NET developer: writing, hosting, and calling WCF services with ASP.NET Web sites. 

Presenting Windows Communication Foundation (WCF)

Before you build your first WCF service application, it is important to get an overview of how the technology works. WCF enables message-based communication to and from endpoints. You write your service and then attach, or configure, endpoints. A given service can have one or more endpoints attached to it. Each WCF endpoint defines a location to which messages are sent and received. This location includes an address, a binding, and a contract. These address, binding, and contract concept is often referred to as the ABCs of WCF. The following list describes each of these items in detail:

A is for address The endpoint’s address is the location of the service as a Uniform Resource Identifier (URI). Each endpoint of a given service is meant to have a unique address. Therefore, if you have a service that exposes more than one endpoint (or transport protocol), you will uniquely identify the address based on the endpoint’s transport protocol. This might mean changing a port number, defining the address as HTTPS, or taking a similar action.

B is for binding The binding defines how the service is meant to communicate, such as HTTP, TCP, MSMQ, Binary HTTP, and so on. This is referred to as the binding’s transport. You can add multiple bindings to a single service. Bindings can include other information, too, like encoding and security. Each binding must, at a minimum, define a transport.

C is for contract The contract represents the public definition, or interface, of the service. It defines things like the namespace of the service, how messages should be sent, callbacks, and related contract items. There are multiple contract types in WCF, including service contract, operation contract, message contract, fault contract (for error handling), and data contract. These contracts work together to indicate to the client code consuming the WCF service how it should define communication messages.

Once you define your WCF service and configure at least one endpoint, you must host it. There are a few options here, which are discussed in a moment. However, the one host this lesson focuses on is IIS and ASP.NET. To call the service, a client generates a compatible endpoint. This endpoint indicates where the service is, how communication should work, and the format of that communication. At run time, clients typically initiate requests to a listening, hosted service. Like a Web service, the WCF service processes the request and returns results—all using the defined endpoint information.

The good news is that there are multiple tools and configuration support for creating WCF services. Again, it is still important to understand how this works. As an additional overview, the following section presents the layers of the WCF architecture.

 

The Layers of the WCF Architecture

A WCF application has multiple layers that work together to provide a wide range of functionality and options for building a service-oriented application (SOA). For the most part, these layers are behind the scenes and the configuration of services is done for you or through configuration tools that make it easier.


It is important to understand these layers and the many options they provide you as a service developer. The following list provides an overview of each layer.

- Contract layer The contract layer is meant to define the contract your service exposes to end clients. This includes the message the service supports for calling operations, receiving results, and managing errors. In addition, the contract includes the endpoint information of policy and binding. For example, the contract might indicate that the service requires HTTP with a binary encoding.

- Runtime layer The service runtime layer controls how your service is executed and how the message body is processed. You can configure this layer to support transactions, handle concurrency, and emit error information. For example, you can use throttling to indicate the number of messages your service can process; you can use the instance functionality to indicate how many instances of your service should be created to manage requests.

- Messaging layer The messaging layer represents the WCF channel stack in terms of transport and protocol. Transport channels work to convert messages to work across HTTP, named pipes, TCP, and related protocols. Protocol channels work to process messages for things like reliability and security.

- Hosting layer The hosting layer defines the host, or executable, that runs the service in process. Services can be self-hosted (run in an executable), hosted by IIS, Windows Activation Service (WAS), a Windows Service, or COM+. Picking a host for your service depends on a number of factors like client access, scalability, reliability, and the need for other services of the host (like ASP.NET). In most enterprise application cases, you will want to use an existing host for your service rather than writing your own.

You can see there are many options for creating, configuring, and hosting a wide array of services. 


Creating a WCF Service with ASP.NET

Creating and consuming WCF services follow a standard set of programming tasks. You follow these steps every time you wish to create and consume a new WCF service:

1. Define the service contract.

2. Implement (or write) the service contract.

3. Configure a service endpoint(s).

4. Host the service in an application.

5. Reference and call the service from a client application.

As you can see, a WCF service application starts with the contract. This contract indicates the features and functionality your service will offer to calling clients. In WCF programming, you create this contract by first defining an interface and decorating that interface with a number of attributes.

These WCF attribute classes are found in the System.ServiceModel namespace. These classes are used to define the details of the contract that your service will have with calling clients. For example, you can indicate if your service contract is one-way, request-reply, or duplex. These attributes also define your service operations and the data that define these operations. The following list provides a description for each of these classes.

- ServiceContract The ServiceContract attribute class is used to indicate that a given interface (or class) is a WCF service. The ServiceContract attribute class has parameters for setting things like whether the service requires a session (SessionMode), the namespace, the name of the contract, the return contract on a two-way contract (CallbackContract), and more.

- OperationContract The OperationContract attribute class is used to mark methods inside an interface (or class) as service operations. Methods marked with OperationContract represent those exposed by the service to clients. You can use the parameters of the OperationContract attribute class to set things like whether the contract does not return a reply (IsOneWay), the message-level security (ProtectionLevel), or whether the method supports asynchronous calls (AsyncPattern).

- DataContract The DataContract attribute class is used to mark types you write (classes, enumerations, structures) as participating in WCF serialization via the DataContractSerializer. Marking your classes with this attribute ensures they can be sent to and from disparate clients in an efficient manner.

- DataMember The DataMember attribute class is used to mark individual fields and properties that you want to serialize. You use this class in conjunction with the DataContract class. 


The WCF Service Application

Visual Studio and ASP.NET define the WCF Service Application project template. This template defines a Web project that serves to host the WCF service. This project contains a reference to System.ServiceModel.dll, which contains the WCF classes. Creating a new instance of this project template will also generate a default service (Service1.svc) and a related contract file (IService1.cs).

The contract file is a regular .NET Framework interface that includes the service attribute classes tagging the service (class), the operations (methods), and the data members (types, fields, properties). The .svc file is a class that implements this interface. Like other ASP.NET templates, you can use these classes to create your own service.

Finally, a WCF Service application is automatically configured to be hosted in IIS and expose a standard HTTP endpoint. This information can be found inside the <system.servicemodel> section of the Web.config file. The following code shows an example:

<system.serviceModel>

<services>

<service name="NorthwindServices.Service1"

behaviorConfiguration="NorthwindServices.Service1Behavior">

<endpoint address="" binding="wsHttpBinding"

contract="NorthwindServices.IService1">

<identity>

<dns value="localhost"/>

</identity>

</endpoint>

<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>

</service>

</services>

<behaviors>

<serviceBehaviors>

<behavior name="NorthwindServices.Service1Behavior">

<!-- to avoid disclosing metadata information, set the value below to false

and remove the metadata endpoint above before deployment -->

<serviceMetadata httpGetEnabled="true"/>

<!-- To receive exception details in faults for debugging purposes, set the

value below to true. Set to false before deployment to avoid disclosing

exception information -->

<serviceDebug includeExceptionDetailInFaults="false"/>

</behavior>

</serviceBehaviors>

</behaviors>

</system.serviceModel>

As you can see, the WCF Service application in ASP.NET takes care of many of the common steps to a WCF service. In fact, steps 1, 3, and 4, as discussed previously, are taken care of by default. That leaves step 2, implement the service, and step 5, call the service from a client application. 


Implementing the WCF Service

To implement the service, you start by defining the contract via the interface. For example, suppose you wish to create a service that exposes methods that work with the Shippers table in the Northwind database. You might start by creating a Shipper class and marking it as a DataContract and marking its members as DataMembers. This allows you to pass the Shipper class in and out of the service. The following code shows an example: 

[DataContract]

public class Shipper

{

[DataMember]

public int ShipperId { get; set; }

//implement remaining properties

}

The next step is to define the methods of your interface. You need to mark those with the OperationContract attribute. You need to mark the interface with the ServiceContract attribute.

For example, suppose the shipping service exposes operations for retrieving a single shipper and saving a single shipper. In this case, your interface should look as follows:

public interface IShipperService

{

[OperationContract]

Shipper GetShipper(int shipperId);

[OperationContract]

Shipper SaveShipper(Shipper shipper);

}

This interface will be used by WCF to expose a service. Of course, the service will be configured based on the information inside the Web.config file. The service interface also still needs to be implemented. To do so, you implement the service interface inside an .svc file.

For example, if you were to implement the interface contract defined previously for working with the Shipper data, you would do so as the following code demonstrates:

//C#

public class ShipperService : IShipperService

{

private string _cnnString =

ConfigurationManager.ConnectionStrings["NwConnectionString"].ToString();

public Shipper GetShipper(int shipperId)

{

//code to get the shipper from the db and return it

}

public void SaveShipper(Shipper shipper)

{

//code to save the shipper to the db

}

}

Consuming a WCF Service in an ASP.NET Page

You are now ready to call the WCF service created previously. The contract is defined via the IShipperService interface. The contract is implemented inside the ShipperService.svc file. An endpoint is configured via the default HTTP endpoint set up inside the Web.config file. The service is hosted by IIS and ASP.NET (or your local Web server). The final step is to set a client to call the service. In this case, we assume the client is another ASP.NET Web site. However, it could easily be a Windows application or another application on a different platform. To start, you need to generate a proxy class for calling the WCF service. This can be done using Visual Studio. You right-click your Web site and select Add Service Reference. This opens the Add Service Reference dialog box.

This dialog box allows you to define an address to your service. Again, this is based on the endpoint that your service exposes. In this example, a connection is being made to the ShipperService.svc created in the prior section. Notice how the contract is shown via the service’s interface.

The namespace defines the name for the proxy class that is generated by Visual Studio. This proxy class is a WCF service client that allows you to program against the service without having to deal with the intricacies of WCF.

You can view the contents of the service reference by selecting Show All Files from Solution Explorer. Of course, this only works with a Web application.

The file Reference.cs contains the actual proxy class. The other files are used by this proxy class when working with the service. This proxy class communicates with the Web service. In fact, it contains classes and methods that look just like those of the service, thanks to the service contract. Notice you can call the ShipperServiceClient code and even pass a local type called Shipper that contains the same properties defined by the service contract. 

Your client code must also define binding and endpoint information. The Add Service Reference task generates the appropriate endpoint information automatically when you add the service reference. This information can be found inside the Web.config file of the service client Web site. The following shows an example:

<system.serviceModel>

<bindings>

<wsHttpBinding>

<binding name="WSHttpBinding_IShipperService" closeTimeout="00:01:00"

openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"

bypassProxyOnLocal="false" transactionFlow="false"

hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288"

maxReceivedMessageSize="65536" messageEncoding="Text"

textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">

<readerQuotas maxDepth="32" maxStringContentLength="8192"

maxArrayLength="16384" maxBytesPerRead="4096"

maxNameTableCharCount="16384" />

<reliableSession ordered="true" inactivityTimeout="00:10:00"

enabled="false" />

<security mode="Message">

<transport clientCredentialType="Windows" proxyCredentialType="None"

realm="" />

<message clientCredentialType="Windows" negotiateServiceCredential="true"

algorithmSuite="Default" establishSecurityContext="true" />

</security>

</binding>

</wsHttpBinding>

</bindings>

<client>

<endpoint address="http://localhost:4392/ShipperService.svc"

binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IShipperService"

contract="NwServices.IShipperService" name="WSHttpBinding_IShipperService">

<identity>

<dns value="localhost" />

</identity>

</endpoint>

</client>

</system.serviceModel>

 

You can edit the WCF configuration information directly in Web.config. Alternatively, you can use the Service Configuration Editor to manage your endpoints (for both clients and the services you create). To do so, right-click the Web.config file and choose Edit Wcf Configuration. 

Calling a WCF Service from Client Script Using AJAX (REST and JSoN) WCF allows you to create and work with a number of different types of services. Remember, it is a technology used to define service endpoints that have an address, binding, and contract.

This level of flexibility built into the framework allows it to support various message types and communication protocols.

One such service type is based on representational state transfer (REST) and JavaScript Object Notation (JSON). Services based on these concepts have become very popular as the result of AJAX programming. AJAX becomes easier with a simple service (REST) based on a simple message format (JSON). WCF and the .NET Framework have built-in support for both.

A REST service is a Web service you write that responds to HTTP GET requests. Clients can therefore call a REST service the same way they would access a page: using a URL and a query string. The server then responds with a text document as it would for any HTTP GET request.

This way, a REST service does not require knowledge of the XML schema used to call the service. Instead, it simply sends the request and processes the text-based response (usually JSON formatted data).

The response of a REST service is typically in the form of JSON data. JSON is a message data format that evolved out of the heavy use of AJAX. The message format is not XML-based (as are most services). Instead, it is simple, lightweight, and text-based. A JSON message can be processed easily by the JavaScript engine that exists inside nearly all Web browsers. This makes it ideal when calling services from JavaScript. In fact, a JSON message can be parsed using the JavaScript eval function because basically it is syntactically formatted JavaScript. The following is an example of a JSON-formatted message:

{

"proudctName": "Computer Monitor",

"price": "229.00",

"specifications": {

"size": 22,

"type": "LCD",

"colors": ["black", "red", "white"]

}

} 

Writing a WCF Service Based on REST and JSON

Creating a WCF service based on REST and JSON is somewhat simplified in ASP.NET. This is due in part to the AJAX support built into ASP.NET. Because of this, there is a WCF template that you can use to quickly create a service that leverages the REST calling mechanism and the JSON data format.

This AJAX-WCF item template is the AJAX-enabled WCF Service template. It can be found in the Add New Item dialog box for a Web site project. The template defines a class that can be used to create a WCF service for use with AJAX.

As an example, suppose you were creating a service to calculate a product’s full price based on the item ID and the postal code to which you are shipping the item. The following code shows an example of an AJAX-enabled WCF Service that simulates such a method: 

namespace PricingServices

{

[ServiceContract(Namespace = "PricingServices")]

            [AspNetCompatibilityRequirements(RequirementsMode =

AspNetCompatibilityRequirementsMode.Allowed)]

public class PricingService

{

[OperationContract]

[WebInvoke]

public double CalculatePrice(string itemId, string shipToPostalCode)

{

double price;

//simulate product price lookup based on item id

price = 45;

//simulate calculation of sales tax based on shipping postal code

price = price * 1.06;

//simulate calculation of shipping based on shipping postal code

price = price * 1.1;

return price;

}

}

}

Notice that the service method is marked with the WebInvoke attribute. This indicates that the method can be called by an HTTP request. Methods marked with WebInvoke are called using HTTP POST. This can be important if you are sending data to the server to be written or do not wish your request to be cached by a browser or the server. If, however, your service typically returns data that is somewhat static, you might mark the method with the WebGet attribute. This indicates an HTTP GET request with results that can be cached. This is the only reason to use the WebGet attribute. The ASP.NET AJAX ScriptManager control can work with both HTTP GET and POST services.

Visual Studio also updates the site’s Web.config file when the AJAX-enabled WCF Service is added to the project. The following shows an example. Notice the element <enableWebScript />. This indicates that the endpoint is a RESTful service that uses the JSON data format and can therefore be consumed by AJAX. Also notice that the binding is set to webHttpBinding indicating again that this service is called via HTTP (and not SOAP). 

<system.serviceModel>

<behaviors>

<endpointBehaviors>

<behavior name="PricingServices.PricingServiceAspNetAjaxBehavior">

<enableWebScript/>

</behavior>

</endpointBehaviors>

</behaviors>

<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>

<services>

<service name="PricingServices.PricingService">

<endpoint address=""

behaviorConfiguration="PricingServices.PricingServiceAspNetAjaxBehavior"

binding="webHttpBinding" contract="PricingServices.PricingService"/>

</service>

</services>

</system.serviceModel>

As you can see, ASP.NET simplifies creating WCF services based on REST and JSON. You can also use the features of WCF and the .NET Framework to define REST services and JSONbased messages outside of ASP.NET. In fact, the .NET Framework supports serializing between .NET types and JSON data structures.

 

Calling a JSON-Based WCF Service from AJAX

The AJAX support in ASP.NET also makes calling a REST-based service from AJAX a relatively straightforward process. The ScriptManager control allows you to set a service reference to the given RESTful WCF service. It then defines a JavaScript proxy class for you to call. This proxy class manages the call from the AJAX-enabled page to the WCF service.

For example, to call the service defined previously, you start by adding a ScriptManager control to your page. You then define a ServiceReference to the actual service. The following markup shows an example:

 

<asp:ScriptManager ID="ScriptManager1" runat="server">

<Services>

<asp:ServiceReference Path="PricingService.svc" />

</Services>

</asp:ScriptManager>

You can then define a script block on your page to call the proxy class generated for you based on this service reference. In the example, the service takes a product ID and a postal code. The following JavaScript assumes these values are entered by a user in a couple of text box controls. The code also responds to a user clicking a button on the page.

 

<script language="javascript" type="text/javascript">

function ButtonCalculate_onclick() {

var service = new PricingServices.PricingService();

service.CalculatePrice(document.forms[0].TextBoxProduct.value,

document.forms[0].TextBoxPostCode.value, onSuccess, onFail, null);

}

function onSuccess(result){

LabelPrice.innerText = result;

}

function onFail(result){

alert(result);

}

</script>

 

Notice that in the previous code, the call to the CalculatePrice method goes through a proxy that defines some additional parameters. This allows you to pass in a JavaScript method name to be called by the ScriptManager after the service is called. You can define a method both for success and for failure. In this case, a successful call writes the results to a Label control.

The following code shows the markup for the page’s controls to round out the example:

<div>

Product:<br />

<asp:TextBox ID="TextBoxProduct" runat="server"></asp:TextBox>

<br />

Ship to (postal code):<br />

<asp:TextBox ID="TextBoxPostCode" runat="server"></asp:TextBox>

<br />

<input name="ButtonCalculate" type="button" value="Get Price"

onclick="ButtonCalculate_onclick()" />

<br />

<asp:Label ID="LabelPrice" runat="server"></asp:Label>

</div> 

Shembull 1:

1. Open Visual Studio and create a new WCF Service Application project using C#. Name the project northwindservices.

2. Delete the IService1.cs and Service1.svc files from the project.

3. Add a new WCF service to the application: Right-click the project and choose Add |New Item. Select the WCF Service template. Name the service shipperservice.svc. Notice that both an interface file (IShipperService) and an .svc file are created.

4. Open Web.config. Navigate to <system.serviceModel>. Delete both the <service> and <behavior> nodes for Service1.

Navigate to the <connectionStrings> node in Web.config. Add a connection string for the Northwind database. This connection string should read as follows (formatted to fit on the printed page):

<connectionStrings>

<add name="NwConnectionString" connectionString="Data Source=.\SQLEXPRESS;

AttachDbFilename=|DataDirectory|\northwnd.mdf;Integrated Security=True;

User Instance=True" providerName="System.Data.SqlClient"/>

</connectionStrings>

5. Open IShipperService.cs. Define a data contract class that represents a Shipper object. Remember to use the DataContract and DataMember attributes. Your code should read as follows: 

namespace NorthwindServices

{

[DataContract]

public class Shipper

{

[DataMember]

public int ShipperId { get; set; }

[DataMember]

public string CompanyName { get; set; }

[DataMember]

public string Phone { get; set; }

}

}

6. Next, define the interface for the Shipper class. Create one method for returning a Shipper instance and another for accepting a Shipper instance for updating. Remember to use the ServiceContract and OperationContract attributes. Your code should look as follows: 

[ServiceContract]

public interface IShipperService

{

[OperationContract]

Shipper GetShipper(int shipperId);

[OperationContract]

void  SaveShipper(Shipper shipper);

}

 7. Open the ShipperService.svc code by double-clicking the fi le. Add using  statements for System.Data, System.Data.SqlClient, and System.Configuration. Add code to get the connection string from the database. Implement the GetShipper method by calling the database to retrieve a record from the Shipper table. Copy this data into a Shipper instance and return it as a result of the function.

Implement the SaveShipper method by updating a shipping record with data inside a Shipper instance. Your code for the service implementation should read as follows: 

namespace NorthwindServices

{

public class ShipperService : IShipperService

{

private string _cnnString =

ConfigurationManager.ConnectionStrings["NwConnectionString"].ToString();

public Shipper GetShipper(int shipperId)

{

string sql = "SELECT shipperId, companyName, phone " +

"FROM shippers WHERE (shipperId = @ShipperId) ";

SqlConnection cnn = new SqlConnection(_cnnString);

SqlCommand cmd = new SqlCommand(sql, cnn);

cmd.Parameters.Add("ShipperId", SqlDbType.Int, 0).Value = shipperId;

SqlDataAdapter adp = new SqlDataAdapter(cmd);

DataSet ds = new DataSet();

adp.Fill(ds);

Shipper s = new Shipper();

s.ShipperId = shipperId;

s.CompanyName = ds.Tables[0].Rows[0]["companyName"].ToString();

s.Phone = ds.Tables[0].Rows[0]["phone"].ToString();

return s;

}

public void SaveShipper(Shipper shipper)

{

string sql = "UPDATE Shippers set phone=@Phone, " +

"companyName=@CompanyName WHERE shipperId = @ShipperId ";

SqlConnection cnn = new SqlConnection(_cnnString);

SqlCommand cmd = new SqlCommand(sql, cnn);

cmd.Parameters.Add("Phone", SqlDbType.NVarChar, 24).Value = shipper.Phone;

cmd.Parameters.Add("CompanyName", SqlDbType.NVarChar, 40).Value =

shipper.CompanyName;

cmd.Parameters.Add("ShipperId", SqlDbType.Int, 0).Value = shipper.

ShipperId;

cnn.Open();

cmd.ExecuteNonQuery();

cnn.Close();

}

}

} 

8. Compile and run your Service application. Doing so should launch the service in a Web browser.

9. Click the link at the top of the Web page to see the WSDL for your WCF service.

10. Generate a test client for your WCF service using Svcutil.exe. Start by opening a Visual Studio command prompt (Start | All Programs | Microsoft Visual Studio 2008 | Visual Studio Tools). Navigate to a directory to which you wish to generate the test client. Copy the command from the top of the Web page into the command window and press Enter. Exit the command prompt.

11. Navigate to the files you generated. Open these files and examine them. There should be a configuration file that defines an endpoint for communicating with the service. There should also be a code file that shows how to call the WCF service. This is the proxy class you can use to write an actual client. These are also the same files that get generated when you set a service reference to a WCF service. 

Shembull 2: 

1. Continue editing the project you created in the previous exercise.

2. Add a new Web site to the solution: Right-click the solution and choose Add | New Web Site. Select ASP.NET Web Site. Name the Web site ShipperClient. Right-click the Web site and choose Set As StartUp Project.

3. Right-click the newly added Web site and choose Add Service Reference. Click Discover in the Add Service Reference dialog box. Select the ShipperService. Set the namespace to NwServices. Click OK to close the dialog box.

4. Open the Default.aspx page. Add controls to the page to allow a user to select a shipper based on their ID, display the details of that shipper, edit them, and save them back to the WCF service.

5. Add code to the Select button’s click event to call the WCF service and put the results into the Shipper Data form. Your code should look as follows: 

protected void ButtonGetShipper_Click(object sender, EventArgs e)

{

//todo: add validation & error handling

int shipperId = int.Parse(TextBoxGetId.Text);

 

NwServices.ShipperServiceClient nwShipper = new NwServices.ShipperServiceClient();

NwServices.Shipper shipper = new NwServices.Shipper();

shipper = nwShipper.GetShipper(shipperId);

TextBoxShipperId.Text = shipper.ShipperId.ToString();

TextBoxCompany.Text = shipper.CompanyName;

TextBoxPhone.Text = shipper.Phone;

} 

6. Add code to the Save button’s click event to call the WCF service with the values from the various TextBox controls. Your code should look as follows: 

protected void ButtonSave_Click(object sender, EventArgs e)

{

//todo: add validation & error handling

NwServices.Shipper shipper = new NwServices.Shipper();

shipper.ShipperId = int.Parse(TextBoxShipperId.Text);

shipper.CompanyName = TextBoxCompany.Text;

shipper.Phone = TextBoxPhone.Text;

NwServices.ShipperServiceClient nwShipper = new NwServices.

ShipperServiceClient();

nwShipper.SaveShipper(shipper);

} 

7. Run the application. Enter a Shipper ID (1, 2, or 3). Edit the data and save it back to the database.


ċ
WcfService1.zip
(25k)
Leogena Zhaka,
Jan 30, 2015, 12:20 AM