In this chapter, you will be introduced to all the changes that have been made in .NET 4.5 with respect to Windows Communication Foundation (WCF). These changes include the use of async-await from the client side, improvements made to WCF configuration, and the inclusion of new bindings—HttpsBinding
and UdpBinding
. In addition, you will also learn about Web API, which is the new way of creating RESTful services.
You finish the chapter learning about WebSockets, which is another addition made to WCF to help in establishing a true bidirectional communication via sockets over the web that doesn’t have the overhead of frequent request-response.
As you have already learned from the previous chapters, .NET 4.5 supports async-await calls, and this support extends to WCF as well. When you create a Service Reference to a service from Visual Studio, the async calls are automatically generated for you in the proxy.
For example, the proxy code generated by the SvcUtil tool for an async call on a simple HelloWorld
method will look like the following:
public System.Threading.Tasks.Task<string> HelloWorldAsync() {
return base.Channel.HelloWorldAsync();
}
And, if you are calling this method in an event handler, say for a button pressed event, your code would look something like this –
private async void OkButton_Click(object sender, EventArgs e)
{
var proxy = new Service1Client();
textBox.Text = await proxy.HelloWorldAsync();
}
This code ensures that the thread doesn’t wait until the call to the WCF service finishes.
In the previous versions of WCF, the configuration file that was auto-generated tended to look complex. This was due to the fact that when Visual Studio uses SvcUtil to create, all the default values of the bindings were present in the configuration file as well. For example, a simple wsHttpBinding
on the client would look like the following:
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_IService1" 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" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8732/Design_Time_Addresses/HelloWorld/Service1/"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService1"
contract="ServiceReference1.IService1" name="WSHttpBinding_IService1">
<identity>
<dns value="localhost" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
Clearly, this doesn’t help in readability of the configuration file. In Visual Studio 2012, when SvcUtil is used to generate the proxy and the configuration file, the default values are taken out and the equivalent configuration file will look like the following:
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<ws2007HttpBinding>
<binding name="WS2007HttpBinding_IService1" />
</ws2007HttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:8733/Design_Time_Addresses/HelloWorldService/Service1/"
binding="ws2007HttpBinding" bindingConfiguration="WS2007HttpBinding_IService1"
contract="ServiceReference1.IService1" name="WS2007HttpBinding_IService1">
<identity>
<userPrincipalName value="Mahesh-XPSMahesh" />
</identity>
</endpoint>
</client>
</system.serviceModel>
</configuration>
When you edit in the configuration file in Visual Studio, you are able to see the tooltips for properties, as shown in Figure 8-1. These tooltips are taken from the WCF configuration documentation and are quite handy while editing the configuration file manually.
Another change in the configuration editor is better IntelliSense support. For instance, when you are trying to type the binding type, you get prompted with all the available options, as shown in Figure 8-2.
This is not the only area of improvement—you will also see IntelliSense pop up when you are typing in a service name, contract name, binding or behavior configuration name, and so on. This allows you to avoid typos when you are manually typing in the information.
Regarding typos, if you do end up typing the wrong behavior or binding configuration name by mistake, Visual Studio will automatically pick up the validation error and display it for you, as shown in Figure 8-3.
Before .NET 4.5, the main transport protocol that was supported out of the box was TCP. Universal Datagram Protocol (UDP) is a protocol that bypasses the handshake between the two connection points, thus speeding up data transfer rates but with a trade-off on other important things such as security, reliability, ordering of messages, and data integrity. The main reason for using UDP binding is for transmitting quick fire-and-forget messages to the service.
There are plenty of examples on how to get UDP working with older versions of WCF, but the process involves a bit of work on your side. However, with .NET 4.5, you can use a binding called UdpBinding
. UdpBinding
cannot be hosted on IIS or WAS, and the code to self host a simple service using UdpBinding
:
class Program
{
[ServiceContract]
public interface IUdpService
{
[OperationContract]
void SendMessage(string message);
}
public class UdpService : IUdpService
{
public void SendMessage(string message)
{
//Do something
Console.WriteLine("Message: {0}", message);
}
}
static void Main(string[] args)
{
using(var serviceHost = new ServiceHost(typeof(UdpService), new
Uri("soap.udp://localhost:40000")))
{
serviceHost.AddServiceEndpoint(typeof(IUdpService), new UdpBinding(),
string.Empty);
serviceHost.Open();
Console.WriteLine("Press any key to close service...");
Console.ReadLine();
}
}
The URI scheme for UDP binding starts with soap:udp://
as seen in the previous code. To use UdpBinding
in your service, you need to add a reference to the assembly System.ServiceModel.Channels
in addition to System.ServiceModel
. The client code using UdpBinding
looks like the following:
var channelFactory = new ChannelFactory<IUdpService>(new UdpBinding(),
new EndpointAddress(new Uri("soap.udp://localhost:40000")));
var proxy = channelFactory.CreateChannel();
proxy.SendMessage("Hello there");
If you need to use the basicHttpBinding
in a secure environment, you will most likely run it over a secure HTTPS connection in WCF. To do that, you need to set the security mode to Transport
, which needs to be done by customizing the binding in the configuration file:
<bindings>
<basicHttpBinding>
<binding name="customHttpsBinding">
<security mode="Transport">
<transport clientCredentialType="None"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
In .NET 4.5, rather than create your own custom binding, you can use the newly added BasicHttpsBinding
. An example of such a binding being set in configuration is shown below:
<services>
<service name="Sample.HelloWorldService">
<host>
<baseAddresses>
<add baseAddress = "https://.../HelloWorldService/" />
</baseAddresses>
</host>
<endpoint address="" binding="basicHttpsBinding" contract="Sample.IHelloWorldService"/>
</service>
</services>
Note Starting from WCF 4.0, if you create a service without specifying any endpoints, the runtime will automatically add endpoints for you. In WCF 4.5, if you host your service in IIS and configure IIS to use SSL, the runtime will automatically create endpoints for both HTTP (BasicHttpBinding
) and HTTPS (BasicHttpsBinding
).
In WCF 4.5, you can now compress the contents of your message when you use binary encoding. To make use of this, you need to set the CompressionFormat
property, which can either be set to Deflate
or GZip
. The following is a sample configuration for enabling GZip
compression:
<customBinding>
<binding name="GZipBinding">
<binaryMessageEncoding compressionFormat ="GZip"/>
<httpTransport />
</binding>
</customBinding>
Enabling compression is really useful where the network bandwidth is an issue, but you have to keep in mind that it puts an additional load on the CPU that could lead to adverse effects.
Tip Remember that you need to enable compression on both the server and the client side. If you don’t do that, you will end up getting a ProtocolException
.
SOAP, REST, AND WCF
The pre-beta release of the Web API was packaged as part of WCF, and it was also called WCF Web API. But there was no need for it to be part of WCF—the Web API relies more on the HTTP protocol and verbs, and it does not use SOAP protocol at all. With the beta, Microsoft moved the API out of WCF into ASP.NET and rebranded it as ASP.NET Web API. However, it doesn’t change the way it works. But, first, you need to understand how REST operates before you delve into how to use the new Web API.
REST stands for Representational State Transfer. It was an idea floated by Roy Fielding. Roy Fielding, for those who don’t know, was one of the principal authors of the HTTP protocol. Strictly speaking, REST is not really a protocol or specification—it is actually an architectural style and is used for representing resources on the net. The representation of a book, for instance, could look something like this: http://someuri/book/9781430243328
, where 9781430243328 is the ISBN of the book. When you access the URL, you could effectively get the details of the book in some format such as Plain Old XML (POX) or JSON.
So, what does REST fix? And why do we really need it? To answer these questions, we need to look at the SOAP protocol, which is what we currently use in WCF. SOAP is good and there are various standards built around it, but if you look into a simple message sent via SOAP, you will realize that it is heavy in nature. To call a simple GetBookDetails
method in a service, the SOAP message would need to be formatted to look like the following:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IBookServic
e/GetBookDetails</Action>
</s:Header>
<s:Body>
<GetBookDetails xmlns="http://tempuri.org/">
<isbn>9781430243328</isbn>
</ GetBookDetails >
</s:Body>
</s:Envelope>
If you start using some of the WS-* protocols for things such as securing the payload or authentication, the message gets even bigger and more complex. The equivalent call using REST would be a simple URL such as http://someurl/book/9781430243328
. The response to this URL would look something like the following, which is a lot more readable than a SOAP response:
<book>
<isbn>9781430243328</ isbn >
<title>Introducing .NET 4.5</title>
<publisher>APRESS</publisher>
<authors>
<author>Mackey, Alex</author>
<author>Tulloch, William</author>
<author>Krishnan, Mahesh</author>
</authors>
</book>
Note SOAP, which originally stood for Simple Object Access Protocol, is just referred to as SOAP and is no longer an acronym—a clear indication that it is no longer simple. And in spite of all the talk about standards, interop is still a big issue while using SOAP.
One of the main advantages of using REST is the ability to perform CRUD (Create, Read, Update, and Delete) operations on resources using just HTTP verbs. Table 8-1 shows how the verbs can be used for CRUD operations.
The HttpClient
class was introduced with the now obsolete WCF REST Starter Kit mentioned earlier. It is primarily used to work with HTTP requests and their responses, which come in handy when you are working with REST services. Unlike the original version from the REST Starter Kit, the HttpClient
shipped with .NET 4.5 supports async calls as shown in the following code snippet:
public async void CallRestService(string url)
{
try
{
using (var client = new HttpClient())
{
var response = await client.GetStringAsync(url);
//Do something with the response
}
}
catch (HttpRequestException e)
{
//Do something with exception
}
}
The class also contains methods to send common HTTP verbs such as Get, Put, Post, and Delete when calling a URL. This is very useful for REST calls, and some of the useful methods are shown in Table 8-2.
To create a simple RESTful service for a resource of books using ASP.NET Web API, follow these steps:
Controllers
, Model
, and Views
), along with several files. You need to create your own model that holds the data structure of a Book
object and a controller that will hold the logic for the REST service.Models
folder in the Solution Explorer and select Add New item from the menu. The Add New Item dialog box will appear.Book
and use the following code snippet to create a class for Book
and Author
in the Book.cs
file:
Controllers
folder and choose Add Controller from the popup menu. An Add Controller dialog box will open up, as shown in Figure 8-6.
BooksController
and choose the Template API controller with empty read/write actions. Press OK to create the controller. The class auto-generated by Visual Studio will look the following code:
public class BooksController : ApiController
{
// GET /api/books
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET /api/books/5
public string Get(int id)
{
return "value";
}
// POST /api/books
public void Post(string value)
{
}
// PUT /api/books/5
public void Put(int id, string value)
{
}
// DELETE /api/books/5
public void Delete(int id)
{
}
}
Note The code template creates entry points to do basic CRUD operations, but the return values are strings and the id used to access objects are integers. In our simple example, we want to make use of the class Book
and the ISBN
string as identifiers.
BooksController
code in the file with the following code:
public class BooksController : ApiController
{
Dictionary<string, Book> bookDictionary =
new Dictionary<string, Book> {
{
"9780470524657",
new Book
{
Title = "Silverlight for Dummies",
Isbn = "9780470524657",
Authors = new Author[] {
new Author { FirstName = "Mahesh", LastName = "Krishnan"},
new Author { FirstName = "Philip", LastName = "Beadle"},
},
}
},
{
"9781430243328",
new Book
{
Title = "Introducing .NET 4.5",
Isbn = "9781430243328",
Authors = new Author[] {
new Author { FirstName = "Mahesh", LastName = "Krishnan"},
new Author { FirstName = "Alex", LastName = "Mackey"},
new Author { FirstName = "William", LastName = "Tulloch"},
},
}
},
};
// GET /api/books
public IEnumerable<Book> Get()
{
return bookDictionary.Values;
}
// GET /api/books/5
public Book Get(string id)
{
try
{
return bookDictionary[id];
}
catch (Exception e)
{
throw new HttpResponseException(new
HttpResponseMessage(HttpStatusCode.NotFound));
}
}
// POST /api/books
public void Post(Book book)
{
}
// PUT /api/books/5
public void Put(string id, Book value)
{
}
// DELETE /api/books/5
public void Delete(string id)
{
}
}
This code creates some sample data for the controller and implements the two Get
methods. When a call to the URL http://.../api/books
is made, the controller will return a list of books, and when a call is made with a specific ISBN, such as http://.../api/books/9781430243328
, only that book will be returned.
RestBookApi
with the actual namespace of the project:
using System.Net;
using RestBookApi.Models;
The type of browser or client you use to access the REST service determines what format the data is returned. But, how does the Web API know what format to send based on the client? The answer to that lies in the requests header—more specifically, the Accept
header—that is passed in the request to the server. If you inspect the request headers in a tool such as Fiddler or the Developer tools in Internet Explorer, you will be able to see these values, as shown in Figure 8-8.
If you change the values of the Accept
header, you can direct the server to send the response in the format of your choosing. For instance, to send the data back in XML from a simple console application using the HttpClient
class discussed earlier, your code will something like the following code:
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace RestClient
{
class Program
{
static string baseAddress = "http://localhost:48178/";
static void Main(string[] args)
{
CallRestServiceAsync(string.Format("{0}/api/books/", baseAddress)).Wait();
}
public static async Task CallRestServiceAsync(string url)
{
try
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Add("Accept", "text/xml");
var response = await client.GetStringAsync(url);
Console.WriteLine(response);
}
}
catch (HttpRequestException e)
{
Console.WriteLine(e.Message);
}
}
}
}
This code allows you to plug in your own set of media formatters on the server that can be used to customize the return type. To create your own media formatter, you need to derive a class from BufferedMediaTypeFormatter
and implement the WriteToStream
override. You also need to specify the supported MIME type and overrides called CanWriteType
and CanReadType
, which is used to determine whether the object type you are serializing is supported. The following is a sample CsvFormatter
that is used to serialize IEnumerable<Book>
:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http.Formatting;
using System.Net.Http.Headers;
using RestBookApi.Models;
namespace RestBookApi
{
public class CsvFormatter : BufferedMediaTypeFormatter
{
public CsvFormatter()
{
SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
}
public override bool CanReadType(Type type)
{
return false;
}
public override bool CanWriteType(Type type)
{
return typeof(IEnumerable<Book>).IsAssignableFrom(type);
}
public override void WriteToStream(Type type, object value, Stream stream, HttpContentHeaders contentHeaders)
{
var books = value as IEnumerable<Book>;
if (books != null)
{
var writer = new StreamWriter(stream);
writer.WriteLine("Isbn, Title");
foreach (var book in books)
{
writer.WriteLine("{0}, {1}", book.Isbn, book.Title);
}
writer.Flush();
}
}
}
}
Note This is a very simple example of a CSV media formatter. When you implement your own formatter, you not only check if the object that needs to be serialized is a type of IEnumerable<Book>
, but also if a Book
object is passed to it. In addition, you would also do more work on the actual WriteToStream
method to make sure that the Author
objects are serialized. If you wish to read the data passed on to you, you would also implement the ReadFromStream
method.
After you’ve added this class to your project, you also need to register this formatter in your global configuration. You can do this in the Application_Start
method in the Global.asax.cs
file:
public class WebApiApplication : System.Web.HttpApplication
{
…
protected void Application_Start()
{
…
GlobalConfiguration.Configuration.Formatters.Add(new CsvFormatter());
}
…
}
When you are working with simple HTTP request/responses, which is what Web API is all about, error messages become nothing more than HTTP status codes on a web response. Some common HTTP status codes are shown in Table 8-3.
This concept doesn’t change when working with errors in Web API. A simple exception of type HttpResponseException
is thrown with the corresponding HttpStatusCode
enumeration inside an HttpResponseMessage
object. For example, if a book with the requested ISBN is not found, then you throw an exception:
//Isbn not found…?
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
If you want to complete the full range of CRUD operations, you need to implement the Put
, Post
, and Delete
methods in the BookController
class. Let’s start with the insert first. A very simple implementation for the Post
, which is an insert operation, would like the following:
// POST /api/books
public void Post(Book book)
{
bookDictionary[book.Isbn] = book;
}
Although this will work, there are a couple of issues with the implementation. REST API should reply to a POST message for insert with a status code of 201 (Created), rather than the default 200 (OK), which is what the Web API sends. In addition, the result of the creation (which is, effectively, a copy of the newly created data) is sent back to the client along with the URI where the newly created resource can be found. To address these issues, the actual implementation of the Post
needs to change to look similar to the following code snippet:
// POST /api/books
public HttpResponseMessage<Book> Post(Book book)
{
bookDictionary[book.Isbn] = book;
var response = Request.CreateResponse ( HttpStatusCode.Created);
string uri = Url.Route(null, new { id = book.Isbn });
response.Headers.Location = new Uri(Request.RequestUri, uri);
return response;
}
To add the update routine, the Put
method in the controller class needs to be changed to something like the following code:
// PUT /api/books/5
public void Put(string id, Book value)
{
if(bookDictionary.ContainsKey(id))
bookDictionary[id] = value;
else
throw new HttpResponseException(new
HttpResponseMessage(HttpStatusCode.NotFound));
}
Notice that if the ISBN
is not found, an HttpResponseException
with the HttpResponseMessage set to HttpStatusCode.NotFound
is thrown.
To implement Delete
, the code snippet will look similar to the following:
// DELETE /api/books/5
public void Delete(string id)
{
if(bookDictionary.ContainsKey(id))
bookDictionary.Remove(id);
}
How does the Web API know that when the URL is http://.../api/books,
the BooksController
should be used? If you are already familiar with ASP.NET MVC, you probably already know the answer. ASP.NET MVC uses a routing table to keep track of what controllers and what actions to invoke when a URL is called. The Web API hooks on to the same routing table. When Visual Studio code generated files for you, it automatically added the routing logic for you in the RouteConfig.cs
file. If you open the file, you will notice the following lines added to the RegisterRoutes
method:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
When a client calls the URL http://.../api/books/12345
, the Web API adds Controller
to the value in the {controller}
part in the route template, which, in this case, is books
(and so the BooksContoller
is used).
The next question is How does the Web API know which method to call? The answer to that is quite simple. It depends on a combination of the HTTP method used in the URL and the method present in the controller class. Table 8-4 demonstrates how it works.
Tip If you have a method that starts with Get
in your controller, but you do not want the Web API to use it, just put the NonAction
attribute in front of the method.
Sometimes using just convention to figure out what action to call, or rather what method to call, just doesn’t cut it. You may want to use your own method names. The way to get Web API to figure out what HTTP method maps to what method in the controller is by way of attributes that you attach to these methods.
For instance, if you want to call your method LoadBooks
instead of GetBooks
, you need to add the HttpGet
attribute to the method:
[HttpGet]
Public Books[] LoadBooks()
{
…
}
Table 8-5 shows the attribute for the corresponding HTTP method.
As REST calls are just URLs, it gives us a wonderful opportunity to use query strings to start filtering data, sorting data in a certain order, and even introducing paging. Microsoft introduced a standard way of doing this in a protocol called OData, which stands for Open Data. To enable Web API to use OData, you need to install the NuGet Package Microsoft.AspNet.WebApi.OData.Web API uses the query parameters that are part of the OData protocol to implement filtering, sorting, and paging. To make use of these features, you need to return an IQueryable<T>
rather than an IEnumerable<T>
in your Get
functions.
For instance, the example of Get
we have used to return all the Book
objects would change to something like the following:
// GET /api/books
public IQueryable<Book> Get ()
{
return bookDictionary.Values.AsQueryable();
}
Once this is done, you would be able to use query parameters such as the following ones:
http://.../api/books?$filter=Title%20eq%20'Silverlight%20for%20Dummies'—
This will filter the results and bring only books whose title equals Silverlight for Dummies.http://...api/books?$top=10
—This will bring in the top ten items.http://...api/books?$skip=10
—This will skip the first ten items and bring the rest.http://.../api/books?orderby=Title
—This will order the results by title.http://.../api/books?orderby=Title%20desc
—This will order the results by title in descending order. Tip You are not restricted by just one query parameter. You can use a combination of the query parameters to get the data you are looking for. The format of the query strings that are used in OData can be found at http://www.odata.org/developers/protocols/uri-conventions
.
Currently, when a web browser, or any other client for that matter, connects to a server via HTTP protocol, it is in a simple request/response format. Although this is just fine for most scenarios, it fails when the server needs to repeatedly send live data (such as share prices or sports scores and updates) back to the client. One way to overcome this is through polling (and a variation of it called long-polling). Another way is through live streaming.
These approaches have several drawbacks—prominent among them being the large amounts of data being sent back and forth across the network. And these solutions don’t scale well, either.
One of the solutions to fix this is to establish a socket connection via the web through which web browsers can connect to the server and have full-duplex, bidirectional communication. This new protocol is called WebSockets. It works with the client and server using the HTTP protocol to exchange information and establish a handshake. Once done, it is used to set up a proper sockets connection, which can then be used to send information both ways anytime—client to server and server to client. And the data sent between the two can either be in binary or text.
To create WebSockets binding in the server, you need to user either NetHttpsBinding
or NetHttpBinding
, depending on whether the transport needs to be secure or not.
Note WebSockets does not work on older versions of the Windows operating system. You need Windows 8 or higher for it to work, as it requires IIS 8.
Follow these steps to create a simple service that runs long-running operations and provides feedback to the client on progress:
WebSocketsExample
(or any other name, but use the name suggested so that the namespace will be the same as the example).IService1.cs
and Service1.cs
file that gets automatically generated.LongRunningService.cs
.using System.ServiceModel;
using System.Threading;
namespace WebSocketsExample
{
[ServiceContract(CallbackContract = typeof(IProgressCallbackService))]
public interface ILongRunningService
{
[OperationContract(IsOneWay = true)]
void DoLongRunningOperation();
}
[ServiceContract]
public interface IProgressCallbackService
{
[OperationContract]
void UpdateProgress(int percentageComplete);
}
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class LongRunningService : ILongRunningService
{
public void DoLongRunningOperation()
{
for (var i = 0; i <= 100; i += 10)
{
Thread.Sleep(1000);
try
{
OperationContext.Current.GetCallbackChannel<IProgressCallbackService>().
UpdateProgress(i);
}
catch (CommunicationException)
{
//Ignore communication exception in this simple implementation
//Handles the client closing down the connection
}
}
}
}
}
This code snippet creates a service called LongRunningService
that takes about ten seconds to run (which we’ve implemented with a Thread.Sleep
method). At the end of each second, it calls a method in the call back service to provide with an update of the progress.
App.config
file in the project and change the service name to WebSocketsExample.LongRunningService
. Then replace the default basicHttpBinding
binding in the service endpoint to NetHttpBinding
and add a bindings section for netHttpBinding
. These changes are shown in bold:
…
<services>
<service name="WebSocketsExample.LongRunningService">
…
<!-- Service Endpoints -->
<!-- Unless fully qualified, address is relative to base address supplied above -->
<endpoint address="" binding="netHttpBinding"
contract="WebSocketsExample.ILongRunningService" bindingConfiguration="WebSocketsBinding">
…
</services>
<bindings>
<netHttpBinding>
<binding name="WebSocketsBinding" >
<webSocketSettings transportUsage="WhenDuplex"/>
</binding>
</netHttpBinding>
</bindings>
<behaviors>
…
Typically, WebSockets services are called from within a browser (using JavaScript). But, to access the service from within a .NET client, you can create a simple console application to the same solution and add a service reference pointing to the service you just created. Then change the default Program.cs
file to call the service as shown in the following code:
using System;
using System.ServiceModel;
using WebSocketsClient.ServiceReference1;
namespace WebSocketsClient
{
class CallbackContract : ILongRunningServiceCallback
{
public void UpdateProgress(int percentageComplete)
{
Console.WriteLine("Progress indicator: {0}", percentageComplete);
}
}
class Program
{
static void Main(string[] args)
{
var callbackContext = new InstanceContext(new CallbackContract());
var proxy = new ServiceReference1.LongRunningServiceClient(callbackContext);
proxy.DoLongRunningOperation();
Console.ReadLine();
}
}
}
This looks like any other WCF duplex implementation, but the app.config file will contain all the binding to make it use WebSockets.
Tip The address for WebSockets starts with ws://
. You can open app.config
on the client project to check this out.
This chapter introduces you to all the changes that have been made to WCF in .NET 4.5. The biggest change, in my opinion, comes from the addition of the Web API. The Web API lets you create REST services with ease from an ASP.NET MVC project template. The bulk of the Web API used to be part of WCF in earlier preview releases on Visual Studio 2012, but it was eventually pulled out and made part of ASP.NET. If you think about it, it does make sense since the REST services are more closely aligned to exposing services across the web.
Support for async calls and the introduction of WebSockets are also important changes in WCF and are covered in the chapter. In addition, WCF 4.5 introduces a number of changes such as support for UDP, a new binding called BasicHttpsBinding
, and support for compression. The chapter also talks about the improvements made in Visual Studio and (WCF) that make their configuration a lot easier.