Writing a Recipe for ASP.NET MVC 4 Developer Preview

asp.net mvc, asp.net, nuget 0 comments suggest edit

NOTE: This blog post covers features in a pre-release product, ASP.NET MVC 4 Developer Preview. You’ll see we call out those two words a lot to cover our butt. The specifics about the feature will change  and this post will become out-dated. You’ve been warned.

recipe All good recipes call for a significant amount of garlic.

Introduction

Last week I spoke at the //BUILD conference on building mobile web applications with ASP.NET MVC 4. In the talk, I demonstrated a recipe I wrote that automates the process to create mobile versions of desktop views.

view-mobilizer-recipe

Recipes are a great way to show off your lack of UI design skills like me!

In this blog post, I’ll walk through the basic steps to write a recipe. But first, what exactly is a recipe?

Obviously I’m not talking about the steps it takes to make a meatloaf surprise. In the roadmap, I described a recipe as:

An ASP.NET MVC 4 recipe is a dialog box delivered via NuGet with associated user interface (UI) and code used to automate a specific task.

If you’re familiar with NuGet, you know that a NuGet package can add new Powershell commands to the Package Manager Console. You can think of a recipe as a GUI equivalent to the commands that a package can add.

It fits so well with NuGet that we plan to add recipes as a feature of NuGet (probably with a different name if we can think of a better one) so that it’s not limited to ASP.NET MVC. We did the same thing with pre-installed NuGet packages for project templates which started off as a feature of ASP.NET MVC too. This will allow developers to write recipes for other project types such as Web Forms, Windows Phone, and later on, Windows 8 applications.

Getting Started

Recipes are assemblies that are dynamically loaded into Visual Studio by the Managed Extensibility Framework, otherwise known as MEF. MEF provides a plugin model for applications and is one of the primary ways to extend Visual Studio.

The first step is to create a class library project which compiles our recipe assembly. The set of steps we’ll follow to write a recipe are:

  1. Create a class library
  2. Reference the assembly that contains the recipe framework types. At this time, the assembly is Microsoft.VisualStudio.Web.Mvc.Extensibility.1.0.dllbut this may change in the future.
  3. Write a class that implements the IRecipe interface or one of the interfaces that derive from IRecipe such as IFolderRecipe or IFileRecipe. These interfaces are in the Microsoft.VisualStudio.Web.Mvc.Extensibility.Recipes namespace. The Developer Preview only supports the IFolderRecipe interface today. These are recipes that are launched from the context of a folder. In a later preview, we’ll implement IFileRecipe which can be launched in the context of a file.
  4. Implement the logic to show your recipe’s dialog. This could be a Windows Forms dialog or a Windows Presentation Foundation (WPF) dialog.
  5. Add the MEF ExportAttribute to the class to export the IRecipe interface.
  6. Package up the whole thing in a NuGet package and make sure the assembly ends up in the recipes folder of the package, rather than the usual lib folder.

The preceding list of steps itself looks a lot like a recipe, doesn’t it? It might be natural to expect that I wrote a recipe to automate those steps. Sorry, no. But what I did do to make it easier to build a recipe was write a NuGet package.

Why didn’t I write a recipe to write a recipe (inception!)? Recipes add a command intended to be run more than once during the life of a project. But that’s not the case here as setting up the project as a recipe is a one-time operation. In this particular case, a NuGet package is sufficient because it doesn’t make sense to convert a class library project into a recipe over and over gain.

That’s the logic I use to determine whether I should write a recipe as opposed to a regular NuGet package. If it’s something you’ll do multiple times in a project, it may be a candidate for a recipe.

A package to create a recipe

To help folks get started building recipes, I wrote a NuGet package, AspNetMvc4.RecipeSdk. And as I did in my //BUILD session, I’m publishing this live right now! Install this into an empty Class Library project to set up everything you need to write your first recipe.

The following screenshot shows an example of a class library project after installing the recipe SDK package.

recipe-class-lib

Notice that it adds a reference to the Microsoft.VisualStudio.Web.Mvc.Extensibility.1.0.dll assembly and adds a MyRecipe.cs file and a MyRecipe.nuspec file. It also added a reference to System.Windows.Forms.

Feel free to rename the files it added appropriately. Be sure to edit the MyRecipe.nuspec file with metadata appropriate to your project.

The interesting stuff happens within MyRecipe.cs. The following shows the default implementation added by the package.

using System;
using System.ComponentModel.Composition;
using System.Drawing;
using Microsoft.VisualStudio.Web.Mvc.Extensibility;
using Microsoft.VisualStudio.Web.Mvc.Extensibility.Recipes;

namespace CoolRecipe {
    [Export(typeof(IRecipe))]
    public class MyRecipe : IFolderRecipe {
        public bool Execute(ProjectFolder folder) {
            throw new System.NotImplementedException();
        }

        public bool IsValidTarget(ProjectFolder folder) {
            throw new System.NotImplementedException();
        }

        public string Description {
            get { throw new NotImplementedException(); }
        }

        public Icon Icon {
            get { throw new NotImplementedException(); }
        }

        public string Name {
            get { throw new NotImplementedException(); }
        }
    }
}

Most of these properties are self explanatory. They provide metadata for a recipe that shows up when a user launches the Add recipe dialog.

The two most interesting methods are IsValidTarget and Execute. The first method determines whether the folder that the recipe is launched from is valid for that recipe. This allows you to filter recipes. For example, suppose your recipe only makes sense when launched from a view folder. You can implement that method like so:

public bool IsValidTarget(ProjectFolder folder) {
    return folder.IsMvcViewsFolderOrDescendent();
}

The IsMvcViewsFolderOrDescendant is an extension method on the ProjectFolder type in the Microsoft.VisualStudio.Web.Mvc.Extensibility namespace.

The general approach we took was to keep the ProjectFolder interface generic and then add extension methods to layer on behavior specific to ASP.NET MVC. This provides a nice simple façade to the Visual Studio Design Time Environment (or DTE). If you’ve ever tried to write code against the DTE, you’ll appreciate this.

In this particular case, I recommend that you make the method always return true for now so your recipe shows up for any folder.

The other important method to implement is Execute. This is where the meat of your recipe lives. The basic pattern here is to create a Windows Form (or WPF Form) to display to the user. That form might contain all the interactions that a user needs, or it might gather data from the user and then perform an action. Here’s the code I used in my MVC 4 Mobilizer recipe.

public bool Execute(ProjectFolder folder) {
    var model = new ViewMobilizerModel(folder);
    var form = new ViewMobilizerForm(model);

    var result = form.ShowDialog();
    if (result == DialogResult.OK) {
        // DO STUFF with info gathered from the form
    }
    return true;
}

I create a form, show it as a dialog, and when it returns, I do stuff with the information gathered from the form. It’s a pretty simple pattern.

Packaging it up

Packaging this up as a NuGet package is very simple. I used NuGet.exe to run the following command:

nuget pack MyRecipe.nuspec

If it were any easier, it’d be illegal! I can now run the nuget push command to upload the recipe package to NuGet.org and make it available to the world. In fact, I did just that live during my presentation at BUILD.

Using a recipe

To install the recipe, right click on the solution node in Solution Explorer and select Manage NuGet Packages.

Find the package and click the Install button. This installs the NuGet package with the recipe into the solution.

To run the recipe, right click on a folder and select Add > Run Recipe. run-recipe-menu

Right now you’re probably thinking that’s an odd menu option to launch a recipe. And now you’re thinking, wow, did I just read your mind? Yes, we agree that this is an odd menu option. Did I mention this was a Developer Preview? We plan to change how recipes are launched. In fact, we plan to change a lot between now and the next preview. At the moment, we think moving recipes to a top level menu makes more sense.

The Run Recipe menu option displays the recipe dialog with a list of installed recipes that are applicable in the current context.

run-recipe-dialog

As you can see, I only have one recipe in my solution. Note that a recipe can control its own icon.

Select the recipe and click the OK button to launch it. This then calls the recipe’s Execute method which displays the UI you implemented.

Get the source!

Oh, and before I forget, the source code for the Mobilizer recipe is available on Github as part of my Code Haacks project!

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

18 responses

  1. Avatar for Andrew Wrigley
    Andrew Wrigley September 21st, 2011

    I'm sure the marketing department will be able to come up with a suitably indigestible and untypable acronym, that will serve the high purpose of making things seem far more complicated and inaccessible than they really are.
    As regards recipes, looks good.

  2. Avatar for Nolo
    Nolo September 21st, 2011

    Phil you are a genius, watched your build session got nothing to say but "job well done". learned a lot from you keep posting.

  3. Avatar for AbdouMoumen
    AbdouMoumen September 21st, 2011

    Very good stuff, as always. And even though I'm just an amateur in the ASP.NET MVC world, I find all of your post very informative.
    For the 'Run Recipe...' menu option, I guess it would make sense, as you said, to put it as a top level option, and maybe have a "recent recipes" list ;)
    One question: aside having to be run from a folder or file (for now, at least), are there any other constraints on what we can do with recipes?

  4. Avatar for Vladan Strigo
    Vladan Strigo September 21st, 2011

    Hi Phil,
    This is great!
    Please add an option to assign shortcuts to recepies. As the number of them will grow, that we have an option to have a shortcut for the ones we mostly use.
    Would it be possible?

    Thanks!
    Vladan Strigo

  5. Avatar for Mike
    Mike September 21st, 2011

    Reminds me of a cut down version of GAT that can be nuget'ted.

  6. Avatar for MisterJames
    MisterJames September 21st, 2011

    I like this...wish I could be a fly on the wall when you guys are talking about how this would influence the invocation of scaffolders and vice-versa, or if there's a fit there at all.
    Can recipe dialogs be invoked from context menus (like, in a code editor)?
    Also, are the IRecipe interfaces (beyond folder) something that other project teams are already working on (WP, Azure, W8), or something you hope they pick up? I like the idea of finding a more suitable place in the UI...recipes should be first class citizens in VS. Even just the implications of adding mobile view support in an automated fashion to existing applications is significant. So...even your trivial example is quite non-trivial.
    Small rock, big ripple.
    Btw...if you roast those peppers and toast the pine nuts, I think we have a very similar recipe. ;o)

  7. Avatar for Nick
    Nick September 21st, 2011

    This seems a really great idea and I'm impressed it is available....
    In the back of my head, I think back to all the previous tooling innovations like drag and drop data grids, designer generated classes - and I get a lot of bad memories.
    I'm hoping people come up with some good uses for this tool and prove it is a worthwhile feature.
    Until then, though.....

  8. Avatar for Jeff
    Jeff September 22nd, 2011

    What exactly is a "mobile versions of desktop views"? AFAIK views are simply markup...if you want to target desktop and mobile use media queries. This is getting kinda smelly...mvc is awesome, don't make it a webforms fail.

  9. Avatar for Paul Cowan
    Paul Cowan September 22nd, 2011

    The tedium is clicking your way through visual studio dialogue boxes.
    Microsoft teaches its developers to ignore the command line.
    This makes for lesser developers who are afraid of the command line.
    You only have to look at the "Add Reference" and the hilarious "Add View" dialogue to see how these visual impediments can be created so horribly wrong when the command line is a much more fluid way of working with the opportunity to more easily extend and write reusable scripts for.
    Every project command in rails is issued through the command line. The exact opposit is true of MS. EVery command is a clunky right click when you can be left with a "Visual Studio is waorking...." warning when you wish you were.
    I don't expect this to ever change.

  10. Avatar for Shawn
    Shawn September 23rd, 2011

    Thanks for the post Phil,
    I have just downloaded VS2011, MVC4 and your project.
    Can't seem to find the Microsoft.VisualStudio.Web.Mvc.Extensibility dll though. Any ideas on where to start looking for it?

  11. Avatar for haacked
    haacked September 23rd, 2011

    @Shawn did you try installing the AspNetMvc4.RecipeSdk package?

  12. Avatar for Giovanni Bassi
    Giovanni Bassi September 24th, 2011

    Hey Phil, I have just uploaded another recipe. I guess it is the second. It converts ASPX to Razor.
    Post here:
    blog.lambda3.com.br/...
    The source code is here:
    https://bitbucket.org/giggio/teched2011mvc4recipes
    Nuget pkg here (or just search recipe):
    nuget.org/List/Packages/Lambda3.ConvertAspxToRazor
    Cheers,
    Giovanni Bassi
    @giovannibassi

  13. Avatar for Konstantin Tarkus
    Konstantin Tarkus September 26th, 2011

    How about calling it "Helpers" and placing in the root menu (under the 'Add' for example). Each "Helper" (aka Recipe) would have it's own name under which it will be displayed in the context menu of Solution Explorer. For example:
    ...
    Add >
    Helpers >
    - Convert to .cshtml
    - Add mobile view
    ...

  14. Avatar for Johan
    Johan October 8th, 2011

    Is my understanding correct that this would be a good replacement for macros and t4templates execution?

  15. Avatar for @kish
    @kish October 9th, 2011

    I have developed one ASP.NET (C#) web application in Framework 4. I want to integrate an external module which is developed in MVC 3 Razor.
    How can I integrate this MVC module with my existing ASP.NET application?
    And also how to perform nevigation between them.
    Please help me...
    explain any sample example if possible...

  16. Avatar for Marc Gravell
    Marc Gravell November 3rd, 2011

    Do you know what would be awesome? (IMO)
    If this nuget mechanism could cater for some of the "custom tool" stuff for generating code from (for example) DSLs etc. At the moment, this is a: hard to do, and b: even harder to deploy - but if it could be done via a nuget-deployed "recipe"-type-thing...
    Just sayin' - looks good, though.

  17. Avatar for huer478
    huer478 January 19th, 2012

    It will have Responsive Web Design support ?
    It will implement something like it : http://awesome.codeplex.com/

  18. Avatar for HerrodFrye
    HerrodFrye January 19th, 2013

    Great post Phil. learned a lot from you keep posting.
    I have just downloaded VS2011, MVC4 and your project.
    Can't seem to find the Microsoft.Visual Studio.Web. Mvc.Extensibility dll though. Any ideas on where to start looking for it ? ?