Published: 03 May 2010
By: Dustin Davis
Download Sample Code

A lot of people asked me why I would ever want to store views in a separate assembly. This article is going to answer that question with a CMS example.

Contents [hide]

The Goal

Our goal is to create an assembly that contains controllers, views and even routes that we can share among any ASP.NET MVC project. What your specific reasons for this need are, I don't know. Mine were because I wanted to build a CMS using the MVC framework and I didn't want to redo any code or recreate any views to accomplish this task.

Setup

I assume you will be using Visual Studio 2008 and already have the MVC framework V2 installed and ready to go.

CMS Library

We'll begin by creating a new blank solution. Call it 'MVCCMS'. Next, add a new class library project and name it 'CMS'. You can remove the Class1.cs if you like, we won't be using it. Add the following references

  • System.Web.Mvc
  • System.Web.Routing

Next, add the following folders

  • Controllers
  • Views
  • Views\Blog
  • Models
  • Core
  • Data

MVC Web Site

Add a new ASP.NET MVC 2 Web Application project and name it 'MySite'. Add a reference to the CMS project.

BlogController

We're going to be creating a single controller in this example to keep things simple, especially since the process for adding other controllers will be exactly the same.

Add a new class file to your CMS project under the Controllers folder. Name it 'BlogController.cs'. Inside, add the following code

Listing 1: BlogController.cs

You will need to add the following using references

We're going to keep this controller very simple. We have an index for displaying a list of blog entries and a ViewBlogEntry which we will be working with later in the routing section.

Next, add a new class file to the Models folder and name it 'BlogEntry.cs'. Add the following code

Listing 2: BlogEntry.cs

This is our very simple model for the blog entries. Since we have a model, we might as well build a data repository. For this example, we'll use a dummy class to return dummy data. Sorry, no database stuff in this article.

Add a new class file to the Data folder and name it 'BlogRepository.cs'. Add the following code

Listing 3: BlogRepository.cs

You will need to add the following references

Our repository will create some dummy data and allow us to do some operations with it. Nothing special. We can wire this up by changing the Index action on our blog controller to the following

Listing 4: BlogController.cs

Add the following using references

Now that we have our model and repository and our controller is wired up, All we need is a view.

Add View

Before we begin, you should know that since we've created a class library, there isn't any support for MVC Views in our project and that also means no intellisense when working with views. The easiest way to do this whole process is to start out with an MVC site and move the controllers and views over to the class library project after you've created them.

Since we don't have support for views, add a new HTML Page and name it 'Index.aspx'. Add the following code

Listing 5: Index.aspx

You might be wondering about the link we added for the entry titles. We'll get to that later. If we were to access this page right now, we would receive a yellow screen of death with the following message

Could not load type:

'System.Web.Mvc.ViewPage<System.Collections.Generic.List<CMS.Models.BlogEntry>>'

To resolve this, add a web.config to the Views folder with the following contents

Listing 6: Web.config

This allows us to use generic view page types.

View Engine

In order for the MVC framework to use our views, it has to be able to find them. The solution that I came up with was to use a custom view engine. The default view engine that MVC starts with is the 'WebFormViewEngine' and it is sufficient for most projects so we won't change it, just building from it. If you take a look at the source code for the MVC framework the top portion of the class for WebFormViewEngine will look like the following

Listing 7: WebFormViewEngine.cs (MVC Source code)

Get the idea? The WebFormViewEngine is setting the paths that tell MVC where to look for views when they are requested. We're going to piggy back off of this. Add a new class to the Core folder and name it 'CMSViewEngine.cs' and add the following code

Listing 8: CMSViewEngine.cs

Add the following references

All we're doing is overwriting the locations for the master page and view locations with paths that reflect where our views will be. Notice that the paths are almost identical to the paths specified in the WebFormViewEngine code. That's because they are with the exception of 'bin/' added to the path.

Why Bin, why?!

Here is an issue that I would like to see a better solution to, but I have not yet found one. In order for the views in our CMS project to be available and runtime, they must be copied to the referencing projects output folder. Unless you wish to do this manually, you have to specifically tell each view to copy to output. This option forces the files to go into the bin folder.

For each view, right click and select properties. Change the 'Copy to Output Directory' option to 'Copy Always'. The will ensure that the files are always put in the output when the referencing project is built. You will also want to do this for the Web.config.

Injection

We have all of the elements that we need ready to go. But how do we inject our CMS controllers and views into the referencing project for consumption? This doesn't happen automatically so we need to write some more code. Add a new class file to the root of the project and name it 'CMSSetup.cs' and add the following code

Listing 9: CMSSetup.cs

Add the following references

There are two steps involved in injecting our controllers and views into the referencing project. The first is telling MVC where to find controllers and the second is where to find the views. We tell MVC where to look for our controllers by adding a namespace which is where our controllers sit 'CMS.Controllers'. MVC will use reflection to find the controllers in that namespace.

For the views, we don't specifically tell it where to find them. We only register our view engine with MVC. MVC will use each view engine until it can find the view it wants. When the default WebformViewEngine can't find it, our view engine will be used which of course knows where to look.

To make these steps simpler for the consumers, we simply wrap them in a single method call. Don't worry about the SetupRoutes method for now.

Hooking CMS to MySite

Hooking CMS up is easy. Open the Global.asax file in the MySite project. You will see two methods, 'RegisterRoutes' and 'Application_Start'. We are going to call the setup in the Application_Start method. Modify the method to match the following

Listing 10: Global.asax

Add the following references

All that needed to be done was to make a call to our setup method. Everything is now ready to go.

Test drive

Go ahead and build 'MySite' and then run it. You should see the default home page that was added when you created the MVC project. In the address bar, add '/Blog' and press enter

Listing 11: Example Url

Viola! You should see the index page with a list of blog entries. You didn't add anything but a single line of code to the MVC project to get this working.

If you see a yellow screen stating that the views could not be found, make sure that the views were copied to the output folder. If they weren't then make sure that you set the 'Copy to Output Directory' to 'Copy always', rebuild and try again.

If you are getting a yellow screen stating that it could not load type 'System.Collections.Generic.List<CMS.Models.BlogEntry>' then make sure that the 'Copy to Output Directory' to 'Copy Always' for the Web.config in the Views folder, rebuild and try again.

Go ahead and click on one of the links. You'll get an error.

Routing

We don't yet have code for viewing a blog entry. Open the BlogController.cs and modify the ViewBlogEntry method to match the following

Listing 12: BlogController.cs

Add a new HTML Page to the Views/Blog folder and name it 'ViewBlogEntry.aspx' then add the following code

Listing 13: ViewBlogEntry.aspx

To access a blog entry, all we have to do is call the '/Blog/ViewBlogEntry/{id}' url and we'd be done. But that is pretty boring and not very seo friendly. Open the CMSSetup.cs file in the CMS project and change the SetupRoutes method to the following

Listing 14: CMSSetup.cs

Our route now allows us to access a blog entry not only in the default way but now also '/{Title}/Blog/{id}'

Listing 15: Example Url

This makes for an seo friendly url and it looks better too. But we still have to register our routes with MVC. Open the Global.asax file from the MySite project and modify the RegisterRoutes method to match the following

Listing 16: Global.asax

All we did was make a call to the SetupRoutes method, passing in the route collection so we can add our own routes to it.

Rebuild and then run the MySite project and browse to '/Blog'. Go ahead and click on one of the title links. You will be taken to the blog entry using our new route.

Master Pages & Security

There are some issues that still need to be covered such as master pages and attributes that specify role permissions. We'll cover these topics and a few others in the next installment.

Summary

Whatever your reasons are for needing to access controllers and views in a separate assemblies, you now have a way to do it. With 2 simple lines of code you can hook up your library and have it ready for use. I would love to hear any suggestions on how to improve this solution.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Dustin Davis

Dustin is a Microsoft C# MVP, Pluralsight author and producer of c0deporn.

This author has published 3 articles on DotNetSlackers. View other articles or the complete profile here.

Other articles in this category


Code First Approach using Entity Framework 4.1, Inversion of Control, Unity Framework, Repository and Unit of Work Patterns, and MVC3 Razor View
A detailed introduction about the code first approach using Entity Framework 4.1, Inversion of Contr...
jQuery Mobile ListView
In this article, we're going to look at what JQuery Mobile uses to represent lists, and how capable ...
Exception Handling and .Net (A practical approach)
Error Handling has always been crucial for an application in a number of ways. It may affect the exe...
JQuery Mobile Widgets Overview
An overview of widgets in jQuery Mobile.
Book Review: SignalR: Real-time Application Development
A book review of SignalR by Simone.

You might also be interested in the following related blog posts


ASP.NET MVC2 Preview 2: Areas and Routes read more
MvcContrib working on Portable Areas read more
SQL 2008 CLR Triggers, use a .NET class library in SQL using WPF read more
New class: ASP.NET MVC Boot Camp developer training read more
Getting started with the ASP.NET MVC framework (excerpt from ASP.NET MVC in Action) read more
The New ASP.NET Framework read more
Move the ViewState off the client and cache it on the server read more
Top
 
 
 

Discussion


Subject Author Date
placeholder Great article! Andrew Siemer 5/5/2010 12:03 PM
Missing line in Listing 8? Steve Henke 5/5/2010 5:16 PM
placeholder RE: Missing line in Listing 8? Dustin Davis 5/5/2010 5:38 PM
Nice article Patrick Smith 5/6/2010 12:10 PM
placeholder Storing ASP.NET MVC Controllers & Views in separate assemblies Anurag Pal 5/8/2010 4:02 AM
RE: Storing ASP.NET MVC Controllers & Views in separate assemblies Dustin Davis 5/8/2010 12:25 PM
placeholder Errors in listing 3? Rory McCrossan 6/28/2010 5:10 PM
RE: Errors in listing 3? Rory McCrossan 6/28/2010 5:15 PM
placeholder RE: RE: Errors in listing 3? Rory McCrossan 6/28/2010 6:15 PM
RE: RE: RE: Errors in listing 3? Dustin Davis 6/28/2010 6:38 PM
placeholder RE: RE: Errors in listing 3? Dustin Davis 6/28/2010 5:39 PM
NOTE: Article errors Dustin Davis 6/28/2010 5:44 PM
placeholder javascript libs Ricky Patnaik 7/9/2014 7:24 PM
javascript libs Ricky Patnaik 7/9/2014 7:26 PM

Please login to rate or to leave a comment.