May 19 2012

Using SpecsFor.Mvc - Establishing Context with Seed Data

Category: SpecsFor | Testing | ASP.NET | MVCMatt @ 16:11

SpecsFor.Mvc gives you a lot in a single NuGet package.  You get a test web host, a strongly-typed API for navigating around and interacting with your MVC app, and standard hooks for dealing with cross-cutting concerns like authentication, but you also get hooks that you can use to add your own behavior.  In this post, I’ll show you how to use one of those hooks to load seed data into your application to facilitate testing.

More...

Tags:

Apr 15 2012

Introducing MvcDisplayTemplates

Category: ASP.NET | MVC | SpecsForMatt @ 16:00

Beginning with version 2.0, ASP.NET MVC has shipped with a set of templates for both displaying and editing data.  These templates are buried within the System.Web.Mvc assembly.  While you can override them outright, you cannot easily extend them since they’re locked down.  The new MvcDisplayTemplates NuGet package fixes this problems.

More...

Tags:

Mar 30 2012

SpecsFor.Mvc 1.1 Released

Category: ASP.NET | MVC | Testing | SpecsForMatt @ 14:25

A new release of SpecsFor.Mvc is now live on NuGet.  This release includes one simple, but important, enhancement as well as a few other minor improvements.

More...

Tags:

Mar 8 2012

SpecsFor.Mvc 1.0 Released!

Category: SpecsFor | Testing | ASP.NET | MVCMatt @ 17:05

image

It’s late, so this will be a short post, but SpecsFor.Mvc version 1.0 has been released.  It took me a lot longer to wrap this up than I expected, but there are a lot more features in the 1.0 release than I had originally planned.  Overall, I’m very pleased with the end-user experience of the framework, though admittedly the internals of the project are in need of some cleanup and refactoring.  I have a lot of content planned that will highlight what SpecsFor.Mvc can do, but for now, this short video will have to do.  Be sure to watch the video in HD!  Enjoy!

Tags:

Mar 8 2012

ASP.NET MVC 3, Razor-C#, and VB.NET WebForms - ActionLink, RenderPartial, and RenderAction in WebForms

Category: ASP.NET | MVCMatt @ 15:55

Today I’m going to show you how to use MVC helper methods in your ASP.NET WebForms markup.  Why would you want to do this?  As I explained and demonstrated in the previous two posts in this series, I’m working on a project that has an extensive investment in WebForms (of the Visual Basic .NET variety), and a wholesale migration to MVC is just not possible.  With a little bit of black magic, I’ve shown you how to write C# MVC code and Razor views that can be consumed via a VB.NET WebForms app.  With this last “spell,” I’ll show you how you can start bringing that content into WebForms pages.

More...

Tags:

Mar 1 2012

ASP.NET MVC 3, Razor-C#, and VB.NET WebForms - Using Razor Views With WebForms Master Pages

Category: ASP.NET | MVCMatt @ 15:42

When we left off last time, I showed you how to write ASP.NET MVC code in C#, then consume and expose that functionality within a VB.NET WebForms project.  Why?  Because I’m maintaining a project with a huge investment in VB.NET WebForms, and a wholesale migration isn’t feasible.  This approach allows for a gradual migration from VB.NET WebForms to C# ASP.NET (with Razor!)  So far, all I’ve shown you how to do is render Razor views.  But what about all those existing VB.NET master pages?  Today I’ll show you how you can use these master pages as “layouts” for your Razor views.

More...

Tags:

Feb 15 2012

ASP.NET MVC 3, Razor-C#, and VB.NET WebForms - A Tale of Black Magic Voodoo

Category: ASP.NET | MVCMatt @ 15:45

What do you get when you mix a legacy VB.NET WebForms application, ASP.NET MVC, and Razor views that are written in C#?  If you said “Pain!” you are quite right.  But what you also get is the ability to leverage your existing investment in VB.NET WebForms while crafting new code in ASP.NET MVC Razory-C# goodness.  In this series of posts, I’m going to tell you how you, too, can concoct a wicked brew that will enable you to do crazy things, such as creating Razor views in C# that utilize VB.NET WebForms master pages, or how you can render MVC action methods from within WebForms markup.

More...

Tags:

Jul 7 2011

Cleaning up POSTs in ASP.NET MVC, the Fail Tracker Way

Category: Fail Tracker | MVC | ASP.NETMatt @ 12:36

Those who have worked with ASP.NET MVC for more than a day have no doubt found themselves repeating common patterns when handling POSTs.  Jimmy Bogard recently blogged one way to simplify your actions.  I handled the same problem in Fail Tracker by implementing a very simple convention (one-model-in, one-model-out) and pushing some responsibility into the application framework.  With this in place, cross-cutting POST handling logic can be pushed out of the action methods, and a common “doh” error (forgetting to perform server-side validation) can be eliminated.  Read on to find out how you can adopt this simple convention in your application framework. 

More...

Tags:

Jul 5 2011

Building Nice Display Names From Pascal-Case View Model Names in ASP.NET MVC 3

Category: ASP.NET | MVC | Fail TrackerMatt @ 12:56

One of my goals with Fail Tracker is to push the “Convention over Configuration” idea as far as you possibly can within the confines of ASP.NET MVC.  I’m obviously biased, but so far I think I’ve been quite successful, and Fail Tracker is probably the most enjoyable codebase I’ve ever worked with.  One convention I recently implemented eliminates the need to decorate view model properties with the DisplayNameAttribute.  The convention says “Pascal cased property names will be intelligently split into strings with spaces.”  Thanks to the infrastructure and pluggable model metadata system that Fail Tracker uses, implementing this convention was quite easy.

By default in ASP.NET MVC 3, a view model like the following:

public class LogOnForm
{
    [Required]
    [EmailAddress]
    public string EmailAddress { get; set; }

    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; }
}

will generate a field label that looks like this:

image

Note the missing space between “Email” and “Address.”  This is obviously not what we want.  The way I solved it originally was to apply the DisplayNameAttribute to my view model:

public class LogOnForm
{
    [Required]
    [EmailAddress]
    [DisplayName("Email Address")]
    public string EmailAddress { get; set; }

    [Required]
    [DataType(DataType.Password)]
    public string Password { get; set; }
}

which gives the desired UI:

image

But this seemed pointless to me.  Why should I have to remember to de-Pascal case my property names every time I created a view model?  Fortunately, I don’t have to.  Here’s the “model metadata filter” (a feature of the Fail Tracker application framework) that achieves the same thing:

public class PascalCaseToDisplayNameFilter : IModelMetadataFilter
{
    public void TransformMetadata(System.Web.Mvc.ModelMetadata metadata, IEnumerable<Attribute> attributes)
    {
        if (!string.IsNullOrEmpty(metadata.PropertyName) && !attributes.OfType<DisplayNameAttribute>().Any())
        {
            metadata.DisplayName = metadata.PropertyName.ToStringWithSpaces();
        }
    }
}

The ToStringWithSpaces extension method is a beautiful piece of Regex that James Kolpack gets credit for:

public static class StringExtensions
{
     public static string ToStringWithSpaces(this string input)
     {
         return Regex.Replace(
            input,
            "(?<!^)" + // don't match on the first character - never want to place a space here
            "(" +
            "  [A-Z][a-z] |" + // put a space before "Aaaa"
            "  (?<=[a-z])[A-Z] |" + // put a space into "aAAA" before the first capital
            "  (?<![A-Z])[A-Z]$" + // if the last letter is capital, prefix it with a space too
            ")",
            " $1",
            RegexOptions.IgnorePatternWhitespace);
     }
}

And that’s all there is to it.  Thanks to the application framework that is baked into Fail Tracker, simply creating the PascalCaseToDisplayNameFilter is all that’s necessary.  I can now remove most of my DisplayNameAttributes from my view models.  Do note though that the convention is quite easy to override.  If I do need to alter the labeling for a property, I can still use the DisplayNameAttribute exactly as before.  All my convention does is provide a more reasonable default without getting in the way of doing something different, which is exactly what a good convention should do.

If you want to know more about how these sorts of conventions are automagically wired up in Fail Tracker, feel free to check out Fail Tracker code.  I do plan to write much more about the application framework itself, but it may be a while as I’m targeting a magazine publication for that. Smile

Tags:

Oct 17 2010

Globally handling status messages in ASP.NET MVC

Category: MVCMatt @ 03:11

I’m a big fan of pushing common concerns down into the infrastructure and framework of an application so that developers can easily leverage them.  One such concern that may come in a web application is displaying status messages, such as “Record Saved” or “There was a problem communicating with the database”.  My initial attempt at adding such functionality to RageFeed failed to encapsulate the concept of a status message, but after rethinking it and actually modeling it as an explicit action, I came up with an approach that I really like.

The Problem

It’s been a while since I’ve talked about RageFeed, my super-secret (probably will never see the light of day) social networking app, but I do manage to find time for it occasionally.  For a story I was working recently, I needed to display a status message to the user: whether or not their last operation succeeded or failed. 

Balsamiq Mockups rocks!

This is a fairly standard requirement.  One thing I wanted to do though was correctly implement the Post/Redirect/Get pattern.  To summarize, I wanted users to be able to bookmark the form, return to it, refresh the form, etc. without triggering duplicate form submissions.  At the same time though, I wanted to have a status message display on the page, but only immediately after the user submitted the form.  If the user refreshed the page again, the status message should disappear. 

This seemingly tricky requirement is actually quite easy to implement using ASP.NET MVC’s TempData container.  Craig Stuntz has a good post on this poorly-named container.  In a nutshell, it’s a session-baked store that persists its contents across exactly one round-trip to the server.  This makes it ideal for passing data between requests in a Post/Redirect/Get scenario. 

First attempt - TempData

My initial attempt at implementing status messages for RageFeed worked as follows.  First, I added the message to TempData after the post-back, then performed a redirect back to the form:

[HttpPost]
[BindCurrentUserToParam("currentUser")]
public ActionResult Stalk([Bind(Exclude = "Id")]User currentUser, StalkForm stalkForm)
{
    if (!ModelState.IsValid)
    {
        return View();
    }

    var reply = _bus.RequestReply<StalkUserRequest, StalkUserReply>(
        new StalkUserRequest {RequestingUser = currentUser, TargetUsername = stalkForm.Username});

    if (!reply.Succeeded)
    {
        ModelState.AddModelError("Username", GetErrorMessageFor(reply.FailureReason));
        return View();
    }

    TempData.Add("SuccessMessage", string.Format("You are now stalking {0}!", stalkForm.Username));

    return this.RedirectToAction(c => c.Stalk());
}

And here’s how the TempData was handled in the view:

<% if (TempData["SuccessMessage"] != null) { %>
<div class="success-message">
    <%=TempData["SuccessMessage"] %>
</div>
<%} %>
<h1>Stalk Your Friends</h1>
<%using (Html.BeginValidatedForm()) { %>

There were definitely some problems with this approach though.  First, it was not reusable at all. I would have had to reimplement this functionality on any other pages that required status messages.  It was also loosely-typed and used a magic string, neither of which are optimal from a maintenance point of view.  Finally, the code did not clearly express it’s intent.  Someone reading the code may see the message being placed in TempData, but without knowledge of the system, they wouldn’t really know what that’s supposed to do.  The concept of displaying a status message after redirecting is very weakly modeled. 

Final solution – custom view result

I needed to do a few things to overcome these limitations.  First, I had to bake the concept of a status message deeper into the application framework.  I also had to make leveraging the concept easier, and make the code’s intent clearer, which I did by making the action (redirecting with a status message) explicit.  The final controller code looks like this:

[HttpPost]
[BindCurrentUserToParam("currentUser")]
public ActionResult Stalk([Bind(Exclude = "Id")]User currentUser, StalkForm stalkForm)
{
    ...

    return RedirectWithStatusMessage<StalkerController>(c => c.Stalk(), 
            StatusMessage.Success("You are now stalking {0}!", stalkForm.Username));
}

The action now utilizes a custom action result. Under the hood it is still using TempData, but the developer utilizing the concept no longer needs to know or care about that: it’s all handled by the application framework.  For completeness, here’s what the custom action result looks like:

public class RedirectWithStatusMessageResult<T> : MvcContrib.ActionResults.RedirectToRouteResult<T> where T : Controller
{
    public StatusMessage Message { get; private set; }

    public RedirectWithStatusMessageResult(StatusMessage message, Expression<Action<T>> expression) : base(expression)
    {
        Message = message;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.Controller.TempData["StatusMessage"] = Message;

        base.ExecuteResult(context);
    }
}

Using a custom action result also makes the code a little easier to test and better separates the concerns, which is a nice bonus.

I also refactored the display of the status message out of the view and into the view master, making it available to any view in the entire application.

Final thoughts

The core problem with my initial solution was that it did not model the concept of redirecting with a status message explicitly.  Instead, it was clumsily specified using the weakly-typed TempData container.  After modeling the concept explicitly and pushing it into the application framework, I gained more readable code, code reuse, improved separation of concerns, and improved testability.  The moral of this story is to always model concepts explicitly, and to push any common concerns into the application framework where application developers can easily take advantage of them.

Tags: