Monster Bits of WCF and Itty Bitty Bits of MVC

I’ve had the pleasure of working with WCF on three specific projects that have brought me to this blog entry.  I haven’t used WCF on only three projects, there are just three that have brought me to write this entry.  I’ve used WCF a lot, since back when it was a beta.  WCF is great when creating SOAP services and you aren’t too worried about the extra overhead.  WCF is great for what it does, for the ideas behind what it does.

But writing RESTful web services doesn’t seem to be its strong point.  On two huge projects WCF has basically been dropped, or so scaled back one really can’t honestly say that WCF is used, and either an alternate framework has been used or a LOT of custom code ends up being written.

The first time I used WCF to implement RESTful service was at Webtrends.  Albeit, there is a single service that returns all types of awesome reporting goodness, however to implement basic auth, logging, polling, and a whole host of other Enterprise Scale needs we had to custom roll most of it.  Keep in mind, when doing this the WCF REST capabilities were brand shiny and new, so there were a few issues to work out.  Now, maybe WCF could be used and a lot of it would be built in.  However as it was, we easily spent 60% of the time writing custom bits because WCF just didn’t have the right options with the right bindings.

But I digress, I recently implemented an architecture using RESTful services using WCF.  But now I’ve come to find myself dropping WCF because of the back and forth and going with ASP.NET MVC controller actions to return JSON instead.  With that, here’s to the lean mean controller actions rockin’ the JSON.  Here’s what I’ve done to port everything from WCF to MVC.

To see what I had done, except on a smaller scale, check out my previous blog entry on ASP.NET MVC with a WCF project smack in the middle of it.  This will give you an idea of what I was using the WCF services for, merely to provide JSON results via RESTful services to an ASP.NET MVC front end requesting data with jQuery.

This is how I’ve setup the controller to return JSON results via an action.

First start a new ASP.NET MVC Project and add a new controller.  Cleanup the controller so that you have the following in the controller.

[sourcecode language=”csharp”]
using System.Web.Mvc;

namespace RestWebServicesWithMvc.Controllers
{
public class ServicesController : Controller
{

}
}
[/sourcecode]

Now create a testing project to create your test first.  Remember to add the reference to the ASP.NET MVC project.  From here we can create the first test.

[sourcecode language=”csharp”]
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RestWebServicesWithMvc.Controllers;

namespace RestWebServicesWithMvc.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
var controller = new ServicesController();
var result = controller.GetBiz();
Assert.IsNotNull(result);
}
}
}
[/sourcecode]

Now fill out the basic skeleton of the action in the controller.

[sourcecode language=”csharp”]
using System;
using System.Web.Mvc;

namespace RestWebServicesWithMvc.Controllers
{
public class ServicesController : Controller
{
public ActionResult GetBiz()
{
throw new NotImplementedException();
}
}
}
[/sourcecode]

Now we should have a good red running on our test.  Let’s create a business model class to return as our result next.

[sourcecode language=”csharp”]
namespace RestWebServicesWithMvc.Models
{
public class BizEntity
{
public string BizName { get; set; }
public string StartupDate { get; set; }
public int SalesThisMonth { get; set; }
}
}
[/sourcecode]

Now let’s return that object with some fake data.  First add [AcceptVerbs(HttpVerbs.Post)] to the action in the controller.  Then return a serializable object to the actual method as shown.

[sourcecode language=”csharp”]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult GetBiz()
{
return Json(
new BizEntity()
{
BizName = "Adron’s Code Workingz",
SalesThisMonth = 3429,
StartupDate = DateTime.Now.AddYears(-5).ToString()
}
);
}
[/sourcecode]

This is a quick starter.  There are a few dozen other options around this capability including other verb usage.  For many, this is all you need for your services, especially if their primary purpose is to communicate with a specific website and one doesn’t want the overhead of WCF.

Shout it

Aggregated Web Services Pt II – Tying it Together

I’ve been working through a project recently that I ended up creating an interesting abstraction of an assembly/classes between multiple web services projects.  I wrote about it initially in Aggregated Web Services Pt I.  In this blog entry is going to cover a few things, based on a full end-to-end implementation of a project from the WCF RESTful Web Services, to the ASP.NET MVC Site, and finally the jQuery calling those same services.

Simple Architecture
Simple Architecture

Before I got started, there is one thing I need to point out.  The communication with Javascript/jQuery/AJAX has a lot of tricky bits one needs to be aware of.  One of those is the same site origin and of course the famous cross domain solution issues.  That is why in this walk through I will place the web services and the site pages in the same project, yes, WCF and MVC living happily in a single project.  🙂  You can of course, if your architecture requires it, break these into separate projects, but for this example I’ll have both in the same project.

First create a new solution.  I always like to start with a new solution because it keeps the naming structured right, just from the practice.

(Click on any of the images to see a larger full size copy)

New Solution
New Solution

Once you create all of that then add an ASP.NET MVC 2 Project.

Adding the ASP.NET MVC 2 Project
Adding the ASP.NET MVC 2 Project

You can create an ASP.NET MVC 2 Unit Test Project if you want to, but I’m skipping it for now.  (yes, I’m still a big TDD advocate, but just setting up a prototype for this example)

Next I wiped out some files I don’t use, and suggest you zap em’ too.

Get Rid of the Nasty MS AJAX
Get Rid of the Nasty MS AJAX

Setting up the WCF Parts

Now that we’ve cleaned up those nasty bits, we’ll add our basic model we’ll be using.

[sourcecode language=”csharp”]
using System;

namespace EndToEnd.Mvc.Models
{
public class Investor
{
public string Id { get; set; }
public string Text { get; set; }
public decimal Money { get; set; }
public DateTime Stamp { get; set; }
}
}
[/sourcecode]

Now add a interface for the RESTful services to the root of the MVC Project as shown below.

[sourcecode language=”csharp”]
using System.Collections.Generic;
using System.ServiceModel;
using System.ServiceModel.Web;
using EndToEnd.Mvc.Models;

namespace EndToEnd.Mvc
{
[ServiceContract]
public interface IEndToEndService
{
[OperationContract]
[WebGet(UriTemplate = "Investors/{pageStart}/{pageEnd}", ResponseFormat = WebMessageFormat.Json)]
List<Investor> GetIncidents( string pageStart, string pageEnd);

[OperationContract]
[WebGet(UriTemplate = "Investor/", ResponseFormat = WebMessageFormat.Json)]
Investor GetInvestor();
}
}
[/sourcecode]

Now add the following abstract base class at the root level also.

[sourcecode language=”csharp”]
using System;
using System.Collections.Generic;
using EndToEnd.Mvc.Models;

namespace EndToEnd.Mvc
{
public abstract class InvestorBase : IEndToEndService
{
#region IEndToEndService Members

public List<Investor> GetIncidents(string pageStart, string pageEnd)
{
return new List<Investor>
{
new Investor
{
Id = Guid.NewGuid().ToString(),
Money = (decimal) (DateTime.Now.Second*2.27),
Stamp = DateTime.Now,
Text = "Lorum ipsum 1"
},
new Investor
{
Id = Guid.NewGuid().ToString(),
Money = (decimal) (DateTime.Now.Second*1.32),
Stamp = DateTime.Now,
Text = "Lorum ipsum 2"
}
};
}

public Investor GetInvestor()
{
return new Investor
{
Id = Guid.NewGuid().ToString(),
Money = (decimal) (DateTime.Now.Second*1.27),
Stamp = DateTime.Now,
Text = "Lorum ipsum"
};
}

#endregion
}
}
[/sourcecode]

Now add a WCF Service file and remove the interface file.  Then change the WCF class itself as shown below.  The reasons for the abstract class, inheriting from the interface, is that it removes any manipulation being needed with the actual *.svc file.  It just seems, at least to me, a little bit cleaner this way.

[sourcecode language=”csharp”]
namespace EndToEnd.Mvc
{
public class EndToEndService : InvestorBase
{}
}
[/sourcecode]

For the last touches for the WCF RESTful Service we need to setup the Web.Config file.  I’ve added the section toward the bottom of the config file in the <System.ServiceModel> section.  I’ve included the full config file below, so you can easily just copy and paste it if you’re working through step by step with me.

[sourcecode language=”html”]
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0">
<assemblies>
<add assembly="System.Web.Abstractions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</assemblies>
</compilation>
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" />
</authentication>
<pages>
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Routing" />
</namespaces>
</pages>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>

<system.serviceModel>
<behaviors>
<endpointBehaviors>
<behavior name="httpBehavior">
<webHttp />
</behavior>
</endpointBehaviors>
<serviceBehaviors>
<behavior name="ServicesBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
<services>
<service behaviorConfiguration="ServicesBehavior"
name="EndToEnd.Mvc.EndToEndService">
<endpoint address="" behaviorConfiguration="httpBehavior" binding="webHttpBinding"
contract="EndToEnd.Mvc.IEndToEndService" />
</service>
</services>
</system.serviceModel>

</configuration>
[/sourcecode]

One of the things I always do at this point is to setup the project properties.  I do this for a number of reasons, primarily to assure that the port number doesn’t go and change itself on me.  The other thing I set is the default startup page.  With ASP.NET MVC things get out of sync with Visual Studio, and Visual Studio tries to startup actual *.aspx files.  So what I do is just set the startup to an empty root startup.  These settings are shown below.

Project Properties
Project Properties

Setting up the MVC Parts

First add a home directory, a HomeController.cs, and then add a Core.Master master page to the project.

MVC Project Parts
MVC Project Parts

Next setup the Core.Master file with the following content sections.

[sourcecode language=”html”]
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;

<html xmlns="http://www.w3.org/1999/xhtml&quot; >
<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>

<script type="text/javascript" src="../../Scripts/jquery-1.4.1.js"></script>

<asp:ContentPlaceHolder ID=HeaderContent runat=server>
</asp:ContentPlaceHolder>

</head>
<body>
<div>
<asp:ContentPlaceHolder ID="MainContent" runat="server">
</asp:ContentPlaceHolder>
</div>
</body>
</html>
[/sourcecode]

One of the things I’ll point out is that with Visual Studio 2010 you get Intellisense with jQuery.  The reason I don’t use the x.x.min.js version of the jQuery is that it doesn’t have the appropriate setup to provide the Intellisense.  So be sure for development to use the fully expanded version and you can go to the zipped min version when you go live in production.  Another thing I do, which may vary on how you want to develop, is use the hosted jQuery on Google or Microsoft.  I did a write up previously for using the hosted jQuery so check it out for reference locations.

In the controller add the following code.

[sourcecode language=”csharp”]
using System.Web.Mvc;

namespace EndToEnd.Mvc.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
}
}
[/sourcecode]

Now that we have the Site.Master and the home controller, create an Index.aspx View in the Home folder of the project.

Adding the Index.aspx View
Adding the Index.aspx View

In the view add the following code for the jQuery calls to the services layer.

[sourcecode language=”html”]
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Core.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
jQuery AJAX Calls to RESTful WCF Web Services
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderID="HeaderContent" runat="server">
<script type="text/javascript">
var domainRoot = "http://localhost:1000/EndToEndService.svc/&quot;;
var investorUri = domainRoot + "investor/";
var investorsUri = domainRoot + "investors/10/15";
</script>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
jQuery AJAX Calls to RESTful WCF Web Services
</h2>

<div id="InvestorUri">
</div>
<div id="InvestorResult">
</div>

<hr />

<div id="InvestorsUri">
</div>
<div id="InvestorsResult">
</div>

<script type="text/javascript">

$(‘#InvestorUri’).html(investorUri);
$(‘#InvestorsUri’).html(investorsUri);

$.getJSON(investorUri, function investor_complete(json) {
$(‘#InvestorResult’).html(‘<li>’ + json.Id + ‘</li>’ + ‘<li>’ + json.Text + ‘</li>’);
});

$.getJSON(investorsUri, function investors_complete(json) {
var builtHtml = ”;

$.each(json, function (x, y) {
builtHtml += ‘<li>’ + json[x].Id + ‘</li>’ + ‘<li>’ + json[x].Text + ‘</li>’;
});

$(‘#InvestorsResult’).html(builtHtml);
});

</script>
</asp:Content>

[/sourcecode]

Run it and you should get the following displayed on screen.

Browser Results
Browser Results

Let me know if you run into any issues trying this out.  Thanks!

Shout it