Friday, February 25, 2011

App_Start folder convention for NuGet and WebActivator

[Please see the WebActivator wiki for the latest docs]

When I first blogged about WebActivator, I showed in my example using a source file named AppStart_SparkMvc.cs.pp under the Content folder in the package, which means when you install it you end up with a file named AppStart_SparkWebMvc.cs at the root of your web project.

Now suppose you install a few more packages that use the same WebActivator pattern, and you would end up with something like that at the root of your project:

AppStart_SparkWebMvc.cs
AppStart_SQLCEEntityFramework.cs
AppStart_BarPackage.cs
AppStart_BlahPackage.cs
Global.asax
Global.asax.cs
Web.config
More files...

That starts getting really ugly, and most devs like to keep the root of their app free of clutter.

We need a better convention!

The solution is simply to agree on a different convention where we put all this startup code into a folder. To match ASP.NET conventions, the obvious name to pick is App_Start. And once we do that, we no longer need to prefix the file names with AppStart, so we would have:

App_Start
SparkWebMvc.cs
SQLCEEntityFramework.cs
BarPackage.cs
BlahPackage.cs
Global.asax
Global.asax.cs
Web.config
More files...

Likewise, the full class names would change from WebApplication1.AppStart_SQLCEEntityFramework to WebApplication1.App_Start.SQLCEEntityFramework. Note that the namespace doesn’t matter a whole lot since you won’t call this code explicitly. But since existing convention is to have the namespace match the folder structure, we may as well do that here.

As of today, there are 17 packages that use WebActivator, so I’ll need to try to convince all the authors to follow this. Fun time ahead! :)

But note that it’s just a convention, with no code changes to enforce it. Nothing written here breaks any existing packages. It’s just something where by agreeing on a better convention, we make NuGet yet a little bit better!

An example: EFCodeFirst.SqlServerCompact

As an example, here is what I ended up with for the EFCodeFirst.SqlServerCompact package using this pattern.

The source file transform in the package is in Content\App_Start\SQLCEEntityFramework.cs.pp, and contains:

// namespaces, etc...

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

namespace $rootnamespace$.App_Start {
public static class SQLCEEntityFramework {

//etc...
Note the use of $rootnamespace$ and of App_Start in the namespace.

8 comments:

  1. I like it. How about taking it a step further and making the method name conventional and you could get rid of the attribute for classes in the App_Start folder.

    ReplyDelete
  2. @joshf: I was kind of thinking about that, but my concern is that it could effect startup times, as we would need to look at all the methods in all classes in all assemblies for methods matching the pattern. With the attribute, we get to do a very quick assembly level check.

    ReplyDelete
  3. @david - even if you don't lose the attribute, having the convention as @joshf proposes would help with readability... thus the methods might be:

    * PreStart
    * PostStart
    * Shutdown

    ReplyDelete
  4. @James: Problem is that we don’t know what’s in App_Start at runtime. All we have is a set of assemblies in bin, with no trace of what sources they came from. So I like the idea, but the implementation is tricky :)

    ReplyDelete
  5. Hi David,

    Either I've missed something, or you've got the wrong end of what I was trying to say...

    If the guidance is to use a convention of PreStart or PostStart for the method name, then this will be more readable to the end user.

    It would still necessitate the attribute, but might make the experience nicer for the consumer!

    Cheers,

    James

    ReplyDelete
  6. @James: Ah sorry, didn't read your comment carefully. I suppose what we could do is have the method name be optional, and default to those names. This way we don't break existing code that use it.

    ReplyDelete
  7. This comment has been removed by the author.

    ReplyDelete
  8. Hi David,
    Would it be detrimental to place classes in the app_start folder in the namespace Company.Website.AppStart to follow the PascalCode naming convention as suggested by Microsoft, even though the folder name is App_Start?

    ReplyDelete