Apr 24 2012

Using SpecsFor.Mvc - Dealing with Authentication

Category: ASP.NET | MVC | SpecsFor | TestingMatt @ 15:31

In all of the SpecsFor.Mvc examples I’ve posted so far, I’ve omitted one common cross-cutting requirement of web applications: authentication!  Most web apps have some sort of authentication, and we need to be able to test our core application logic without this cross-cutting concern getting in the way.  SpecsFor.Mvc makes it easy to achieve exactly that, as I’ll show you in this post.

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:

Jul 8 2010

Using MVCContrib glue to bring Visual Basic WebForms and C# MVC together

Category: ASP.NET | MVCMatt @ 16:14

While I’m digging my new job, I’ve found myself in the less-than-desirable situation of having to work with WebForms again.  After working almost exclusively with ASP.NET MVC for the better part of three years, the inadequacies of WebForms seem even more prominent.  While running MVC and WebForms in the same applications is easy enough, we’re facing an added complication: our WebForms application was written in Visual Basic.  While you can’t mix-and-match C# MVC and VB WebForms within the same application directly, you can leverage MVCContrib’s Portable Areas to bring the two together, which is what I’ll show you how to do in this post.

Giving credit where credit is due, Jeffery Palermo already described how you can leverage MVCContrib’s Portable Areas within a WebForms application.  Unfortunately, his sample doesn’t work (it appears to depend on an older MVC 2 CTP), and his post omits several important details.  Jeffery’s post also focuses on the creation of reusable modules whereas my interest is bringing C# into our VB project as seamlessly as possible.  Hopefully this guide, even though it’s focus is on bridging the C#-VB divide, will be useful to anyone that’s envious of MVC but stuck with a substantial amount of WebForms code.

Create a new solution

To get started, let’s go ahead and create a new empty solution in Visual Studio 2010.

VisualStudioNewProject

Next, add a Visual Basic WebForms application.  When you run the application, you should see something like this:

VbWebFormsHome

Note that I modified my master page to make it clear that the master page is a WebForms master page.  You’ll understand why later. 

Add MVC Support

As I mentioned, there’s plenty of information on the web about adding MVC support to an existing WebForms application.  The two play nicely side-by-side in the same project.  First, add references to the required assemblies: System.Web.Mvc, System.Web.Routing, and System.Web.Abstractions.  The last one is probably not strictly required, but I added it anyway. This is also a good time to go ahead and add a reference to MVCContrib.  Note that for the approach I’m going to describe to work, you will need to use a build based on the trunk and not the older release build available from CodePlex.  You can pick up a nightly build from the MVCContrib TeamCity build server (be sure to grab artifacts from a successful build).

MvcContribTeamCity

Next, you need to update your web.config file.  Replace the default compilation section with the following:

<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>

Also add a pages element with the namespaces shown:

<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>

This step probably isn’t necessary (depends on how you are deploying your application), but for good measure, go ahead and add the MVC HTTP handler and a binding redirect for MVC 1:

<httpHandlers>
    <add verb="*" path="*.mvc" 
        validate="false" type="System.Web.Mvc.MvcHttpHandler,
        System.Web.Mvc, Version=2.0.0.0, Culture=neutral, 
        PublicKeyToken=31BF3856AD364E35"/>
</httpHandlers>
<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>

Next up, we need to add some code in our Global.asax file:

Shared Sub RegisterRoutes(ByVal routes As RouteCollection)
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}")

    ' MapRoute takes the following parameters, in order:
    ' (1) Route name
    ' (2) URL with parameters
    ' (3) Parameter defaults
    routes.MapRoute( _
     "Default", _
     "{controller}/{action}/{id}", _
     New With {.controller = "Home", .action = "Index", .id = UrlParameter.Optional} _
    )

End Sub

Sub Application_Start()
    AreaRegistration.RegisterAllAreas()

    PortableAreaRegistration.RegisterEmbeddedViewEngine()

    RegisterRoutes(RouteTable.Routes)
End Sub

The RegisterRoutes procedure is pretty standard, and AreaRegistration.RegisterAllAreas is boiler-plate MVC 2.0 stuff.  The other bit is important though: PortableAreaRegistration.RegisterEmbeddedViewEngine will register a special view engine that allows the application to consume Portable Areas.

The final thing we need to do is add some special folders that are needed by MVC: the Areas folder and the Views folder.  For now, go ahead and add a view master page at ‘~/Views/Shared/Site.master’.  I’d recommend copying both the Views and Areas folders from an existing ASP.NET MVC application (be sure to remove any of the views you don’t need, but save the Site.master one).  Once you’re finished, you should have a structure like this:

VbProjectCurrentState

At this point, the WebForms project is ready to consume our C# ASP.NET MVC Portable Area.  All we need to do is create one!

Creating a Portable Area

Most people recommend that you create a regular class library to serve as the host of your portable area, but there are some disadvantages to that approach.  First, you will have to re-add all the necessary MVC references in order to build controllers and views.  Second, you will lose all the Visual Studio tooling for MVC (as well as Resharper extensions for MVC projects).  In light of that, I recommend adding a regular-old ASP.NET MVC 2 (C#) application to your solution.  An MVC project is essentially a web application project, which means it produces a single DLL that you can reference from another application. 

After adding your MVC 2 project, you can remove all of the following: web.config files, Global.asax, views (but leave ‘~/Views/Shared/Site.master’ for now), controllers, models, script files, and CSS files. Save your project at this point, then unload it so that you can edit it. 

Edit project file, and add the following line to end of script before closing ‘project’ tag:

<Target Name="BeforeBuild">
  <ItemGroup>
    <EmbeddedResource Include="**\*.aspx;**\*.ascx;**\*.gif;**\*.css;**\*.js;**\*.png;**\*.jpg" />
  </ItemGroup>
</Target>

One of the requirements for building a Portable Area is that the views and any resources they depend on must be embedded in the assembly.  You can do this manually using the Properties pane in Visual Studio, but it’s easy to forget this critical step.  The MSBuild snippet above automatically embeds your views and any common content files. 

With your project file now modified, go ahead and reload it, then add a reference to MVCContrib.  We’ll add a “HelloWorld” controller to our project:

public class HelloWorldController : Controller
{
    public ViewResult Index()
    {
        return View();
    }

}

And we’ll create the corresponding view for our Index action:

<%@ Page Title="Hello from C#!" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Hello from C#!
</asp:Content>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">

    <h2>Hello, World!</h2>
    <p>This is a view from a portable area created in C#!</p>
    <p>The current time is <%=DateTime.Now.ToShortTimeString() %></p>

</asp:Content>

When creating the view, go ahead and specify the master, but note that at runtime the view will actually use the master from the parent project, not the Portable Area project.  We left the master view in the Portable Area project so that the views we create will have the correct content placeholder IDs (more on this later). 

The last thing we need to do to complete our portable area is add a registration class:

public class HelloWorldRegistration : PortableAreaRegistration
{
    public override string AreaName
    {
        get { return "HelloWorld"; }
    }

    public override void RegisterArea(AreaRegistrationContext context, IApplicationBus bus)
    {
        context.MapRoute("HelloWorld", "helloworld/{action}",
                         new { controller = "helloworld", action = "index" });

        RegisterAreaEmbeddedResources();
    }
}

Registration is fairly simple: we give our area a name, map a route to it, and call a base class method to register all the embedded resources for our Portable Area.

Visual Basic: Say Hello to C#!

So far we have a Visual Basic WebForms application and  C# MVC application, but the two aren’t playing in the same sandbox yet.  In the WebForms application, add a project reference to the C# MVC application.  Visual Studio will kindly let us do this because an MVC project is really just a class library project underneath all the bells and whistles.  

Build and launch your WebForms app.  It should take you to Default.aspx, which is a regular WebForms page.  Now, try navigating to the controller we created in C# by appending “/helloworld” to the URL.  If I’ve described the steps correctly, you should be greeted with something like the following:

CSharpPortableArea1

Our WebForms app is routing requests to the controller we defined in C#, and the embedded view engine is returning the corresponding view.  Do note, though, that our view is being rendered using our VB MVC view master page, and not the view master page from our C# Portable Area.  That’s by design: Portable Areas are designed to be plugged in and reused within an application.  In this case though, we’d actually like for our C# view to use the same WebForms master page as our existing WebForms content; we don’t want to maintain two distinct master pages.  It turns out that you can do this quite easily.

C# View, Meet VB WebForms Master Page

Examining our view again, we see that it provides content for two placeholders: TitleContent and MainContent.  These placeholders match the standard MVC view master page that we added to our WebForms project under ‘~/Views/Shared/Site.Master’.  If you open up the WebForms master at ‘~/Site.master’ though, you’ll notice that it defines only HeadContent and MainContent.  Let’s add a TitleContent placeholder so that our views can still define their own page titles in the usual MVC way:

<head runat="server">
    <title><asp:ContentPlaceHolder ID="TitleContent" runat="server"/></title>
    <link href="~/Styles/Site.css" rel="stylesheet" type="text/css" />
    <asp:ContentPlaceHolder ID="HeadContent" runat="server">
    </asp:ContentPlaceHolder>
</head>

Next, go back our view in the Portable Area, and change its MasterPageFile property to ‘~/Site.master’.  If you are using Resharper, you’ll notice errors indicating that there’s no master page file at that location and that your content placeholder IDs are no longer valid.  That’s because as far as Resharper (and Visual Studio) know, you are now referencing a master that doesn’t exist.  The only view master page in the C# project is at ‘~/Views/Shared/Site.master’.  All we need to do is move the master from it’s current location to the root of our C# project, and the errors will go away.  When we create new views in the future, we can reference the view master page at its new location, and our views will be generated with the correct path for our WebForms master.

Rebuild and refresh your application, and you’ll see that the Index view from our HelloWorld controller is now being rendered within the WebForms master, title-and-all:

CSharpPortableArea2

Keep in mind that what I’ve described is a proof-of-concept only; I have not tried applying this to a real project yet, and there are likely to be issues with this approach.  If you do run into issues, I’d like to hear about them.  If you want to checkout my code, you may download the sample project here.

Tags: