Easy as ABC

You will see a lot of introductions and samples out there about WCF and they will be pretty complicated if you are a beginning programmer. You will see things like the "ABC's" of WCF:
  • A = Address - Where is the service? What is the URI (URL) and what protocol (HTTP, etc.).
  • B = Binding - How will the message be transmitted? SOAP? WSI compliant? Encrypted by WCF?
  • C = Contract - What data is sent to and from the service and what operations are supported?

For the vast majority of you "A" is very simple - this will be the URL of a .svc file on your IIS web server. You will use HTTP. "B" is pretty simple as well - for the most part you will use "BasicHttpBinding" and will stick with defaults unless you blow out some of the default quotas and need to increase those. So the "C" is where things get interesting and really constitutes what you need to know.

Contractual obligations

So what is a contract in WCF? A contract in this context is an agreed-upon set of information that (typically) does not change. There are 2 types of contracts we will focus on in WCF: Data Contracts and Operation Contracts.

Data Contracts

Data Contracts are, as the name indicates, the agreed-upon set of information making up your data. For example, if you are going to send data for a "customer" you might have items such as first name, last name, e-mail address, street address, phone number, etc. Now what is interesting abut data contracts is that they are very closely tied to XML so when you define your contract you are really defining an XML schema. In the case of a customer, you can actually have objects for some of those properties with additional values. Your end result customer schema could look something like this:

<Customer>
  <Name>
    <First/>
    <Last/>
    <Display/>
  </Name>
  <EmailAddress/>
  <Address>
    <Number/>
    <Street/>
    <Stree2/>
    <City/>
    <State/>
    <Zip/>
  </Address>
  <PhoneNumber>
    <Prefix/>
    <Number/>
  </PhoneNumber>
</Customer>


In this example, the Customer contract includes 3 other contracts - Name, Address, and PhoneNumber. This is a very important consideration when building your schema. One simple approach is to model your schema after you database (if you have one) so your object model and your data contract and your database schema all agree. While this model works in simple scenarios, more often than not it requires a little more thought. Let's expand our discussion now to include an order object. If you work for the company filling the order, it may be important while accessing an order to be able to get the customer associated with it so you might consider including the customer in your order contract. If you are a customer ordering multiple items from a company, you may want to get all of your orders from the company with your customer record so you would want all of your orders as paort of your customer contract which would then include the customer record and hopefully you can see the problem there. In that case you would probably want to include only the customer ID in the order contract. In fact, I would also argue that you would NOT want to include the orders in the customer contract anyway. The contract should include data that defines that object. In the case of the customer, it is not defined by the orders he or she has made (although you could probably make some valid assumptions of their persona based on that), it is only defined by the data pertinent directly to the customer.

That does not mean that it is not necessary to have a contract that includes a customer and multiple orders. Just like we included contracts for name, address, and phone number in our customer, we could create a new contract called "CustomerOrders" which included the customer and the associated orders or...

Operation Contracts

Operation contracts (once again as the name indicates) define what operations a service can perform. For example, in the example of customers and orders above you might want to retrieve a customer, place an order, etc. This picks up where we left off above. In order to get a list of orders for a customer, you could simply define an operation which takes a customer ID and returns a collection of orders. You would already have the pertinent customer information and then would be able get a list of orders that consist of only order data contracts and is specific to that customer.

Get on with it!

Now that we understand contracts, what kind of contracts do we need for "Hello World!"? Once again we are going to make some additional assumptions. We are going to assume that if we want to display "Hello World!", then we probably want to be able to display other messages and that those messages have some information that is pertinent and specific to any message. Based on these assumptions we come up with the following XML schema:

<Message>
  <Name/>
  <Text/>
  <DateCreated/>
  <DateModified/>
  <DateExpires/>
</Message>


Now let's add a Message class to the our HelloRealWorld.Common library that looks like this:

    public class Message
    {
        public string Name
        { get; set; }

        public string Text
        { get; set; }

        public DateTime? DateCreated
        { get; set; }

        public DateTime? DateModified
        { get; set; }

        public DateTime? DateExpires
        { get; set; }

        public Message()
        {
        }
    }


Note that we used the nullable generic for the dates as indicated by "DateTime?". The question mark indicates that this is a nullable type meaning that you can either assign null or a DateTime value to each of these properties and check the HasValue property to determine if the underlying value is null and then use the Value property to get the date if it has been assigned. The bottom line is that it allows us to support nulls in our object which is a good thing because they are allowed by default in our XML schema and most database systems. So how do we make this a WCF data contract? There are 2 steps to this process. First we need to add a reference to System.Runtime.Serialization to our common library. Then we need to add the DataContract attribute to our class and the DataMember attribute to our properties. In addition, we will add a unique namespace to the DataContract attribute to make sure it does not get confused with other contracts for messages from other services. Don't forget to use Alt-Shift-F10 to add the using statement for System.Runtime.Serialization. The final result should look like this:

using System;
using System.Runtime.Serialization;

namespace HelloRealWorld.Common
{
    [DataContract(Namespace = "http://hellorealworld.codeplex.com/")]
    public class Message
    {
        [DataMember]
        public string Name
        { get; set; }

        [DataMember]
        public string Text
        { get; set; }

        [DataMember]
        public DateTime? DateCreated
        { get; set; }

        [DataMember]
        public DateTime? DateModified
        { get; set; }

        [DataMember]
        public DateTime? DateExpires
        { get; set; }

        public Message()
        {
        }
    }
}

At your service

Next we need to write a service to provide this message. Once again let's start with the Visual Studio approach. Simply add a new WCF service to our web application project we created in Hello ASP.Net World, but instead of Service1.svc let's call our service MessageService.svc. So what happened? Four things:
  • MessageService.svc was added
  • MessageService.svc.cs was added
  • IMessageService.cs was added
  • Some stuff was added to web.config
So let's take a look at what was added to web.config:
    <system.serviceModel>
        <behaviors>
            <serviceBehaviors>
                <behavior name="HelloRealWorld.WebApplication.MessageServiceBehavior">
                    <serviceMetadata httpGetEnabled="true" />
                    <serviceDebug includeExceptionDetailInFaults="false" />
                </behavior>
            </serviceBehaviors>
        </behaviors>
        <services>
            <service behaviorConfiguration="HelloRealWorld.WebApplication.MessageServiceBehavior"
                name="HelloRealWorld.WebApplication.MessageService">
                <endpoint address="" binding="wsHttpBinding" contract="HelloRealWorld.WebApplication.IMessageService">
                    <identity>
                        <dns value="localhost" />
                    </identity>
                </endpoint>
                <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
            </service>
        </services>
    </system.serviceModel>

Whether you have created web services befoe or not, that probably looks like a lot of information.

Last edited Jul 24, 2009 at 2:05 AM by douglampe, version 5

Comments

No comments yet.