This post was migrated from Justin’s personal blog, 'Codethinked.com.' Views, opinions, and colorful expressions should be taken in context, and do not necessarily represent those of Simple Thread (and were written under the influence of dangerous levels of caffeination).

I’m gonna go ahead and say it… I don’t think WCF is all that bad. In fact, I think it can be pretty easy! I hear lots of complaints about it, and many of them valid, but by far the biggest complaint I hear is that it is just so darn difficult. Especially cause of all that xml configuration. Holy crap I hate XML configuration! (If you want to complain some more, I’m on Twitter) And because it is perceived as difficult, it is often overlooked in favor of ASMX web services because they "just work".

While normally I would be the first in line to use a tool that "just works", in this case I think that WCF is carrying around baggage of its earlier incarnations. I want to show you that as of .NET 3.5, WCF can be just as easy for setting up web services as ASMX, and that you no longer need to fear it. Or fear the day that Microsoft deprecates ASMX web services.

In fact, we are going to setup WCF web services without any configuration! In order to get started, let’s go ahead and add a WCF web service to our project:

image

We should be able to now see our service sitting in our project:

image

You probably also see the interface that it created, we don’t need that right now, you can go ahead and delete that. I hear the purists getting their panties in a bunch right now, just calm down.

If we double click our service to open it up, we will see that we now have to remove the interface declaration on the service as well:

public class MyTestService : IMyTestService
{
    public void DoWork()
    {
    }
}

Once we remove it, change it so that it returns a value, and move our contract attributes which were previously on the interface, it now looks like this:

[ServiceContract]
public class MyTestService
{
    [OperationContract]
    public string DoWork()
    {
        return "Did Something!";
    }
}

Okay, so this is the part where we start putting in a bunch of xml configuration, right? Wrong. This is the part where our little friend ServiceHostFactory comes to the rescue. The first thing we need to do is write click on the .svc file and click "View Markup". Yeah, it is weird that you have to click "View Markup", but don’t look at me, I didn’t write it.

image

After you click "View Markup" you’ll be looking at the XML that binds the .svc file to the implementation of your service. It should look like this:

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

Now we can add one single attribute called "factory" and remove the Debug and CodeBehind attributes (unless you are running as a website and not a web project):

<%@ ServiceHost Language="C#"
    Service="MyTestWebsite.MyTestService" 
    Factory="System.ServiceModel.Activation.ServiceHostFactory" %>

Now if we hit the service, what do we get? We get the WCF service screen. We can click the link to view the WSDL or we can do what it says and use svcutil to generate a client.

image

To make it even more simple, you could move the code for the service out of the CodeBehind file and into the .svc file. Now you have an entire WCF service in a single file! Check it out:

<%@ ServiceHost Language="C#"
    Service="MyTestWebsite.MyTestService"
    Factory="System.ServiceModel.Activation.ServiceHostFactory" %>
using System.ServiceModel;

namespace MyTestWebsite
{
    [ServiceContract]
    public class MyTestService
    {
        [OperationContract]
        public string DoWork()
        {
            return "Did Something!";
        }
    }
}

Did you know that was even possible? Go ahead, check the service, it still works! And you know what else we can do? What if we want to make it a REST web service which can return JSON? We just have to do a few things.

First we need to change ServiceHostFactory to WebServiceHostFactory, then we just add a "WebGet" attribute to the operation contract. Next we have to change the result type to Stream so that we can use the JavaScriptSerializer instead of the stupid DataContractSerializer which requires a bunch of attributes on the serialized classes.

Now our service looks like this:

<%@ ServiceHost Language="C#"
    Service="MyTestWebsite.MyTestService"
    Factory="System.ServiceModel.Activation.WebServiceHostFactory" %>
using System.IO;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Web.Script.Serialization;

namespace MyTestWebsite
{
    [ServiceContract]
    public class MyTestService
    {
        [OperationContract]
        [WebGet]
        public Stream DoWork()
        {
            return WriteJson(new { MyValue = "Did Something!" });
        }

        private Stream WriteJson(object value)
        {
            var javaScriptSerializer = new JavaScriptSerializer();
            var json = 
                Encoding.UTF8.GetBytes(javaScriptSerializer.Serialize(value));
            var memoryStream = new MemoryStream(json);
            WebOperationContext.Current.OutgoingResponse.ContentType 
                = "application/json; charset=utf-8";
            return memoryStream;
        }
    }
}

And that is it, we just hit the url at MyTestService.svc/DoWork and we get a chunk of JSON returned:

image

Now we had to add a single method in order to allow us to write a raw JSON response, I hope you’ll agree that since you have the code to do it now, it really isn’t that hard!

Hopefully you found this post useful, and I hope that if you have overlooked WCF services because you hate all of the configuration (I know I did!), that you’ll give them a second look.

One last note, if you are using ASP.NET MVC then you already have a much easier method of doing JSON REST web services, so please don’t do this! πŸ™‚

20 Comments

Dave Ward

For me, it’s not so much that WCF’s current state is difficult as it’s tedious. Moreover, my real issue is that the tedium doesn’t bring any tangible benefits to the table in most of the situations I use ASMX for (I do use WCF currently; just not for simple AJAX callback endpoinst).

That extra work to set up the factory (per service) and add a JSON wrapper feels almost akin to setting up an HttpHandler to instantiate and render an ASPX Page, when you could just add a new ASPX page instead. Sure, you can do it, and it’s not even technically difficult, but why?

That said, this is a great tutorial on making the current iteration of WCF less of a pain to deal with. I wasn’t aware of that approach to using JavaScriptSeralizer with WCF (which helps with one of my major WCF grievances).

Reply
Javi

Justin,

From .net 3.5 sp1 there is POCO support.
You don’t need to mark your DTOs with DataContracts and DataMembers attributes. The convention is that all public properties with getters and setters will be serialized if u dont specify those attr

Javi

Reply
Justin Etheredge

@Dave I tend to agree with you, use whatever works for you and forget the rest. For me, I’ll use ASP.NET MVC for a REST endpoint before I’d ever touch WCF. I just wanted to show that WCF can be easy, and that for SOAP web services there are a *ton* of advantages to using WCF. I threw the JSON service in there because I wanted to show that you don’t necessarily have to split your stack between asmx/wcf if you just want simple JSON web services.

On a side note, while I was doing a bit of research for this article, I was somewhere that they had deprecated the JavaScriptSerializer in the beta of .NET 3.5 because of the DataContractJsonSerializer, aren’t you glad they reversed that decision!? πŸ™‚

Reply
Justin Etheredge

@Chris Your link doesn’t seem to be working right now, so I can’t see what it is, so I’m assuming that you are using the normal WebGet config for JSON. And yep, it is easy, but you have to use the DataContractJsonSerializer which forces me to declare a class and mark it up with attributes. I much prefer to use the JavaScriptSerializer, but YMMV.

Reply
Milan Negovan

*cough* A URI like MyTestService.svc/DoWork doesn’t fit the REST philosophy. It should address a resource, not an action.

Incidentally, this is exactly why I believe WCF is the wrong tool for building REST APIs. You have to wrestle with the WS-* stack and bend WCF to something it wasn’t intended to handle.

Reply
Justin Etheredge

@Milan Completely agree. And I would never use WCF for REST stuff unless I had an ASP.NET app which was already built around WCF. There are many much better solutions for REST services that don’t involve WCF.

Reply
Josh

I just want SOMETHING to work. I’m just trying to return plain JSON from a web service, so I can format my own string… no "d" rubbish etc.

I thought your post was the answer. But just errors after errors…. (my fault of course!)

I got up to the bit where you say "Now if we hit the service…", and…

First error is "The contract name ‘TestService.IService2’ could not be found in the list of contracts implemented by the service ‘Service2’."

Then I removed the web.config stuff referring to it (which was inserted into web.config when I created the WCF service), and got "Service ‘TestService.Service2’ has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element."

Could you please tell me what I’m doing wrong?

Reply
Justin Etheredge

@Josh WCF can be really frustrating, the code in the article is pretty non-standard. I would recommend you start from scratch and go step by step. The error you are seeing at the end of the comment is because you haven’t defined any endpoints in your configuration, but the code in this article is all about setting up a service without configuration, which means that you aren’t using the same "factory" approach.

Reply
Dave Ward

@Josh: Unless you’re being forced to use WCF, a simple MVC action or HttpHandler is much easier than even the most simplified WCF approach. Also keep in mind that the .d is there for a reason. Avoiding unwrapped JSON arrays does legitimately defend against a certain class of cross-site attacks.

Reply
Dean

We have a ruby team and I use json serialisation from an ashx handler for any read data it’s fast quick and simple plus it’s nice to show off a bit πŸ™‚ ps it’s under system web extensions if I remember correctly and you need 3.5 sp1 or something like that

Reply
Ciaran Bruen

Hi Justin, good article I have to say…wish I found it sooner it would have saved me a lot of headaches. I was all set to use WCF, jQuery, and JSON in my current app until I found out the the production servers only allow netTcpBinding. I can’t for the life of me get JSON returning from a netTcpBinding, nor can I find a definitive answer if it’s possible or not. So maybe you know – can netTcpBinding return JSON?

Reply
codeulike

Thanks, this was handy.

Is there any way to set the maxReceivedMessageSize and related attributes with this method? Or do we have to go back to angle-brackets for that?

Reply
Keith Cromm

OK, this solution *does* require a configuration file xml entry, that being the web.config file which contains the system.serviceModel and serviceBehavior nodes.

Delete these notes (for that given WCF) and you get the *holy carp* [yeah, I’m picking on the fish…] .NET error as follows:

Service ‘MyTestWebsite.MyTestService’ has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.

Thus, the solution is *not* “… an entire WCF service in a single file”. Correct me if I’m incorrect, though.

Reply
Dan Clarke

“I hear the purists getting their panties in a bunch right now, just calm down.”

This literally made me burst out laughing. Excellent blog post – but this was the best bit πŸ™‚

Reply

Leave a Reply

Your email address will not be published. Required fields are marked *