Practical .NET
Changes Large and Small: WCF 4.5 and the ASP.NET Web API
While Windows Communication Foundation 4.5 has lots of little improvements, the ASP.NET Web API is a very big change. You'll probably end up taking advantage of both, so here's what's in the pipeline for you.
The Microsoft Windows Communication Foundation (WCF) seems to be approaching a kind of maturity. The upcoming release of the Microsoft .NET Framework 4.5, for instance, offers lots of improvements that will make your life better, but only one change that, I think, qualifies as critical -- a new binding that supports the WebSockets protocol (I'll cover WebSockets in my online column later this month). In this article, I'll cover all the little changes in WCF that will make your life better.
But, in a roughly similar time frame as WCF 4.5, Microsoft is also rolling out the ASP.NET Web API, which gives you a very different way of building services by concentrating on the industry-standard technology sets. While WCF lets you build any kind of service you need, the ASP.NET Web API focuses on creating services that run over HTTP -- not necessarily AJAX services, but certainly services that favor interoperability over performance.
Easing the Pain of Configuration
What you'll probably like best about WCF 4.5 is a set of changes that Microsoft has bundled together under the heading "WCF simplification." If you're like most developers, what you find intimidating about WCF isn't writing code in your service methods (which isn't really different from the code that ends up in any other method), but having to deal with WCF configuration elements in your application's config file.
The most obvious example of WCF simplification is the elimination from your application's config file of any element that just makes a default setting -- about 75 percent of the configuration text is gone. If you've been using the older, more verbose entries as a guide when adding or modifying configuration settings, this change might be a mixed blessing. However, the WCF configuration editor still continues to list all the "missing" elements, giving you a guide for how to add new entries.
If you do intend to edit your config file entries in Visual Studio 11, you'll see numerous improvements. All the elements and attributes now have tooltips and IntelliSense support. The IntelliSense support is also dynamic: If you define your named configuration elements before you use them (for instance, if you define a behavior element before referencing it in a service element), you'll get a dropdown list when entering the service element that lists all the applicable behaviors you've defined.
And WCF configuration settings are now validated at compile time, letting you know about problems before you run your code. If, for instance, you misspell a behavior configuration, you'll get an error message when you build your application, at the very latest. I feel obliged to point out that the message is completely obscure, but the important thing is that you'll get an error message before you run your code.
Making Your Life Easier
Some changes reduce the effort to do things you might already be doing. If, for instance, you didn't like working with configuration files and preferred configuring your site with code, you had several hoops to jump through to make that work in a Web application. With WCF 4.5, you can just add to your service class a static/shared method called Configure that accepts a parameter of type ServiceModel.ServiceConfiguration. WCF will take care of calling that method before executing any of your code. In the method you can configure your service by adding objects to the parameter's various collections. The following example adds an endpoint in code:
Public Shared Sub Configure(serviceConfig As _
ServiceModel.ServiceConfiguration)
serviceConfig.Description.Endpoints.Add(
New ServiceModel.Description.ServiceEndpoint(
New ServiceModel.Description.ContractDescription("ICustomer"),
New ServiceModel.BasicHttpsBinding,
"http://www.phvis.com/Customers.svc"))
End Sub
One of the more interesting methods on the ServiceConfiguration class is the LoadFromConfiguration method, which allows you to load your configuration from any file accessible to your service. This allows you to manage all your service configurations from a central location.
If you're using the binary encoder (that is, not in a Web Service) you can now specify a compression format (you need to ensure both the client and the service have matching compression settings). If you want to have an operation on your service that doesn't return a value (a method that clients can call in a "fire and forget" mode), you can use the new User Datagram Protocol (UDP) transport mode.
On the client side, if you're using the default client-side proxy that Visual Studio generates for you when you add a service reference, the ChannelFactory that handles communication with the service is now cached by default and won't be recreated -- provided you don't read the "security related" properties ClientCredentials, EndPoint and ChannelFactory.
If you're going to read those settings and want to keep caching the ChannelFactory, you must set the CacheSetting property on the ClientBase object from which all client-side proxies inherit. The following code, placed in a client's code somewhere before the client-side proxy is instantiated, sets the CacheSetting property for any service that implements to the ICustomers interface so that caching will never be turned off:
ServiceModel.ClientBase(Of ICustomers).CacheSetting =
ServiceModel.CacheSetting.AlwaysOn
In the category of "I'm not sure where I'd use this feature, but …": If you have some XML that defines a business entity and want to create a class that corresponds to that XML, you can have Visual Studio generate the code for you. For instance, this XML effectively describes a Customer entity with a single property called FirstName:
<Customer>
<FirstName>Peter</FirstName>
</Customer>
If you select this XML and copy it, then open a code file in Visual Studio, and (from the menu) select Edit | Paste Special | Paste XML as Classes, you'll end up with code like that shown in Listing 1. (Attribute-based designs are converted similarly.)
No-Effort Improvements
Some of the changes in WCF 4.5 don't require any real effort on your part to take advantage of, especially for services hosted in a Web site. If, for instance, your site has been correctly configured for HTTPS using an SSL certificate and you've enabled HTTPS for the site's virtual directory, you should now be able to add an HTTPS endpoint to your service without any additional effort.
Also, by default, you'll be able to access more ASP.NET functionality from a WCF service hosted on a site. WCF 4.5 also supports multiple authentication modes for a single endpoint, matching the multiple security modes that IIS makes available for a single virtual directory.
If an existing service doesn't scale well or erratically throws errors, upgrading to WCF 4.5 might solve your problem. Often these problems occur because the service is processing larger amounts of incoming data than you initially expected, and the service has either bumped up against one of the arbitrary limits of WCF or has exhausted available memory. WCF 4.5 offers two forms of relief here.
First, on incoming messages, the default limits on message sizes have been raised. The overall controlling limit is still set through the MaxReceivedMessageSize. However, the XmlReaderDictionaryQuotas allow you to refine that setting, and the default limits in that dictionary have all been increased, effectively, to their largest allowed value.
The second source of relief comes on both the sending and receiving side. On the receiving side, WCF used to buffer large, incoming messages until they were completely built before making them available for processing. WCF will now make messages available for processing as soon as they arrive (this also makes the maxRequestLength setting irrelevant, and WCF now ignores it).
On the sending side, if your service is sending messages to multiple slow-reading consumers, you can set the new DispatcherSynchronizationBehavior element's AsynchronousSendEnabled attribute to true, so that threads will be released for use without having to wait for the consumer to accept the whole message.
WSDL Support
I have mixed feelings about the newly added support for contract-first service development. Ideally, Visual Studio should include a tool for generating a Web Services Description Language (WSDL) file that describes a Web Service. Also, ideally, from within Visual Studio, you should be able to generate the skeleton for a Web Service from a WSDL file along with the necessary changes to the config file that WCF requires. The new Visual Studio contract-first support doesn't give you any of those things.
What you do get is a new switch on the svcutil utility called serviceContract. Used in the command prompt with a WSDL file, this switch generates a code file containing your service and any required data-transfer classes. It does not, however, generate the appropriate config file entries that would let you use that service (best of luck with that). It's also not really clear to me that this is much of an improvement over the existing ability to generate code and an interface file using svcutil. Besides, even if it is, I hate shelling out of Visual Studio to use svcutil.
On the other hand, another new option does solve some WSDL-related problems. When you request a WSDL document from a WCF service with the ?wsdl querystring, you get a file with multiple links to other URLs containing components of the WSDL contract. Unfortunately, some toolsets can't deal with this subdivision of files, so some of your business partners may not have been able to process your WSDL document. This format also makes it difficult to pull together a single file for documentation purposes (for instance, to send to a business partner).
You can now get a single file containing all the components by passing ?singlewsdl as the querystring to your service's endpoint. This, obviously, won't solve the problem for toolsets that can't handle the "subdivided" WSDL and follow the industry standard for retrieving a contract by using ?wsdl -- they'll still get the contract they can't process. You'll need to inform those developers that they need to use ?singlewsdl to get a contract they can use.
Web API: Thoroughly Modern Millie
So, if lots of things are better but there's nothing really groundbreaking in WCF 4.5, what's really new? That's where the ASP.NET Web API comes in. The Web API is compatible with the .NET Framework 4, so you can try it out right now (in fact, the Web API installer refuses to install on computers with .NET Framework 4.5). You might know the tool under a different name -- Microsoft originally called this technology the WCF Web API, but it doesn't have much to do with WCF, hence the name change.
Fundamentally, the Web API supports doing what modern, interoperable services are expected to be able to do. You get, for instance, the ability for the client and server to negotiate the format of the content they exchange -- including, out of the box, XML, JSON and Representational State Transfer (REST) formats (Microsoft refers to REST as "URL-encoded formats").
The Web API is designed to support test-driven development (TDD) from the ground up, and is compatible with modern frameworks that manage dependency injection (such as Inversion of Control containers). It even has a dependency management framework of its own.
The Web API also supports the Microsoft technology set, including the ASP.NET MVC authorization attributes. The Web API is (mostly) compatible with the routing framework used in both ASP.NET and ASP.NET MVC. You can even use the OData syntax in making requests to a Web API service, and -- provided your services are returning an IQueryable<T> result -- get the expected result. You can issue requests to Web API services both from JavaScript and .NET clients, and host Web API services in an IIS-based application or in a host you create yourself.
While the Web API can be used in a variety of scenarios, I'm going to demonstrate it in an ASP.NET MVC 4 application. The Web API beta can be downloaded in a variety of formats (you can get it through Visual Studio Extension Manager, for instance), but it comes bundled with the beta of ASP.NET MVC 4.
Building Services and Accessing from JavaScript
To try the Web API out, create a new ASP.NET MVC 4 app from the File | New Project menu choice after installing the ASP.NET MVC 4 beta. You'll then be presented with six different templates, one of which is called Web API. (Note that I tested a few of the other templates and found I could create a Web API application with any of them.)
Using the Web API requires establishing some new routings in your ASP.NET application to decode the RESTful URLs used to make service requests. In the Visual Studio templates, a sample routing has already been added to the project's global.asax file. In the file's RegisterRoutes method you'll find code that sets up a routing by calling a new extension method called MapHttpRoute on the RouteCollection object:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
This code establishes that all URLs beginning with the characters "api" will be processed using a pattern that breaks the URL down into a controller name and a value assigned to a token called id. Under these rules, a valid URL that specifies the Customer controller, without supplying an id value, would look like this: http://www.phvis.com/api/Customer.
Notable by its absence in this routing is any place for an action method name. That's because the Web API automatically assigns the action method name based on the HTTP verb used. So for a standard HTTP Get, the action method invoked by this URL is assumed to be "Get."
In an ASP.NET MVC application, your service code goes into a controller. Because my sample URL sets the controller name as Customer and defaults the action name to Get, to process this URL I need to have a class called CustomerController with a method called Get. For Web API calls, that controller class must inherit from a new class called System.Web.Http.ApiController.
Putting that all together, a controller that would return a collection of Customer objects when requested with my sample URL would look like this:
public class CustomerController : ApiController
{
public IQueryable<Models.Customer> Get()
{
return Models.CustomerFactory.GetSomeCustomers();
}
The Customer class I'm returning is a plain old CLR object (POCO): a collection of properties decorated with the DataContract and DataMember attributes with a constructor to initialize the properties. The class is also decorated with the Serializable attribute so that .NET will let the object travel off the server and down to the client (see Listing 2).
To call this service method from JavaScript, I can use the jQuery getJSON function. The code here calls my Get method and processes the resulting Customers collection by using the jQuery "each" function to insert the object's FirstName property into a list on the page:
$(function ()
{
$.getJSON("/api/Customer",
$.each(custs,
function (key, cust)
{
$("#custNames").append("<li>" + cust.FirstName + "</li>");
});
);
Beyond the Defaults
You're not restricted, however, to naming your server-side methods after the HTTP verbs. There's nothing stopping you from adding your own methods, like these:
public IEnumerable<Models.Customer> AllCustomers()
{
return Models.CustomerFactory.GetAllCustomers();
}
public IEnumerable<Models.Customer> CustomerById(string id)
{
return Models.CustomerFactory.GetCustomerById(id);
}
Your simplest solution for avoiding interfering with the routing for the default HTTP verbs (or the routings for the rest of your site) is to use a different prefix in the URL for your custom named methods. This example specifies that any Web API service URL that includes the string "apiAction" includes the action method name:
routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "apiAction/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });
With this routing in place, a valid URL to request my CustomerById method for customer A123 would look like this: http://www.phvis.com/apiAction/CustomerById/A123.
Managing the Response
While your action method can return bare data, the result that reaches the client will be wrapped up as an HTTP response. If you want complete control over your response, you can use the HttpResponse object. This example, for instance, returns a well-formatted HTTP error with a message and code:
HttpResponseMessage hrm =
new HttpResponseMessage(System.Net.HttpStatusCode.NotFound);
hrm.ReasonPhrase = "No such customer";
hrm.RequestMessage = this.Request;
return hrm;
Returning a single Customer object as an HttpResponseMessage might look something like this:
Models.Customer cust =
Models.CustomerFactory.GetCustomerById(id);
HttpResponseMessage hrm =
new HttpResponseMessage<Models.Customer>(cust)
hrm.StatusCode = System.Net.HttpStatusCode.Created;
return hrm;
Using the HttpResponseMessage, you can also specify the headers to be included in the response.
If the return types from your action methods implement the IQueryable interface, you get free support for the OData querying convention. For instance, if a consumer wanted to get the list of Customer objects from my service methods sorted by their FirstName property, a querystring like this would do the trick:
$.getJSON("/api/Customer?$orderby=FirstName",
function (custs) {
There are a number of ways to create collections that implement the IQueryable interface -- the output of a LINQ query implements IQueryable, for instance. However, you can convert any collection into an IQueryable one using the AsQueryable extension function. For instance, I used the following code to create an IQueryable list of Customer objects to use in testing:
List<Customer> custs = new List<Customer>();
Customer cust = new Customer("A123", "Peter", "Vogel");
custs.Add(cust);
cust = new Customer("B456", "Janis", "Irvine");
custs.Add(cust);
return custs.AsQueryable<Customer>();
However, this is one of those situations where just because you can do it, doesn't mean you should. For instance, OData lets the client request the top n number of results. However, your code might still be retrieving the full set of results -- it's just that in response to an OData request, only a few of those objects will be returned to the client. From an efficiency point of view, you don't want your service to do any more work than the client asks for; and the last thing you want to do is retrieve 50 objects and only send five to the client.
Integrating with .NET Clients
With the Web API, you're not restricted to accessing your service from JavaScript: You can call a Web API service from any .NET application by using the HttpClient class. To use the HttpClient class, you'll need to add references to System.Net.Http and System.Net.HttpFormatting DLLs to your client application.
Because you'll have already created an ASP.NET MVC 4 project, the easiest place to find those libraries is in the packages folder of your MVC project -- just drill down through the System .Net.Http.2.0.20126.16343 folder. You'll also need a reference to the System.Json library, which is distributed with .NET. Finally, you'll need to add a using or Imports statement for the namespace System.Net.Http to grab some of the extension methods used in the following code.
Not surprisingly, considering how much Microsoft is moving toward asynchronous programming in .NET, all the methods on the HttpClient are asynchronous. The easiest way to deal with that is to use the ContinueWith extension method on the HttpClient asynchronous method.
The ContinueWith method accepts a lambda expression with a parameter that represents the Task being executed asynchronously, and a body holding the code that manipulates the result the asynchronous task produces. This example uses the GetAsync method, passing the URL of the request and using ContinueWith to handle the asynchronous processing:
System.Net.Http.HttpClient hc =
new System.Net.Http.HttpClient();
hc.GetAsync("http://localhost:49697/api/Customer/").
ContinueWith(tskRead =>
{
The Result property on the Task passed in the lambda expression holds the result returned from the asynchronous method. For the GetAsync method, that result will be an HttpResponseMessage (even if your method only returned data). The HttpResponseMessage Content property will contain the data you requested, but it would be a good practice to check the HttpResponseMessage Is Success StatusCode property to ensure nothing went wrong on the service before accessing the data:
HttpResponseMessage hrm =
(HttpResponseMessage)tskRead.Result;
if (hrm.IsSuccessStatusCode)
{
To actually retrieve the content, you'll need to use the ReadAsSync method, specifying whether you're expecting a JsonArray or a JsonObject (again, the ContinueWith method is useful here to handle processing the content):
hrm.Content.ReadAsAsync<System.Json.JsonArray>().ContinueWith(
(tskConvert) =>
{
System.Json.JsonArray custs = (System.Json.JsonArray)tskConvert.Result;
foreach (var cust in custs)
{
var custVal = (System.Json.JsonObject)cust;
var string = custVal.GetValue("FirstName").ToString();
}
With the Web API, you're not restricted to accessing your service from JavaScript: You can call a Web API service from any .NET application by using the HttpClient class. But if you're building a service to be used by a wide variety of external business partners, the benefits of Web services (especially the ability to define a WSDL contract) shouldn't be ignored. Similarly, if you're creating services that will only be accessed by consumers from within the company, you'd be foolish to give up the performance benefits of the WCF TCP bindings to gain the HTTP compatibility that the Web API provides.
However, if you're creating services that will be accessed from JavaScript or services that will be accessed by one or two external clients who you're willing to work closely with, then the Web API would be an excellent technology to choose -- once it gets out of beta.