September 2010 Entries
WCF REST Services Inside MVC Projects

Recently I blogged about WCF REST services with no svc file and no config. In this post I also discussed the pros/cons of WCF services as compared to using MVC controller actions for web services and I made the case that, in many instances, WCF REST services is better than using the MVC infrastructure because WCF provides:

  • a more RESTful API with less work
  • a convenient automatic help page to assist consumers of your service
  • automatic format selection (i.e., xml/json) depending on HTTP headers

In any case, using both WCF REST services and MVC-style web services are *both* quite convenient – and you don’t have to choose. You can use both. In fact, you can use both inside the same web application project. However, a recent question on StackOverflow illustrates that you have to be careful how you set up your routing in order to make WCF REST services work inside MVC applications.

If you have a look at the question on StackOverflow, you’ll see the routes were originally set up like this:

   1:  routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
   3:  routes.Add(new ServiceRoute("Person", new WebServiceHostFactory(), typeof(PersonService)));
   5:  routes.MapRoute(
   6:      "Default", // Route name
   7:      "{controller}/{action}/{id}", // URL with parameters
   8:      new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
   9:  );

The service route was for the WCF Service (using no *.svc file) and the last route was the standard MVC route. In most of the cases this works fine. Navigation to the root correctly lands on the HomeController’s Index method. Navigation to “/Person/help” correctly goes to the WCF REST help page and the WCF REST service can be invoked just fine. However, the action links were not correct.  When formulating a simple action link like this:

   1:  <%: Html.ActionLink("Home", "Index", "Home")%>

The resulting URL being produced was: “/Person?action=Index&controller=Home” which was clearly not correct. It’s hitting the first route and thinking it should just put “Person” for the first segment. It then cannot match the “controller” or “action” tokens so it just puts them in the query string. It will attempt to go to the service but the URI is incorrect so, if it’s a WCF REST service, you’ll just get the default help page.

Of course, we know that the *order* matters for routing so what if we put the MVC route first and the service route second? In this case, we run into a different problem. If the first URI segment is “Person” then it will try to match that as the controller name and you’ll get a 404 or, if you’re using a controller factory, your IoC container won’t be able to find a controller named “PersonController”.

So how can we get the WCF REST service route and the MVC routes to play nice with each other? The simple answer: use a route contraint:

   1:  routes.MapRoute(
   2:      "Default", // Route name
   3:      "{controller}/{action}/{id}", // URL with parameters
   4:      new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
   5:      new { controller = "^(?!Person).*" }
   6:  );
   8:  routes.Add(new ServiceRoute("Person", new WebServiceHostFactory(), typeof(PersonService)));

Notice on line #5 above, we add a route constraint so that it will try to use the MVC route first as long as the first segment (where the “controller” token is positioned) is not the “Person” string which matches where we want our WCF REST service to be. If it is “Person”, then it will fall through to the service route and correctly point to our WCF REST service.

One other interesting note when using service routes in MVC applications – anytime you’re going beyond the default MVC routes, I highly recommend you unit test your routes. This can be done very easily with the TestHelper that comes with MvcContrib. It makes it trivial to unit test routes like this:

   1:  [TestMethod]
   2:  public void default_path_should_be_able_to_match_controller()
   3:  {
   4:      "~/".Route().ShouldMapTo<HomeController>();            
   5:  }

The unfortunate caveat here is that you cannot use this when you’re combining service routes. If you do, you’ll get this exception when you initialize your routes at the start of the unit test by invoking the static RegisterRoutes() method: “System.InvalidOperationException: System.InvalidOperationException: 'ServiceHostingEnvironment.EnsureServiceAvailable' cannot be invoked within the current hosting environment. This API requires that the calling application be hosted in IIS or WAS.” Fortunately, we can still use Phil Haack’s Route Debugger:

 wcf route debug


With the URL of “/Person/23”, it correctly matches my service route for my WCF service.

WCF REST and MVC are both great and they should be used *together* when appropriate.

Posted On Wednesday, September 22, 2010 10:55 PM | Comments (7)
MVC 2 Model Metadata to Render Dynamic UI

Recently we had a project where we needed to render certain questions on the screen dynamically based on answers to previous questions on previous screens.  For questions that need to dynamically be visible/invisible on the same screen, this can simply be controlled with jQuery.  However, in this case, based on the user’s input on previous screens we know there are certain questions that will not be applicable before the current screen even loads. In this case, we could use jQuery to set those questions invisible as well – but given we know the questions will not be applicable for this user, it would be more efficient to filter those questions out to start with which minimizes the HTML that is sent down to the browser. Additionally, the client wanted a way to easily map the question answers to the requirements. 

For this example, suppose that on a previous screen, the user indicated that they are married. On the current screen, some of the information that needs to be collected is information on the spouse – but this should only be part of the screen if the user indicated they were married on the previous screen. The original code from the view models looked something like this:

   1:  public bool ShouldShowNB0021FirstName { get; set; }
   2:  public string NB0021FirstName { get; set; }
   3:  public bool ShouldShowNB0022LastName { get; set; }
   4:  public string NB0022LastName { get; set; }
   5:  public bool ShouldShowNB0023Age { get; set; }
   6:  public int NB0023Age { get; set; }

A couple of things immediately jump out about this code. First, the property names are prefixed with a cryptic identifier (e.g., “NB0021”). This identifier is probably very meaningful to some business analyst somewhere and helps map back to the requirements doc but it’s somewhat distracting on the property names. Further, it’s going to render in the HTML mark up on the html element names which is a little odd. Also, each of these properties has a corresponding Boolean property which controls visibility for it. This resulted in views that looked somewhat like this:

   1:  <% if (this.Model.ShouldShowNB0021FirstName) {  %>
   2:      <div>
   3:          <%: Html.LabelFor(m => m.NB0021FirstName) %>
   4:          <%: Html.TextBoxFor(m => m.NB0021FirstName) %>
   5:      </div>
   6:  <% } %>
   7:  <% if (this.Model.ShouldShowNB0022LastName) {  %>
   8:      <div>
   9:          <%: Html.LabelFor(m => m.NB0022LastName) %>
  10:          <%: Html.TextBoxFor(m => m.NB0022LastName)%>
  11:      </div>
  12:  <% } %>
  13:  <% if (this.Model.ShouldShowNB0023Age) {  %>
  14:      <div>
  15:          <%: Html.LabelFor(m => m.NB0023Age) %>
  16:          <%: Html.TextBoxFor(m => m.NB0023Age)%>
  17:      </div>
  18:  <% } %>

While this certainly works, it’s a little verbose and not particularly DRY. Further, there is a lot of procedural code necessary to appropriately assign the Boolean visibility properties before rendering the view.

So let’s see how we can refactor this a little to save ourselves some work. First off, let’s create a custom attribute called “QuestionId” so that we can simplify the property names:

   1:  [QuestionId("NB0021")]
   2:  public string FirstName { get; set; }
   4:  [QuestionId("NB0022")]
   5:  public string LastName { get; set; }
   7:  [QuestionId("NB0023")]
   8:  public int Age { get; set; }

The implementation of the attribute is trivial:

   1:  public class QuestionIdAttribute : Attribute
   2:  {
   3:      public string Id { get; set; }
   5:      public QuestionIdAttribute(string id)
   6:      {
   7:          this.Id = id;
   8:      }
   9:  }

Next, let’s just add a single property to the view model (let’s call it “ApplicableQuestions”) which contains the collection of QuestionId’s that are still applicable given all the user’s answers on the previous screens. This “applicability logic” is encapsulated in another layer and beyond the scope of this post. What is relevant is that it will return an array of QuestionId’s that are still applicable. This now allows are views to look like this:

   1:  <% if (Model.ApplicableQuestions.Contains("NB0022")) { %>
   2:      <%: Html.EditorFor(m => m.LastName)%>
   3:  <% } %>

This is somewhat better because we don’t have to have all of the Boolean visibility properties, but I’ve still got a bunch of IF statement in the view and the identifier is still cryptic. To solve this, let’s created a custom EditorTemplate called “ScalarQuestion.ascx” that is meant for all questions that have basic text boxes. We can complete our view model by adding [UIHint] attributes as well as [DisplayName] attributes. Now the properties of our view model look like this:

   1:  [UIHint("ScalarQuestion")]
   2:  [DisplayName("First Name")]
   3:  [QuestionId("NB0021")]
   4:  public string FirstName { get; set; }
   6:  [UIHint("ScalarQuestion")]
   7:  [DisplayName("Last Name")]
   8:  [QuestionId("NB0022")]
   9:  public string LastName { get; set; }
  11:  [UIHint("ScalarQuestion")]
  12:  [QuestionId("NB0023")]
  13:  public int Age { get; set; }
  15:  public IEnumerable<string> ApplicableQuestions { get; set; }

Ultimately, we want to be able to call the EditorFor() method without having to wrap it in an IF statement which we can now do like this:

   1:  <%: Html.EditorFor(m => m.FirstName, new { applicableQuestions = Model.ApplicableQuestions })%>
   2:  <%: Html.EditorFor(m => m.LastName, new { applicableQuestions = Model.ApplicableQuestions })%>
   3:  <%: Html.EditorFor(m => m.Age, new { applicableQuestions = Model.ApplicableQuestions })%>

Since we’re passing in the list of applicable questions, the editor template can check to see if it should display:

   1:  <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
   2:  <%@ Import Namespace="DynamicQuestions.Models" %>
   3:  <%@ Import Namespace="System.Linq" %>
   4:  <% 
   5:      var applicableQuestions = this.ViewData["applicableQuestions"] as IEnumerable<string>;
   6:      var questionAttr = this.ViewData.ModelMetadata.ContainerType.GetProperty(this.ViewData.ModelMetadata.PropertyName).GetCustomAttributes(typeof(QuestionIdAttribute), true) as QuestionIdAttribute[];
   7:      string questionId = null;
   8:      if (questionAttr.Length > 0)
   9:      {
  10:          questionId = questionAttr[0].Id;
  11:      }
  12:      if (questionId != null && applicableQuestions.Contains(questionId)) { %>
  13:          <div>
  14:              <%: Html.Label("") %>
  15:              <%: Html.TextBox("", this.Model)%>
  16:          </div>
  17:  <%  } %>

One line #5 we get the applicable question that were passed in. Then we check for the [QuestionId] attribute on the view model property – if it’s there, then we know we need to display the HTML – if not, we don’t. At this point we’ve 1) eliminated all of the Boolean visibility properties, 2) eliminated the cryptic identifier from the property name and made it part of metadata instead by using a declarative attribute, 3) eliminated all of the IF statements in the views, and 4) automatically mapped the applicability identifiers to the metadata specified on the view model. The only thing that still bugs me a little is the fact that having to create the anonymous type to pass the ApplicableQuestions property for every property means our code still violates DRY. To improve this, let’s create a custom Html helper called QuestionFor():

   1:  public static MvcHtmlString QuestionFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) where TModel : class, IApplicable
   2:  {
   3:      return htmlHelper.EditorFor(expression, new { applicableQuestions = htmlHelper.ViewData.Model.ApplicableQuestions });
   4:  }

Note that I introduced an interface called IApplicable which simply has a single property:

   1:  public interface IApplicable
   2:  {
   3:      IEnumerable<string> ApplicableQuestions { get; set; }
   4:  }

I can have my view models that have the ApplicableQuestions property implement this interface so that we can utilize the generic constraint on the HTML helper. Now my view becomes simplicity:

   1:  <%: Html.QuestionFor(m => m.FirstName) %>
   2:  <%: Html.QuestionFor(m => m.LastName) %>
   3:  <%: Html.QuestionFor(m => m.Age) %>

A final consideration to keep in mind is validation. Given that some properties of the view model won’t be present when the user saves the screen, validating a dynamic UI must be taken into account. The code sample for this post can be downloaded here.

Posted On Wednesday, September 8, 2010 11:37 PM | Comments (6)

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers

Google My Blog

Tag Cloud