WCF Services
and Ajax Calls - Part 1

Feb 17

Introduction

Sitefinity uses WCF as its technology for it's RESTful web services. But how do you create one to call your own custom data. Most people would argue it's much easier to use a MVC widget or WebApi directly. WCF is just too hard and heavy. Or is it?

There are lots of options when creating back-end services\events from the ASP.NET Forms post back, MVC Widget or WebApi by its self. Even the old asmx service. There is also WCF. But surely WCF is too heavy, too old. No way!, you say.

Sitefinity uses WCF as its service communication. Thunder has an option to create a Sitefinity service which produces one. But I have never seen anyone actually promoting its use. Why not? Oh yeah, Too heavy, Too old, WebApi is the bee’s knee’s dude!

I don’t disagree but I think there are some valid reasons you should choose it as a first option rather than “only if you make me” option. First and foremost it is supported by Sitefinity. All their services are written in it. So if I write my services using their WCF standard and they do an update to improve the implementation then my services also get the improvement which is a good scenario.

Some of you nosy folk may pipe up and say, “Actually I think they may move away from WCF as they are now introducing Service Stack. That may be true but I wonder if that may be changed since Service Stack started charging big bucks for its usage. (Well big enough bucks for me to decide to drop its use)

Anyway, for those that are interested here is how I implement a Sitefinity WCF service so I can make Ajax calls from my web pages.

I start with Thunder and create my Service. You will notice it also gives you a hint of some built in goodness. The ability around caching and security. Another good reason to implement a Sitefinity WCF Service.

ServiceUtility.DisableCache();
//ServiceUtility.RequestAuthentication(false, false);

For a GET request I copy the Test ServiceContract as they have it.

[WebHelp(Comment = "Tests the connection to the service. Result is returned in JSON format.")]
[WebGet(UriTemplate = "/TestConnection/", ResponseFormat = WebMessageFormat.Json)]
[OperationContract]

For a POST you need to use the WebInvoke. Notice that I have also specified the ResponseFormat and the BodyStyle. These options are important as we need to be specific about the format for our jQuery ajax call to work later on.

[WebHelp(Comment = "Result is returned in JSON format.")]
[WebInvoke(Method = "POST"
UriTemplate = "/SubmitContactForm"
RequestFormat = WebMessageFormat.Json, 
ResponseFormat = WebMessageFormat.Json, 
BodyStyle = WebMessageBodyStyle.Bare)] [OperationContract] ContactFormResponse SubmitContactForm(ContactFormRequest form);

Unless it’s a single parameter I always use a Request and a Response object model. It's much cleaner especially when it comes to validation.

We return the response as a JSON. But did you know. That though the package is significantly smaller the time to generate the JSON is longer in a WCF service. But I am happy sacrificing the CPU for smaller network packets myself.

SOAP transmits 20-40% more data than JSON, but it is (in WCF) faster approximately 20-25% than JSON. How this is possible? The reason is in WCF-JSON implementation itself. WCF internally serializes JSON streams to some kind of XML-Infoset wire format that requires about 50% more bytes than DataContractSerializer.

First, my response model. I have an inherited model class of ServiceReponse. This is my standard return data. If the particular call needs to return other data then it is added in the abstraction.

[DataContract]
public class ServiceResponse
{
    [DataMember]
    public String Status { getset; }
    [DataMember]
    public String Message { getset; }
}
[DataContract]
[KnownType(typeof(ServiceResponse))]
public class ContactFormResponse
{
    public ContactFormResponse()
    {
        Response = new ServiceResponse();
    }
 
    [DataMember]
    public ServiceResponse Response { getset; }
 
    // If I wanted to return data
    // then I would add those properties here
}

Notice I have added the KnownType attribute. This is required to ensure correct serialization of the whole model into our JSON response.

I also new up the ServiceResponse in the constructor so I don't have to do it every time I create a new response.

And just for completeness here is my Request.

[DataContract]
public class ContactFormRequest
{
    [DataMember]
    public String Name { getset; }
    [DataMember]
    public String Email { getset; }
    [DataMember]
    public String Message { getset; }
}

Finally we have to register our service.

We use a Virtual Path to do this. This means we don't need a physical .svc file. You can read about Virtual Paths in Sitefinity at this post.The service code is all in a separate class library. I have named this Pdr.MyService. Open the AssemblyInfo class file and add...

[assemblyPreApplicationStartMethod(typeof(Pdr.MyService.Installer), "PreApplicationStart")]

You can read more about this PreApplicationStart Method at this blog post.

In my class I have an Installer class which then registers my virtual path and my new service. Thus to install my service I just need to drop my class library dll into the bin folder of my Sitefinity project and its registered and all ready to go.

public class Installer
{
    public static void PreApplicationStart()
    {
        Bootstrapper.Initialized += (new EventHandler<ExecutedEventArgs>(Installer.Bootstrapper_Initialized));
    }
 
    private static void Bootstrapper_Initialized(object sender, ExecutedEventArgs e)
    {
        if (e.CommandName != "RegisterRoutes" || !Bootstrapper.IsDataInitialized)
        {
            return;
        }
 
        InstallVirtualPaths();
 
        SystemManager.RegisterWebService(typeof(Pdr.MyService.Services.ContactForm), "PdrMyService/Services/ContactForm.svc");
    }
 
    private static void InstallVirtualPaths()
    {
        SiteInitializer initializer = SiteInitializer.GetInitializer();
        var virtualPathConfig = initializer.Context.GetConfig<VirtualPathSettingsConfig>();
        var myControlsViewConfig = new VirtualPathElement(virtualPathConfig.VirtualPaths)
        {
            VirtualPath = ControlsVirtualPath + "*",
            ResolverName = "EmbeddedResourceResolver",
            ResourceLocation = ControlsAssemblyName
        };
        if (!virtualPathConfig.VirtualPaths.ContainsKey(ControlsVirtualPath + "*"))
        {
            virtualPathConfig.VirtualPaths.Add(myControlsViewConfig);
            Config.GetManager().SaveSection(virtualPathConfig);
        }
    }
}

So that's the WCF service created. I now need to apply my validation. I'll talk about that in my next post and following that will be a post about calling the service from the client using JQuery.


Darrin Robertson - Sitefinity Developer

Thanks for reading and feel free to comment - Darrin Robertson

If I was really helpful and you would buy me a coffee if you could, yay! You can.


Leave a comment
Load more comments

Make a Comment

recapcha code