Light up your NuGets with startup code and WebActivator

[Please see the WebActivator wiki for the latest docs]

Wow, it’s hard to believe that it’s been less than a week since NuGet went public.  We were hoping to get noticed, but the attention we got was way beyond my wildest expectations!  The buzz on Twitter has just been phenomenal, and for the most part the feedback has been very positive.  Thank you all for that, this is very encouraging for our little NuGet team :)

 

Going beyond assembly references

 

The most common type of NuGet packages bring in assembly references into a project.  It’s super useful, because it saves you from finding it, downloading it, adding the reference, getting dependencies and dealing with versioning.  If NuGet didn’t do any more than that, I think it could be very successful, because that is such a core step that gets repeated manually so many times by so many people.

But it can in fact do a lot more than that!  The first thing that you probably saw already is the config transform support.  I demoed that in my earlier screencast and various people blogged it, with the ELMAH package adding a bunch of things to web.config to make things work.

But not everything that configures libraries goes through web.config.  Thankfully, many libraries come with a code driven way of setting things up (I personally much prefer that over config).  A good example is Louis DeJardin’s Spark View Engine.  In order to use it in an ASP.NET MVC application, you need to create a SparkSettings instance, configure it, feed it to a SparkViewFactory, and then register it as a View Engine with MVC.  Pretty painful steps if you’ve not done it before and just want to get started with Spark.  See Step 5 in this page for an example of what I’m referring to.

So what can NuGet do to make your life easier in these scenarios?  Quite simply, NuGets can include some source code that ends up as part of the project.  For instance, if you install the SparkMvc package, you’ll notice a file named SparkMvc.cs under the App_Start folder, which contains this set up code.  How did the package pull that trick?  Very easy: all it needs to do is include a file named SparkMvc.cs .pp in its Content\App_Start folder.  The reason it has an extra .pp extension is to indicate that it should be preprocessed, which allows it to end up in the correct namespace.  SparkMvc.cs.pp contains (omitting one magic line that I’ll discuss later!):

 using System.Web.Mvc;
using Spark;
using Spark.Web.Mvc;

// Secret line goes here! :)

namespace $rootnamespace$.App_Start {
    public static class SparkMvc {
        public static void Start() {
            var settings = new SparkSettings();

            // comment this if you want to use Html helpers with the ${} syntax:
            // otherwise you would need to use the <%= %> syntax with anything that outputs html code
            settings.SetAutomaticEncoding(true); 

            settings
                .AddNamespace("System")
                .AddNamespace("System.Collections.Generic")
                .AddNamespace("System.Linq")
                .AddNamespace("System.Web.Mvc")
                .AddNamespace("System.Web.Mvc.Html");
                // Add here more namespaces to your model classes
                // or whatever classes you need to use in the views

            settings
                .AddAssembly("Spark.Web.Mvc")
                .AddAssembly("System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")
                .AddAssembly("System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");

            ViewEngines.Engines.Add(new SparkViewFactory(settings));
        }
    }
}

So the only funny business here is the $rootnamespace$ token, which gets replaced by the namespace of the target project (as in Item Templates).  Other than that, it’s just a static method that contains all the Spark set up logic, which you can of course freely modify once it’s in your app.  It’s just starter code to get you on the right path.

Finally, you’d need to go in your global.asax’s Application_Start and add a line to call this static method.  That works, but that’s kind of a lame manual step that you always have to take after installing the package.  But wait, maybe you don’t have to!

 

Enter WebActivator

 

Luckily, there is a very simple way to avoid forcing the package’s user to manually call the startup code from global.asax.  And this mechanism is not provided by the NuGet engine itself, but comes simply as a result of taking a dependency on a little NuGet called WebActivator.  If you look for at the .nuspec manifest file for SparkMvc, you’ll see something like:

   <dependencies>
    <dependency id="spark" minversion="1.0" />
    <dependency id="WebActivator" version="1.0.0.0" />
  </dependencies>

Note that in addition to having a dependency on Spark core, it has a dependency on the WebActivator NuGet.  So what exactly does that do?  It lets you write the secret magic line that I omitted in the code above:

 using System.Web.Mvc;
using Spark;
using Spark.Web.Mvc;

[assembly: WebActivator.PreApplicationStartMethod(typeof($rootnamespace$.App_Start.SparkMvc), "Start")]

namespace $rootnamespace$.App_Start {
    public static class SparkMvc {
        public static void Start() {
            // etc...

 

So WebActivator lets you define a PreApplicationStartMethod attribute which is used to mark your method as something that gets called when the Web app starts.  And that’s it!  Now when you install the SparkMvc NuGet in your app, Spark is completely ready to go, and you just need to add your spark views.

Note: the sources for WebActivator are on bitbucket.

 

Hey, this attribute looks familiar!

 

If the PreApplicationStartMethod attribute looks familiar, it’s because ASP.NET 4.0 actually comes with one named exactly the same, and that does the exact same thing (Phil Haack did a nice post on it).  So why the heck aren’t we using it instead of coming up with a new one named the same and that does the same thing?!  The reason is that we were short sighted when we designed this feature in ASP.NET, and only allowed this attribute to be used once per assembly.  This works fine for scenarios where you own the whole assembly, but fails terribly for our NuGet scenario where we want unrelated packages to each come with their startup logic.  ASP.NET fail!  Wait, I was involved in that feature :(

So the idea is that we can use WebActivator for now, and then we’ll make the ASP.NET attribute more flexible in a future release, at which point WebActivator will not be needed as a separate thing (that’s just a tentative plan, it’s not settled yet).  But until then, WebActivator does the job nicely, and using it is as easy as adding one dependency line in your .nuspec file, so we’re not in a bad place. :)

 

Update (2/15/2011): new switch to have method be called after global.asax App_Start

 

In some scenarios, the pre app start method gets called too early, as it happens before global.asax. In WebActivator 1.3, I added support for a new attribute that makes the method be called after global.asax's Application_Start. To use this, simply use PostApplicationStartMethod instead of PreApplicationStartMethod, e.g.

 

 [assembly: WebActivator.PostApplicationStartMethod(typeof(TestLibrary.MyStartupCode), "CallMeAfterAppStart")]

 

What this does is register a module (using this technique), and then call your method the first time an instance of the module's Init is called. Note that your method will only be called once even though the module can get instantiated multiple times. So the fact that it uses a module should mostly just be an implementation detail. What matters is that you method will get called later than Application_Start (but still very early in the appdomain start up sequence).

 

Other NuGets that use WebActivator

 

Here is the current list of NuGets that make use of this mechanism:

  • Spark.Web.Mvc3, as discussed here
  • Ninject.MVC3 uses it to register the Ninject DI Container with ASP.NET MVC (new MVC3 feature, see Brad Wilson’s post!)
  • EFCodeFirst.SqlServerCompact uses it to make the EF CTP4 bits use SQLCE 4
  • 51Degrees.mobi
  • AddMvc3ToWebForms
  • AttributeRouting
  • Combres
  • DataAnnotationsExtensions.MVC3
  • entile-server
  • FubuMVC
  • MefContrib.MVC3
  • NuGet.Server
  • RestfulRouting
  • SimpleMembership.Mvc3
  • StructureMap-MVC3
  • VirtualPathTemplates

And I’m sure more will come!  Pretty much any NuGet that needs to execute startup code should make use of this.

 

Feedback welcome

 

WebActivator is still very new, and I’d love to hear feedback about it from NuGet users.  You can leave a comment here, or start a discussion on https://nuget.codeplex.com/.  The latter is probably best as it makes it easier to have a discussion among many folks.