Creating a WCF Service for JSON

Saturday I gave a presentation for the first time at the Southern California Code Camp on how to use jQuery to perform AJAX operations against a WCF Endpoint. Today I want to review how to create a WCF service that supports JSON and how to properly create messages that can be serialized to and from JSON.

The first step is to add a WCF Service to a web site, typically this will be your actual web site but it does not have to be. Your architecture will vary by application requirements and network security rules. Even behind the firewall you may have cross domain scripting restrictions, etc. Adding a WCF Service to the site in Visual Studio gives us a .svc file, but it also creates content we need to remove.

Visual Studio 2008 and .NET 3.5

Since Visual Studio 2010 is just around the corner I want to try and break the two experiences apart. I have a follow up post using Visual Studio 2010 planned in a few days. Select 'WCF Service' from the Add new Item dialog. This adds the .svc file to the web site, but it also adds two files and some unneeded sections to the web.config file as well.

First the files, a code file (.vb or .cs) is added to the project that defines the interface implemented by the service. It also includes some minimal attributes to tell WCF how to use the service. A code-behind file is also added to the .svc that implements the service. Both of these files can be used as is, however I like to remove these files. Instead I prefer to create a class library containing the code for both the interface and class implementation. In the long run this is a much cleaner way to manage the code for your WCF services. First it makes it easier to create unit tests for the code, which I will cover in another post. Second it gives you a certain flexibility to add another provider that implements the service interface. As you will see this does not make life any harder for us, it just requires the service's web site to reference the class library.

The web.config file has also become polluted because unneeded behaviors, services and endpoint sections have been added to the system.serviceModel section. The following is example configuration that was added, it can simply be removed from the file.

<behaviors>
  <serviceBehaviors>
    <behavior name="Web.TestsvcBehavior">
      <serviceMetadata httpGetEnabled="true" />
      <serviceDebug includeExceptionDetailInFaults="false" />
    </behavior>
  </serviceBehaviors>
</behaviors>
<services>
  <service behaviorConfiguration="Web.TestsvcBehavior" name="Web.Testsvc">
    <endpoint address="" binding="wsHttpBinding" contract="Web.ITestsvc">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>
<bindings>
  <basicHttpBinding>
    <binding name="basicHttp" maxReceivedMessageSize="2147483647">
      <security mode="None"/>
    </binding>
  </basicHttpBinding>
</bindings>

Ultimately this comes down to the configuration of the services and I am not bashful to admit I know very little about WCF configuration. I have people for that . So here is where I am going to punt and say just trust me this works great.

The only thing left is to update the actual .svc file to use the correct service implementation and factory. This is an example of the directive in the default .svc file.

<%@ ServiceHost Language="C#" Debug="true" Service="Web.Testsvc" CodeBehind="Testsvc.svc.cs" %>

The following is an updated version of the file that points the Service to the service implementation class. It also tells the service to use the WebScriptServiceHostFactory, which tells WCF to use web protocols and adds the JSON serialization we need to make our service work with jQuery AJAX.

<%@ ServiceHost Language="C#"
    Service="WCFjQuery.ContactBLL.Implementation.TestSvc"
    Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>

The Class Library

The next step is to add a class library to actually implement the service if you do not already have one. I like to organize the files into folders describing their purpose. You can organize how you would like, it is all a matter of personal productivity. I like to have a Contracts folder that contains the service interfaces, I use the plural here because often you will have more than one WCF service in your application.

The WCF attributes depend on a reference to the System.ServiceModel dll, so you should go ahead and add it up front, unless you are using a tool like R# that will add it for you when you add the first WCF attribute. The first file to add is the service interface. The example I use in my presentation is a simple contact form, so the service is called ContactUs. The corresponding interface is IContactUs.cs.

If you remember the attributes included with the interface file you deleted in the web site, the class was adorned with a ServiceContract attribute. This time we will define the full namespace of the interface, the name of the service, SessionMode and ProtectionLevel. For our purposes the following work for my service.

[ServiceContract(Namespace = "urn:WCFjQuery.ContactBLL.Contracts",
    Name = "ContactUs",
    SessionMode = SessionMode.NotAllowed,
    ProtectionLevel = ProtectionLevel.None)]

Now we can add the members of the service interface. For the sample application there are 4 methods; PostNewContact, GetContact, GetActiveContacts and BadMethod. Each method has a definition like the following:

[OperationContract(IsTerminating = false,
    IsInitiating = true,
    IsOneWay = false,
    AsyncPattern = false,
    Action = "PostNewContact",
    ProtectionLevel = ProtectionLevel.None)]
PostNewContactResponse PostNewContact(PostNewContactRequest request);

 

Now we can add the class to implement the service. I place this file in the Implementation folder because this file implements the service. We will add some more files here in a second. The ContactUs.cs file contains this file. The ContactUs class implements the IContactUs interface, it also has a ServiceBehavior attribute adorning it. In this demo the IncludeExceptionDetailInFaults property is set to true. This means exception messages will be bubbled through the service using the proper serialization for the client request. In this case it means our jQuery will get a JSON data object it can parse for us.

The class members don't require any attributes. You can quickly auto implement those by either using the SmartTag on the interface name or with a tool like R# or CodeRush.

At this point you should have a bunch of references to classes that don't exist yet. We need to create them. They are also added to the Implementation folder. This is where things get highly customized to your particular needs. By that I mean each one of these classes is a message class. It contains the body of the message, which typically is the contents of the request or the response.

Before I loose you here, this is something that took me a bit to comprehend at first. WCF and SOA architecture is about messaging and passing objects around. Typically each service method accepts a request object and returns a response object. In the case of GetActiveContacts it does not accept any request object.

I always like to get a response object back to check on the client. This is where I differ from some who focus on the WCF side of the application. The first situation I found myself wanting this was on a method that added a new object to the data store. The author of the service method did not return any confirmation value, or any value at all, just a null value. His reasoning was if it did not work it would have thrown an exception. I think this is a bad practice for any code, you should not rely on just bubbling an exception to the client. Instead you should catch the error and return something meaningful to the client. You can read more about that in Brad Abrams book.

Lets get back to the code at hand by examining the request object for retrieving a specific contact record. The service method requires a ContactId, which is the business key for a contact object.

Another side-bar here, notice I am not referring to a contact record, this is because modern application are querying a data model and not the database anymore. So think Entity Framework, oData or nHibernate. An actual contact object may be composed of values contained in several tables such as person, address, phone, email etc.

The GetContactRequest has to be serialized by the WCF engine, thus we need to add a a DataContact attribute to the class and DataMember attribute for each member. In this attribute the name of the member is defined. It could be anything we want, just remember it is the name you need to reference on the client. Next we need to specify this value is required, otherwise we would make the request without specifying the record we want. Finally the Order is set to 0. This tells the serilizer to add this value first or to the 0 index of the serialized object.

using System.Runtime.Serialization;

namespace WCFjQuery.ContactBLL.Implementation {

[DataContract(Namespace = "urn:WCFjQuery.ContactBLL.Implementation",
Name = "GetContactRequest")]


    public class GetContactRequest {

        [DataMember(Name = "ContactId", IsRequired = true, Order = 0)]
        public int ContactId { get; set; }

    }
}

On to the response object; GetContactResponse. Again you need to reference the Serialization namespace and add the attributes. This class also only includes a single object; a ContactInfo class.

using System.Runtime.Serialization;

namespace WCFjQuery.ContactBLL.Implementation {

    [DataContract(Namespace = "urn:WCFjQuery.ContactBLL.Implementation",
Name = "GetActiveContactsRequest")]
    public class GetContactResponse {

        [DataMember(Name = "ContactInfo", IsRequired = true, Order = 0)]
        public ContactInfo contactInfo { get; set; }

    }
}

The ContactInfo class also needs to be serialized. So again you need to add attributes to manage this process. The ContactInfo class contains more than one member this time, some required, some not required. This time you will notice the index of the value increasing from 1 to 11.

using System.Runtime.Serialization;

namespace WCFjQuery.ContactBLL.Implementation {

    [DataContract(Namespace = "urn:WCFjQuery.ContactBLL.Implementation",
    Name = "ContactInfo")]
    public class ContactInfo {

        [DataMember(Name = "ContactId", IsRequired = false, Order = 0)]
        public int ContactId { get; set; }

        [DataMember(Name = "FirstName", IsRequired = true, Order = 1)]
        public string FirstName { get; set; }

    ...

        [DataMember(Name = "Coment", IsRequired = true, Order = 11)]
        public string Comment { get; set; }

    }
}

The process for the remaining request and response objects is the same. Remember any objects that are being passed in or out need to have serialization attributes defined. You are not required to add your contact or implementation classes to a class library, but as you will see in future posts it makes it much easier to test your code.

At this point we have a fully functioning WCF web service that will naturally accept and return JSON objects that can be used on the client. So in the next post I will review the infrastructure and patterns for making AJAX calls using JSON to a WCF service.

Share This Article With Your Friends!

We use cookies to give you the best experience possible. By continuing, we'll assume you're cool with our cookie policy.

Install Love2Dev for quick, easy access from your homescreen or start menu.

Googles Ads Bing Pixel LinkedIn Pixel