|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionI’m impressed! And that’s not an easy thing to do. I’ve been playing with the CTP (February 2006) release for a while now, and believe it or not, I think we may actually have something here. I have not had the time to explore the other ‘Foundation’ members (of WinFX), but from what I’ve seen of the Windows Communication Foundation (WCF), it really is a very nice package. Yes, I know that each time there is a new technology released, there is always some neat features. But in the past, I always thought that we were paying more (in complexity) than what we were getting (in functionality), can you spell COM? This time, it feels like we are getting the whole thing for free. It’s possible that it may be just my naïve perspective, but hey it’s all about me anyway. I like to preface my articles with a disclaimer. The content in this article is simply my impressions and interpretation, and it should be read in that context. Even though the prose may at times seem like I know what I’m talking about, any similarity to actual facts may simply be coincidence. Enjoy the journey. Why can’t we just talk?The minute we stepped out of the DOS box, we realized that we were not alone any more, and we needed to learn to get along and communicate with the other inhabitants of the virtual world. DDE, OLE, COM, DCOM, and Remoting have been some of the attempts at providing mechanisms for two applications to be able to talk to each other. Remember how OLE and COM were described when first introduced? As the ‘foundation for all future products’. With hindsight, we can see that they were really just baby steps. Each one solved only a small part of the whole problem. So if they were baby steps, then WCF is certainly a giant leap. WCF provides a complete solution to the communication problem. And it does it with elegance and simplicity. Can you tell that I’m just a little enthusiastic? Whether your requirement is to communicate with another module on the same machine, or another module implemented in a different language, or you need to communicate with a module that’s on a machine on the other side of the world, or you want to communicate with a module running on a different platform, or even communicate with a module that’s not even running!, yup, you can do it under WCF. In my opinion, the beauty of WCF is that it is an ‘all-inclusive’ solution. It is the first one to provide a complete end-to-end solution covering the scope and depth of the problem. It is also the simplest from a programmer's point of view; you are always just making a method call. Yeah, I am sure that there is quite a bit of magic going on under the covers to support that simplicity of use. Now, I have not explored every nook and cranny, or option, or possibility, but from what I’ve seen, it’s an excellent solution. At least for me, and as I said before… What is a service?Let’s see how much trouble I can get myself into here. I think that, in its most elemental form, a service is simply some functionality (code) running in some external process that is made available to other processes in a standard way. That’s pretty much the crux of it, except that in a standard way also encompasses platform and language neutrality. So, by the above definition, a service really has two parts. First is the code that must be running somewhere in order to provide some functionality to clients. And second, there must be some generic mechanism that can be used by any process, regardless of the platform, language, or locality, that makes the service accessible. That generic mechanism has turned out to be XML and SOAP. Of course, there are some additional facilities required in order for a client to be able to know (or discover) what functionality the service makes available. But I think of those as supporting technologies. There is also some glue that is required in order to tie the two parts of a service together. That glue is the code that will support the communication medium (transport) that is being used by the service and the client to talk to each other. Being lazy…I mean smart, we’ve come up with some generic glue also. This way, each service implementation does not have to re-invent the wheel. For Web Services, the generic glue is a Web Server. So, a Web Server provides a hosting environment for services that use HTTP as their transport mechanism. I would also like to suggest that Web Services are a special implementation of a service as defined above. Here are the things that we will be examining in the rest of this article. How do you define a service? How do you implement a service? How do you host a service? How do you access and use a service? Once we have the basics nailed down, we’ll look at some of the more complex communication options that WCF facilitates. So what is WCF?Here is what WCF is for me:
WCF is services based technology. It has, as its roots, Web Services, and thus XML and SOAP are its core technologies. WCF took the concept of Web Services and super-charged it. Much of the look and feel of WCF behaves like traditional Web Services. In fact, I like to think of WCF services as Web Services on steroids. You define WCF services much like Web Services. You can interrogate a known service for its methods with the same protocols that are available for Web Services. And you have very similar infrastructure requirements as are for Web Services. The main difference is that WCF has expanded the transports that are available to include TCP/IP, IPC (named pipes), and message queue-ing, in addition to HTTP. I think the focus of Web Services is to solve the interoperability problem, and the focus of WCF is to solve the much broader communication problem. And it has done this while still maintaining a uniform API as well providing more efficient mechanisms. The most important feature from a developer perspective is that you don’t have to be concerned with what or how you are communicating. The code is the same, no matter what the final transport mechanism or locality of service might be. Service Contracts-exposing functionalityOur WCF journey starts with how services define the functionality that they expose. Much of the infrastructure required to implement services under WCF is specified using declarative programming. That means, using attributes to specify functionality. The following shows how to declare an interface that will be exposed as a service: [ServiceContract]
public interface ILocalTime
{
[OperationContract]
string GetLocalTime();
}
public class LocalTimeService : ILocalTime
{
...
}
The By the way, you don’t have to use interfaces when implementing a service, just like you don’t have to use an interface to define a class. You do have to specify what you want exposed through a service, explicitly. You can define anything else you want or need as part of the interface, but only methods, and only methods that get decorated with Data Contracts-exposing data typesWCF also allows you to expose custom data types so that you are not restricted to simple data types of the CLR. These are simple structs with no methods associated with it. This can be a little confusing sometimes, because the same syntax is used for both services as well as for CLR definitions. Here’s an example of a [DataContract]
public class SensorTemp
{
[DataMember]
public int probeID;
[DataMember]
public int temp;
}
Coding options in WCFAs we saw above, one of the options available to specify functionality under WCF is to use attributes. Attributes are translated by the compiler to generate much of the infrastructure required by WCF in order for us to create and use services. The second way you can specify many of the options is through configuration files. This allows you to make changes without having to re-compile. Many of the WCF classes will automatically use default values from the config file. Here’s an example of an endpoint specified using config data (endpoints will be described shortly). First, the config file, then the code statement referencing the config file data: <endpoint name ="LocalTimeService"
address="net.pipe://localhost/LocalTimeService"
binding="netNamedPipeBinding"
contract="ILocalTime" />LocalTimeProxy proxy = new LocalTimeProxy("LocalTimeService");
Finally, the third way of coding functionality is, of course, programmatically. Many of the things that you can do via attributes or config files can also be done programmatically. Here is the previous endpoint, defined programmatically: Uri baseAddress = new Uri(ConfigurationManager.AppSettings["basePipeTimeService"]);
baseAddress = new Uri(ConfigurationManager.AppSettings["basePipeTimeService"]);
serviceHost.AddServiceEndpoint(typeof(ILocalTime),
new NetNamedPipeBinding(), baseAddress);
EndpointsEndpoints are the ‘identity’ of a service. They define all the information that we need in order to establish and communicate successfully with a service. Endpoints are made up of three pieces of information: Address, Binding, and Contract. The address is obviously the location of the service, such as ‘net.pipe://localhost/LocalTimeService’. The binding specifies security options, encoding options, and transport options, which means a lot of options! Luckily, there is a collection of pre-defined bindings provided with WCF that we can use to make our life simpler. And finally, the contract is the actual interface that the service implements. Implementing a WCF ServiceSo, a service is nothing more than a regular class that gets decorated with some special attributes. The attributes are then translated by the compiler to generate the special infrastructure code required to expose the class as a service to the world. In the following code, we first define an interface that has one method that returns the local time of where the service has been deployed. The [ServiceContract]
public interface ILocalTime
{
[OperationContract]
string GetLocalTime();
}
[ServiceBehavior(InstanceContextMode =
InstanceContextMode.PerCall)]
public class LocalTimeService : ILocalTime
{
public string GetLocalTime()
{
return DateTime.Now.ToShortTimeString();
}
}
That’s all that’s needed to create a WCF service. If you compile the above code into a DLL (a library), you will have created a service. Of course, there is a little more needed in order to have something that’s useable. We need two other pieces in order to complete the service. We need something that will be able to load the service DLL when a client requests the functionality of the service. And we need something that will be able to listen on a communication port, and look through everything that is being received to see if it matches what we are responsible for, our service contract. Deploying a WCF ServiceThere are a number of ways to deploy our service. First, if we implement our service to support HTTP, then we could deploy our service just like a regular Web Service, using IIS. If we want to support some of the other transports, then we could deploy the service using Windows Activation Service (WAS), which is an enhancement available in IIS 7.0. If either of these is not suitable or we want more control over the service, then the other solution is to build our own hosting environment by using
class TimeService
{
static void Main(string[] args)
{
Uri baseAddress = new Uri(
ConfigurationManager.AppSettings["basePipeTimeService"]);
ServiceHost serviceHost = new ServiceHost(typeof(LocalTimeService),
baseAddress);
serviceHost.Open();
Console.WriteLine("Service is running....press any key to terminate.");
Console.ReadKey();
serviceHost.Close();
}
}
You can now compile the service. However, if you try to run it, you’ll get an error message indicating that you haven’t provided Here’s the config file that we’ll need for the <configuration>
<appSettings>
<add key="basePipeTimeService"
value="net.pipe://localhost/LocalTimeService" />
</appSettings>
<system.serviceModel>
<services>
<service name="LocalTimeService">
<endpoint
address=""
binding="netNamedPipeBinding"
contract="ILocalTime"
/>
</service>
</services>
</system.serviceModel>
</configuration>
Let’s examine the entries in the config file. You should note that there could be as many entries as needed. For example, there could be several endpoints that the service supports (different transports). There is also only one service being specified in this example, but there could be several services provided in the same housing. You can see that the endpoint has three properties: address, binding, and contract. The binding indicated is referencing the standard I will say here that you too will encounter the “zero application (non-infrastructure) endpoints” exception at some point. There won’t be too many clues as to what exactly is not matching up, so you’ll have to scrutinize the text. Make sure that you have the correct namespaces specified. Now you can execute the application, and the service will be available to any client that knows how to communicate with it. ProxiesJust by saying we want to go to New York and we are going to go by car, does not get us there. We need a car to actually get us there. Having a completely defined endpoint is not enough. We need something (code) that will actually take the endpoint as a parameter and allow us to do what we want to do, call a method. And that something is a proxy. As was the case in the past with COM, proxies take care of all the low level plumbing (serializing and packaging our parameters) so that we just need to make the call. We don’t care how they are forced through the ‘spigot’ or how they are pulled out on the other side. And as we’ve also had in the past, there is a utility that will create the proxies for us. However, this is one area of WCF that needs some improvement. And I’m guessing this functionality will become incorporated into Visual Studio in future releases. At least, I would hope so. To create a proxy, you need to use the command line utility, svcutil, which has a number of switches that are not all or well documented. But hey, I’m not complaining, it’s a small inconvenience for a whole lot of major improvements. And it’s still only Beta. So, you run svcutil against your service DLL and bam! You got your proxy class. There are other options, like if the service has a MEX endpoint, you can direct it to the service, and it will extract the service information dynamically from the service. This is essentially the same functionality provided through Studio when creating a Web Service, and we use the ‘Add Web Reference’ dialog. What I really want is for Visual Studio to automatically generate the proxies since it has all the information in the source files to begin with! But as I said, I’m not complaining. ;) Currently then, creating the proxy is a two step process. First, you run svcutil against your service DLL, which will create the schema (XSD) and WSDL files. Then, you invoke svcutil again, but this time, you run it against the output it just created (*.xsd, *.wsdl). The svcutil will generate ‘output.cs’ as the default file name, unless you specify otherwise, I normally just rename it. There are also options to just generate [ServiceContract]
public interface ILocalTime
{
[OperationContract]
string GetLocalTime();
}
public interface ILocalTimeChannel : ILocalTime,
System.ServiceModel.IClientChannel
{
}
public partial class LocalTimeProxy :
System.ServiceModel.ClientBase<ILocalTime>, ILocalTime
{
public LocalTimeProxy()
{
}
public string GetLocalTime()
{
return base.InnerProxy.GetLocalTime();
}
}
Consuming a WCF ServiceSo now, the next logical step is to build a client that knows how to consume the service that is being provided. The client code only needs two things: the proxy that allows it to communicate with the service, and the endpoint to the service. Here’s one version of a client that consumes the class Client
{
public bool keepClocking = true;
LocalTimeProxy proxy = null;
public Client()
{
proxy = new LocalTimeProxy();
}
public void ClockingThread()
{
while (keepClocking)
{
Console.WriteLine(proxy.GetLocalTime());
Thread.Sleep(1000);
}
proxy.Close();
}
static void Main(string[] args)
{
Client client = new Client();
//Create a separate thread to request the time once a second
Thread thread = new Thread(new ThreadStart(client.ClockingThread));
thread.Start();
Console.WriteLine("Service is running....press" +
" any key to terminate.");
Console.ReadKey();
client.keepClocking = false;
//wait 2 seconds
Thread.Sleep(2000);
}
}
Not much to this client. All that it's doing is making a call to the service method <configuration>
<system.serviceModel>
<client>
<endpoint name ="LocalTimeService"
address="net.pipe://localhost/LocalTimeService"
binding="netNamedPipeBinding"
contract="ILocalTime" />
</client>
</system.serviceModel>
</configuration>
Compile and run the client. Start several instances. Just make sure that the service is started before you start the clients, otherwise nobody will be listening. That’s it for the basics in getting services listening and consuming services. In Part 2, we'll build some examples that will demonstrate WCF's support for the various communication patterns. The download includes all of the source code for the sample applications described in the articles. | ||||||||||||||||||||