In May, Microsoft released version 1.0 of Web Application Projects, a much needed add-in for Visual Studio 2005. This article sets out why I strongly recommend you check them out if you're building and deploying VS 2005 / ASP.NET 2.0 web applications.
Ed: This article originally appeared on Rick's blog (and in Code Magazine). With Rick’s permission, I edited the piece, and summarized some of the discussion points. It serves as a useful follow-up to Rick's earlier ASP.NET 2.0 and VS 2005 - You win some, you lose some.
Web Application Projects provide a project model that more closely resembles the old but proven VS2003 projects and come in response to some fairly noisy criticism of the stock project model shipped with Visual Studio 2005. It addresses a number of issues in ASP.NET 2.0 and Visual Studio 2005 related to:
- Page compilation
- Project creation and management
- Project deployment
Many developers in the community ran into quirky behavior due to the complex new compilation model. Ironically, the new stock compilation model was supposed to make it easy to create and work with Web applications, and while it does make many things easier most of the time, the few things it doesn't do well can be major stumbling blocks.
Deployment is also affected by the compilation and project model and many developers ended up downright baffled by the messy deployment output generated by stock projects. The output contains a ton of randomly named files and you cannot create a repeatable installation unless you do an in-place install including source files.
In addition, there are project management issues because stock projects aren't real Visual Studio projects and some features that have always been available to projects like Build Actions, XML Documentation and MSBuild support, are simply not there.
To their credit, Microsoft took the issues raised quite seriously early on and have built a couple of powerful project template add-ins that address just about all of these concerns. Unfortunately these tools didn't make it into the shipping product of Visual Studio 2005 and they ended up getting released as add-ins that can be downloaded. The initial tool released was Web Deployment Projects, which addresses the deployment process by combing the jumble of files that the ASP.NET 2.0 compiler (ASPNET_COMPILER.EXE) creates, into a single assembly, along with the ability to create repeatable installs. Shortly thereafter a second tool called Web Application Projects introduced, which essentially brings back a full project model similar to VS2003, but with significant enhancements. Both tools were recently put out as V1.0 releases and both will be integrated into future refreshes and updates of Visual Studio starting with SP1 later this year. Neither of the products works with Visual Web Developer.
Stock ASP.NET 2.0 Projects - the good
You may be wondering what all the fuss is about. If stock ASP.NET 2.0 projects work well for you then there's no reason to switch or add any tools. However, I would argue that no matter what you do in terms of deployment (short of in-place installation with source code), Web Deployment Projects (WDP) will make your life easier. WAP is something that you need to worry about only if you've hit some of the snags of stock projects or you simply like the more standard approach that it takes to project management.
Stock ASP.NET 2.0 projects are nice in a lot of ways. One of the highlights of the new stock model is the ability to open a project using file-based projects that require no configuration. You simply point Visual Studio at a physical directory and open it up. Right-click on a page, and navigate View ¬ Browser, and the built in Web Server starts up and you're running the web application. Look Ma, no hands!
You can also make changes to the code of a page, save the page and run it in the browser without having to recompile it. I know, I know – no compilation? Blasphemy! But I have to admit that I have got quite used to not compiling my stock projects and simply running pages after changes. This scenario is even nicer if you have the debugger attached to your web application. You can now make changes to your code while the debugger is attached, refresh the page and see the refreshed code in the debugger. No stopping the debugger, no restarting of the app – this can be a huge timesaver.
The final improvement in the compilation model comes if you are using CodeBeside pages – the new partial class model eliminates all of the control definition from the source code of your classes and instead generates them at runtime. There's no code clutter of control and event definitions as there was in ASP.NET 1.1 CodeBehind pages in InitializeComponent, and no longer are there issues with Visual Studio mucking up the control definitions or event hooks because that task has been offloaded to ASP.NET at compile time.
All of this makes ASP.NET 2.0 easier to get started with than ASP.NET 1.1. Making ASP.NET more approachable is surely the main reason why Microsoft went through the trouble of cooking up a whole new compilation model; and they partially succeeded...
Stock ASP.NET 2.0 Projects – a few problems
When I first started using ASP.NET 2.0 Web Projects I was pretty excited. Indeed, it did seem much easier to work in 2.0. In particular, the Edit and Go functionality, along with the ability to simply open a project, seemed nice, especially as I was checking out a lot of sample projects at the time. I started using 2.0 early on in the beta cycle, and like most people who used these early versions I was just trying to understand the many changes by creating small samples and pages – and ASP.NET 2.0 and the stock project model worked fantastically well for that. I never really got around to a final compilation of my samples, for deployment, and most if not all of my work was with single pages that didn’t have external dependencies or complex page class hierarchies. Life seemed good…
Fast forward to the final betas. By now I was getting more familiar with ASP.NET 2.0 and decided to port one of my more complex applications to 2.0. The migration didn't go very smoothly. The migration tool missed many of the pages that were inherited from custom Page classes or pages that referenced CodeBehind classes of other pages. At this point, some of the differences between the CodeBehind and CodeBeside models became clearer and it took me quite a bit of time and experimentation to understand just what you could and could no longer do when dealing with page inheritance. And where do you put files? Can I leave it in my Web directory or do I have to put it in APP_CODE? Will ASP.NET generate my controls or not?
Most of these issues involved ASPX pages that used inherited Page classes. I had a number of pages that used a CodeBehind class to serve a number ASPX pages. This is not easily done in 2.0, unless you create your own control definitions at the base class level (i.e. a fair amount of manual work), or resort to using FindControl() in the base class. It took a lot of trial and error to figure out exactly what you can and can't do with page inheritance – things that worked just fine and logically in the previous version of ASP.NET. The process is not easy and although I now have a pretty good understanding of how the page model works, I think it's horribly confusing and riddled with inconsistencies and workaround implementation details that developers should not have to deal with. CodeBehindBaseClass, anyone?
At the time I assumed that these problems were arising because I was upgrading, and I hadn't initially done things "the ASP.NET 2.0 way". I figured that if I started from scratch, things should be smoother. Unfortunately, as I moved on to develop new projects I ran into these same quirks over and over again. In most cases I was able to work around the issues, but because of the complexity of the model was a major time drain. Ever try to create two Master Pages and inherit one of them from the CodeBehind class of the first? It can be done but it's not very obvious. Need to load a user control dynamically and can't have a @Register tag on the page?
Simple tasks like this shouldn't require any special directives, or require you to resort to Reflection or FindControl() semantics in order to get a proper reference to a type. The model of ASP.NET 1.1 was simple and elegant and inheritance worked the way you expect it to, whereas the 2.0 model seems too clever and full of workaround hacks and directives to force things to work a certain way. If you are doing cross page/control referencing I bet you have run into these issues and wasted a fair amount of time working trying to find the right combination of directives and inheritance to make it work. It shouldn't be this hard!
The next real shock was deployment. When the time came to deploy to a staging site, I was stunned by the number of the choices that had to be made. Should I precompile? Should I include the ASPX files in the pre-compilation? Should I use fixed names, or force assemblies to be created for each directory? And the deployment tools built into VS.NET 2005 are pretty lame as they don't even give access to all the options available on the ASP.NET compiler. If you want all the choices you have to use the command line compiler and figure out which one of the 20 or so compilation combinations is the one you need. I got so frustrated with the options I ended up building a front end GUI tool for the ASPNET_COMPILER.EXE, which let me experiment a little more easily with the options.
It took me a couple days of experimentation and reflection on the concepts to find a model that I could work with and that I could use to manage my site reasonably efficiently. Reasonably… the process was far from optimal and involved deleting all existing files in the BIN directory (50 files just for the web app plus the satellite assemblies) then recopying all of those files into the BIN directory. The worst part though, was that the install was not repeatable – every build created in the BIN directory a different set of deployment files with unique names. The only way to manage deployment was to delete all the files and copy them back up even if I had only made a single, tiny little change.
This model also makes updating your site in real-time a problem. There are many binary files that need to get updated, and while this is happening the site becomes unstable and can result in errors being produced on a live site. ASP.NET 2.0's workaround solution? Use a placeholder file while the update is in progress – riiiight.
Long story short, over time, and after much teeth-gnashing, I've become very disillusioned with stock Web Projects for real project work. I still prefer stock projects for samples and simple projects, but for anything critical I've moved on to Web Application Projects.
Luckily Microsoft heard these and other serious complaints (no build actions, source control limitations) from other developers and responded with Web Deployment Projects and Web Applications Projects.
Web Deployment Projects
To address these deployment issues, Microsoft shipped a pre-release version of the Web Deployment Projects (WDP) add-in almost simultaneously with the release of Visual Studio 2005. Since then WDP has been released as Version 1.0. WDP addresses only compilation issues and it works in combination with stock projects. It can also be used with Web Application Projects to provide for pre-compilation of ASPX pages in those projects (more on this later).
WDP gets added to an existing solution that includes a Web project. You add a new WDP and this new project manages the publishing of your project to a specific directory. It works in a similar way to the Publish feature, but provides a number of useful enhancements. WDP uses the same compilation as native ASP.NET 2.0 and then post-processes the output generated by the stock compiler.
The key feature of the tool is that its ability to create a single assembly of the mess of files that stock projects create. It still doesn't create just a single file – there are still .compiled marker files for each page, if you chose to precompile the ASPX pages as well.
But unlike stock projects, all the files generated use fixed hash values for file names, so the installs generated are fully repeatable. For example, if you compile to a single assembly the single assembly will always have the same name, and each of the .compiled files for the ASPX pages will have the same name as for the previous build.
This means that now, when you deploy, you can usually update the single assembly and leave the other files alone. You'd only need to update the .compiled files if you add new files to the project. So if there needs to be a minor fix after deploying to the live site, you can now update a single file.
A single assembly also means that you are updating a single binary file on the server, which makes it possible again to do an in-place update of the application without shutting down the server and without seeing error messages while the assembly is being updated. No more out-of-sync assemblies on the server.
Because WDP is a real Visual Studio project, it is a straight MSBUILD file and can be triggered for automated builds (which was one additional complaint with stock projects). There are also a number of useful features in WDP that allow you to replace sections in web.config with content from external files. This is useful, because your config settings for deployment are surely going to be different from what they are in your development environment. You can also assign version information to the created assembly– previously the best you could do was store a AssemblyInfo.cs file in your APP_CODE folder, which would give a version to the generated APP_CODE assembly only.
If you plan on using stock projects with ASP.NET and you plan to deploy a pre-compiled Web site, I highly recommend you combine it with Web Deployment projects to create workable installs.
Web Application Projects
To address the main logistical issues with stock projects, Microsoft has provided Web Application Projects (WAP). WAP basically takes us back to the compilation model that Visual Studio 2003 used, and adds some important enhancements and full support for all ASP.NET 2.0 features. WAP brings back the CodeBehind compilation model, where all CodeBehind code gets compiled by Visual Studio into a single assembly in the BIN directory. When you build your projects you get a fully compiled, single assembly and an in-place installation that is fully self-contained.
WAP is provided as a Visual Studio Project template. To create a new project you Create a new Web Application Project which creates a familiar .csproj or .vbproj file. Unlike VS2003 though, the new project doesn't use FrontPage extensions, but rather accesses the files directly from disk, so it's much faster. The project is a full Visual Studio style project with all the features you'd expect from a project including XmlComments, versioning, compiler switches, build actions etc. As any VS.NET project, it's also an MSBUILD script so you can customize as needed with all the functionality MSBUILD allows.
If you already have a stock ASP.NET 2.0, the conversion to WAP isn't completely automatic, but it can be done fairly quickly and you can find a useful tutorial here:
The converter runs through your project and fixes up each of the ASPX/ASCX/MASTER pages, updates the CodeFile tags to CodeBehind, and adds the new .designer file with the control definitions. I ran into a few issues with control assemblies in the BIN directory not being found so it might be a very good idea to compile your existing project under stock projects just before doing a conversion to ensure that all the required dependencies exist in the BIN directory.
WAP works like VS2003 projects in that it uses the project file to manage the files that it can see, rather than simply looking on disk and blindly pulling everything in the directory into the project, WAP only holds what you add to the project. So any files you want in the project need to be added, even if they exist on disk. You can also exclude files to remove them – another feature that is missing from stock projects.
The key WAP feature in regards to compilation is that it moves control code generation of the ASP.NET page back into the designer – WAP controls and pages contain three files that make up each ASPX/ASCX/MASTER page (C# files shown here):
- The aspx/ascx/master markup file
- The .aspx.cs file that contains your CodeBehind code
- The .aspx.cs.designer file that contains the control definition
If you've worked with WinForms 2.0, then you're already familiar with this model, whereby your code goes into one file (the plain .cs file) and the designer generated code goes in the other (.cs.designer). The generated code consists merely of the control definitions for the page that give the base class the proper signature. This is an important shift because, with this mechanism, the class name is known at original compile time and the class is available in the current, single assembly of all CodeBehind code, so Page and Control classes can be easily referenced across the application.
Visual Studio now compiles these two classes together into the base class. The result of this is that the code generation for the control definitions is no longer delayed until runtime and ASP.NET doesn't need to generate a unique classname for the base class. In fact, this class compiles into a single assembly for all CodeBehind classes. Any page class can access any Page or Control or Master class via its CodeBehind type, as you'd expect because the type now exists at initial compile time.
This simple change solves the problem of cross page referencing of controls and pages, the various inheritance related quirks, and it also makes for a much more speedy compilation process. Compilation of a moderately-complex project with 50+ pages takes about one second with WAP vs. 20-25 seconds with stock projects. If you compile a stock project the process is excruciatingly slow, because ASP.NET actually compiles the entire project including the ASPX files and copies it out to a deployment folder. With WAP a compile only compiles the CodeBehind code – there's no ASPX parsing and the compilation is in-place and results in a single assembly that is placed in the BIN directory.
WAP also supports Edit and Continue, which was a feature squeezed in at the very last minute. Edit and Continue give you some semblance of the Debug and Go functionality of stock projects. Truthfully though, I'm not a big fan of Edit and Continue; the fact that all but the active source file are locked and inaccessible for editing really is more hassle to me than it's worth. But if you're already using Edit and Continue in WinForms or library projects, you can now expect similar behavior with Web applications. Again, consistency is a virtue.
Web Application Projects and Web Deployment Projects in combination
On the downside, WAP requires compilation of any change made, so you can't simply execute pages after a change. You can get familiar once again Ctrl-Shift-B to build your project. Fortunately, the compile is fast.
You can no longer get error detection inside of ASPX pages with WAP alone, because the compiler only compiles your CodeBehind classes and not the markup code, so errors there don't show up in the Error display. However, if you still want this useful feature, you can use WAP in combination with Web Deployment Projects and precompile your markup pages. WDP compiles the markup pages and can also flag the errors in its compilation Error display. This compile is slower, like stock projects, but it's a great tool to run at least from time to time to ensure that your expression markup is valid in markup pages.
Web Application Projects are for me
I've spent a fair amount of time now both with stock projects and WAP and I can tell you that I feel much more comfortable and productive with WAP, than with stock projects. Everything just works as you would expect and I've not run into any issues or things that don't work. It's a predictable and proven model that is based on standard .NET principles that anyone who can look at the generated output can understand easily.
If you are building reasonably complex projects I think you too will find that this model is a better fit to the way most developers work, even though the model is more rigid. Most importantly it's consistent with the rest of Visual Studio, and not an oddball project type and an oddball compilation model with many intricate clever tricks to make it work.
There are doubtlessly some of you who are happy with stock projects too, and if they are working for you, and, unlike me, you are not running into the boundary cases I mention above, by all means stick with stock projects. They are less rigid to work with and the projects are more portable. If I build any sort of demo application or samples, it surely will be done using stock projects.
The good news is that should you run into a problem with stock projects that you can't solve you can now easily switch to WAP if necessary. Even with some of the compiler issues I had in my conversion I ended up doing the 50+page project in about an hour. It is also possible to go back to stock projects although this is a manual process (you can find out more here: http://west-wind.com/weblog/posts/5571.aspx) although I suspect there will be little need to go this direction.
I think that Microsoft did a bang-up job in getting these tools out to solve the problems with stock projects, as they became apparent. Both WAP and WDP were very publicly discussed and Microsoft listened very carefully to community input during the development cycle. Many features were added after the intial releases came out and public community input was made available. Each new release during the preview cycle brought out many of these new changes very quickly. It's good to see this kind of flexibility at Microsoft after dealing with the slow-as-molasses monster releases of Visual Studio .NET during the betas.
It would have been even better if these tools had made it in the box, but given the timeframe of release that wasn't possible. As it stands these tools will become part of Visual Studio 2005 SP1 later this year and future releases.
Note though that neither of these tools work with Visual Web Developer currently, and I don't believe there are any plans to integrate them into updates in the future either. VWD developers will be stuck with stock projects, which is probably OK given the scope of VWD.