Previous posts in this series:
Working with WCF: Part One – Introduction and Your First Service
So now you have a service, congratulations! What are you going to do with it?
Be A Good Host
Some people like to say of WCF that you have “… a multitude of hosting options” but in reality you have two; host it in IIS or create your own host.
IIS has a lot going for it. Odds are if you are in a .NET shop you are already using it and have people on staff who know how to manage it. It has facilities to handle scaling, memory management and process cycling. IIS makes managing services relatively easy.
One thing to note here is that if you are using IIS 6 you can ONLY host HTTP based endpoints for your service (more on endpoints below). That means no TCP, Named Pipes or MSMQ for you! IIS 7 ships with Windows Activation Services (WAS) which allows you to host all types of endpoints in your IIS installation. If you’re one of the lucky ones that has access to IIS 7 with WAS, I say use it!
IIS deployment can be a bit tricky (especially with WAS), so I’m going to cover that in it’s own dedicated post in the future.
Self-hosting is the umbrella the “multitude people” use to cover the “multitude of ways” you can host WCF You can host WCF services in a console application, a Windows service, a Windows application, etc. But they all come down to the same thing; writing your own custom host for WCF. In the days of ASMX, if you wanted to self-host you'd have to, well… you'd have to write IIS. Which sounds hard, and provides little benefit since someone already wrote IIS! You could write customized HTTP modules to inject some of your own functionality, but the pain level for that is rather high.
I’m Already Out of Dumb “Host” Puns, So Let’s Just Write Some Code
Don’t worry, this is gonna be a breeze
When we last left our WebBagels solution, it looked something like this:
In this example we’re going to host this service in a console application, but the same approach can be used for Windows services or Windows applications. The first step is to add a console application called WebBagels.Host to our solution:
WCF allows you to write your own hosting logic by exposing a class called ServiceHost which is in the System.ServiceModel assembly/namespace. You’ll need to add a reference to System.ServiceModel to webBagles.Host. At a minimum all you have to do is tell it what kind of service class you want to host and and then call Open:
On line 12 you can see the declaration of our service host, which is going to do all of our heavy lifting. In order to create this class we have to pass it the type where our service implementation exists, in this case BagelOrderService. Our contract is IBagelOrderService, and BagelOrderService is but one possible implementation of this contract. We could have several implementations of our service available, and if need be our hosting logic could determine which particular implementation it wants to host at start up. A good, almost everyday example of this would be testing. You may have a production implementation that uses actual production resources, and a “stub” test version that just responds as if it was doing something.
The rest of this Console App 101; we open our service, tell the user we’re ready and wait for them to press a key, at which point we shut everything down.
Some example of this you’ll see will create the service host in a using block:
I don’t like to do that. Yes, this is easier, but the problem is the Dispose method on ServiceHost (I know, it doesn’t show up, but trust me it’s there) attempts to close the host. The but what if the exception is thrown during open? In this case close is going to create it’s own exceptions (in this case an InvalidOperationException and a CommunicationObjectFaultedException) and we’ll never know why the initial open failed. Wrapping our hosting logic in a try/catch/finally block give us the opportunity to see what really is happening.
Great, we’re done! Let’s fire ‘er up! Change your solution setting to make WebBagels.Host the startup project and hit F5
Uh oh!
[sigh] Well, we avoided it as long as we could, but like most things in .NET, WCF requires configuration.
Creating the Service Configuration
Go ahead and add an app.config file to your WebBagels.Host application.Initially, it should look something like this:
All of the configuration for WCF, both clients and services goes in the system.ServiceModel node. Instead of doing a bunch of typing to create all this, we’re going to copy the config file that Visual Studio created for us when it made our service library project. Open that app.config file and copy the system.ServiceModel node, and everything in it, to the app.config you created for your console hosting application, careful to place it IN the configuration element. You’re file should look something like this (note: the part that we are adding is boxed in red and I have removed the comments to make the file a little easier to read):
Let’s look at this file in a little more detail:
The Services Node
The services node contains service nodes, which in turn contain all the information WCF needs to host your service. Since you can host multiple services in a hosting application (by creating multiple instance of ServiceHost) your configuration file can have multiple service nodes in your services nodes:
The service node has a couple attributes that are important. The name attribute identifies the concrete implementation of our service, in this case the BagelOrderService class that implemnts our service contract in IBagelOrderService. That’s the value ServiceHost is going to use to find which section of the configuration file it’s going to use, so it needs to be the fully qualified class name of your service implementation.
The behaviorConfiguration attribute identifies a section of the configuration file that will contain information about what behaviors our service will use. I’ll cover behaviors later in this post.
Within our service node we have a few sections. The first is the host section:
This node holds some configuration information that is applied to all endpoints for your service. Right now we are using it to specify a base address, which will be pre-pended to the address we will define in our service endpoints. You can have base addresses for each type of transport. TCP base addresses begin with “net.tcp.” MSMS address begin with “net.msmq” and so on. You can only have one base address for each transport type AND if your transports communicate over a port, the port numbers, even across transports MUST be unique (more on that below).
Speaking of endpoints:
Our service has two endpoints; the service endpoint which clients use to invoke the service, and a metadata endpoint that returns a WSDL (Web Service Definition Language) document which in turn allows clients to discover and learn about the capabilities and requirements of the service.These, like all endpoints, boil down to three things. The ABC of the endpoint; Address, Binding and Contract.
The Address is the location of the service. It’s basically a URI that allows clients to locate the service on a network and invoke it’s actions or acquire it’s metadata. You can see that the address for our service is, uh, empty. But, if you remember, we defined a base address in the host section of our service. The WCF runtime will take the base address and append whatever address we put in the endpoint to it to create a complete address. In this case the address to invoke our service is the base address because we have left the address in our service endpoint blank. The address to access the WSDL document would be the base address plus “mex” (base address plus the value of the endpoints address attribute) This does NOT mean that you can just omit the address attribute. WCF doesn’t like that. So, you know, don’t do it. It’s also important to remember that addresses must be unique. You don’t like it when your mail gets delivered to your neighbors house. WCF REALLY doesn’t like it!
The Binding is the way the service communicates with the client. The fancy-pants way to describe this is as a “channel stack” which involves a lot of stuff we’ll cover in a later post. For now if someone says “channel stack” just remember that they are talking about the binding. The out-of-the box bindings that come with WCF in .NET 3/3.5 fall into a few families; HTTP, TCP, MSMQ and Named Pipes. Each of these have specialized sub-types for things like pier-to-pier and service bus communications. We’ll cover the which of these to use when in a later post. But for now since HTTP is probably the most widely interoperable transport in that list, we’ll stick with that. In fact, when in doubt you’ll want to use the wsHttpBinding that we are using here. In this case, the “ws” means that the binding supports the most common WS-* protocols. For all the SOAP geeks out there, it specifically implements WS-I 1.1 Basic Profile.
The Contract is the service contract that our service implements. In this case its the IBagelOrderService interface we created in part one. 99% of the time, this is a no-brainer; you have a service implementation that implements the service contract. In some cases you may want to have a couple different contracts for the same implementation. This allows you to segment your service and only expose certain methods on certain contracts, and other methods on other contracts. This works because although a service implementation may have methods of A, B and C, the contract may only define A and B. In this case clients using that endpoint would not be able to invoke C since it’s not part of the contract that endpoint supports.
The identity element deals with security, which we’re not using right now and will cover in a future post. You could leave it or take it out, the service (right now) will work the same either way.
The last section deals with behaviors:
Behaviors is a deep topic that we’ll discuss in a later post. For now, we’ll just focus on what we’re doing here.
As you’ll recall our service node had an attribute called "behaviorConfiguration” which had a value of “WebBagels.Service1Behavior.” This is the section of the configuration file that that attribute points to. In this case we’re telling the service to allow HTTP Get requests to get the services WSDL document. If you do not wish to make your WSDL public, simply set this value to false and comment out the metadata endpoint. We are also not including exception details when the service throws and exception. If you’ve done ANY ASP.NET development work, this concept should be very familiar; instead of throwing the actual exception information down to the client, a “sanitized” message is returned which hides your shame any information that could compromise the security of your application.
OK, let’s try it again…
Well, we’re getting closer…
One At A Time Please
Even though we may have set the solution to treat our host as the startup project, I bet you saw something like this when you hit F5:
Yep, thought so.
The addresses in WCF endpoints are made up of a path that includes the machine name and a port number. If you’ll recall when we copied the configuration file from the WCF service library, we didn’t change anything. So, when our host AND the WcfSvcHost both ran they tried to grab the same port. And when it comes to ports, there can be only one listener at a time.
There are two ways to fix this. The easiest is to simply change the port number of the address in one of the configuration files. Quick and easy, but then you have two instances of your service running. And this could cause some “confusing” results if you need to debug the service. Mostly because YOU WILL forget to make the corresponding change to one of your clients, which will continue to hit the “other” host. Happens all the time. At any rate, if you don’t need to have a second instance running, why run it?
In the Solution Explorer, right click on the WCF service library project and select “Properties.” When the properties page for the project comes up, you should see a tab at the bottom labeled “WCF Options.” You should see a page that looks something like this:
Turns out that one little checkbox is what’s causing all of our problems. Uncheck it, recompile and hit F5:
Success!
To verify that the service is actually running, we can navigate to the endpoint we defined in our app.config file. We should see the information page for our service:
Great! Now we have our very own hosted WCF service! Now we just need to,learn how to use it. In the next installment we’ll talk about consuming services on the client side.
Code on!
Print | posted on Wednesday, March 17, 2010 10:13 AM