Logo

Maarten Balliauw {blog}

ASP.NET, ASP.NET MVC, Windows Azure, PHP, ...

About the author

Maarten Balliauw is currently employed as a Technical Evangelist at JetBrains. His interests are mainly web applications developed in ASP.NET (C#) or PHP and the Windows Azure cloud platform.
More about me More about me
Send mail E-mail me


ASP.NET MVC Quickly Pro NuGet Subscribe to my RSS feed Follow me on Twitter! View Maarten Balliauw's profile on LinkedIn
Maarten Balliauw - MVP - Most Valuable Professional
Maarten Balliauw - ASPInsider

Search

Archive

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright Maarten Balliauw 2013


Viva, Las Vegas!

Vote your MIX sessionI have asked it last year, and I’ll ask it again. One of my session proposals made it to the “short”list for MIX11. One thing left though: votes are the only currency to get my session proposal in Vegas.

Here’s the session abstract:

Fun with ASP.NET MVC 3 and MEF

So you have a team of developers? And a nice architecture to build on? How about making that architecture easy for everyone and getting developers up to speed quickly? Learn all about integrating the managed extensibility framework and ASP.NET MVC for creating loosely coupled, easy to use architectures that anyone can grasp.

If you think this session deserves a place in Vegas, please cast your vote right here. And while you are at it, feel free to vote for both of my direct colleagues Kevin Dockx and Sandrino Di Mattia as well.

PS: No, I will not steal Mike Tyson’s tiger.


Categories: ASP.NET | C# | Events | General | ICT | jQuery | MEF | MVC | Personal | Presentations

Writing an Orchard widget: LatestTwitter

Last week, Microsoft released Orchard, a new modular CMS system built on ASP.NET MVC and a lot of other, open source libraries available. I will not dive into the CMS itself, but after fiddling around with it I found a lot of things missing: there are only 40 modules and widgets available at the moment and the only way to have a more rich ecosystem of modules is: contributing!

And that’s what I did. Feel the need to add a list of recent tweets by a certain user to your Orchard website? Try my LatestTwitter widget. Here’s a screenshot of the widget in action:

Orchard LatestTwitter widget

And here’s what the admin side looks like:

Orchard LatestTwitter widget admin

It supports:

  • Displaying a number of tweets for a certain user
  • Specifying the number of tweets
  • Caching the tweets for a configurable amount of minutes
  • Specifying if you want to display the avatar image and post time

In this blog post, I’ll give you some pointers on how to create your own widget for Orchard. Download the code if you want to follow step by step: LatestTwitter.zip (1.56 mb)

Setting up your development environment

This one is probably the easy part. Fire up the Web Platform Installer and install WebMatrix and the Orchard CMS to your machine. Why WebMatrix? Well, it’s the new cool kid on the block and you don’t want to load the complete Orchard website in your Visual Studio later on. I think WebMatrix is the way to go for this situation.

That’s it. Your local site should be up and running. It’s best to test the site and do some initial configuration. And another tip: make a backup of this initial site, it’s very easy to screw up later on (if you, like me, start fooling Orchard’s versioning system). In WebMatrix, you’ll find the path to where your site is located:

WebMatrix Orchard

Creating the blueprints for your widget

I’ll be quick on this one, if you need the full-blown details refer to Creating a module on the Orchard website.

Fire up a command prompt. “cd” to the root of your site, e.g. “C:\USB\_werk\Projects\AZUG\Azure User Group Belgium”. Execute the command “bin\orchard.exe”. After a few seconds, you’ll be in the command-line interface for Orchard. First of all, enable the code generation module, by executing the command:

1 feature enable Orchard.CodeGeneration

This module makes it easier to create new modules, widgets and themes. You can do all of that manually, but why go that route if this route allows you to be lazy? Let’s create the blueprints for our module:

1 codegen module LatestTwitter

There’s a new Visual Studio project waiting for you on your file system, in my case at “C:\USB\_werk\Projects\AZUG\Azure User Group Belgium\Modules\LatestTwitter”. Easy, no?

Building the widget

In order to build a widget, you need:

  • A model for your widget “part”
  • A record in which this can be stored
  • A database table in which the record can be stored

Let’s start top down: model first. The model that I’m talking about is not an ASP.NET MVC “View Model”, it’s really the domain object you are working with in the rest of your widget’s back-end. I will be doing something bad here: I’ll just expose the domain model to the ASP.NET MVC view later on, for sake of simplicity and because it’s only one small class I’m using. Here’s how my TwitterWidgetPart model is coded:

1 public class TwitterWidgetPart : ContentPart<TwitterWidgetRecord> 2 { 3 [Required] 4 public string Username 5 { 6 get { return Record.Username; } 7 set { Record.Username = value; } 8 } 9 10 [Required] 11 [DefaultValue("5")] 12 [DisplayName("Number of Tweets to display")] 13 public int Count 14 { 15 get { return Record.Count; } 16 set { Record.Count = value; } 17 } 18 19 [Required] 20 [DefaultValue("5")] 21 [DisplayName("Time to cache Tweets (in minutes)")] 22 public int CacheMinutes 23 { 24 get { return Record.CacheMinutes; } 25 set { Record.CacheMinutes = value; } 26 } 27 28 public bool ShowAvatars 29 { 30 get { return Record.ShowAvatars; } 31 set { Record.ShowAvatars = value; } 32 } 33 34 public bool ShowTimestamps 35 { 36 get { return Record.ShowTimestamps; } 37 set { Record.ShowTimestamps = value; } 38 } 39 }

Just some properties that represent my widget’s settings. Do note that these all depend on a TwitterWidgetRecord, which is the persistency class used by Orchard. I’ll give you the code for that one as well:

1 public class TwitterWidgetRecord : ContentPartRecord 2 { 3 public virtual string Username { get; set; } 4 public virtual int Count { get; set; } 5 public virtual int CacheMinutes { get; set; } 6 public virtual bool ShowAvatars { get; set; } 7 public virtual bool ShowTimestamps { get; set; } 8 }

See these “virtual” properties everywere? Ever worked with NHibernate and have a feeling that this *may* just be similar? Well, it is! Orchard uses NHibernate below the covers. Reason for these virtuals is that a proxy for your class instance will be created on the fly, overriding your properties with persistence specific actions.

The last thing we need is a database table. This is done in a “migration” class, a class that is responsible for telling Orchard what your widget needs in terms of storage, content types and such. Return to your command prompt and run the following:

1 codegen datamigration LatestTwitter

A file called “Migrations.cs” will be created in your module’s directory. Just add it to your solution and have a look at it. The Create() method you see is called initially when your module is installed. It creates a database table to hold your TwitterWidgetRecord.

Note that once you have an install base of your widget, never tamper with this code again or people may get stuck upgrading your widget over time. Been there, done that during development and it’s no fun at all…

Because I started small, my Migrations.cs file looks a bit different:

1 public class Migrations : DataMigrationImpl { 2 public int Create() { 3 // Creating table TwitterWidgetRecord 4 SchemaBuilder.CreateTable("TwitterWidgetRecord", table => table 5 .ContentPartRecord() 6 .Column("Username", DbType.String) 7 .Column("Count", DbType.Int32) 8 ); 9 10 ContentDefinitionManager.AlterPartDefinition(typeof(TwitterWidgetPart).Name, 11 builder => builder.Attachable()); 12 13 return 1; 14 } 15 16 public int UpdateFrom1() 17 { 18 ContentDefinitionManager.AlterTypeDefinition("TwitterWidget", cfg => cfg 19 .WithPart("TwitterWidgetPart") 20 .WithPart("WidgetPart") 21 .WithPart("CommonPart") 22 .WithSetting("Stereotype", "Widget")); 23 24 return 2; 25 } 26 27 public int UpdateFrom2() 28 { 29 SchemaBuilder.AlterTable("TwitterWidgetRecord", table => table 30 .AddColumn("CacheMinutes", DbType.Int32) 31 ); 32 33 return 3; 34 } 35 36 public int UpdateFrom3() 37 { 38 SchemaBuilder.AlterTable("TwitterWidgetRecord", table => table 39 .AddColumn("ShowAvatars", DbType.Boolean) 40 ); 41 SchemaBuilder.AlterTable("TwitterWidgetRecord", table => table 42 .AddColumn("ShowTimestamps", DbType.Boolean) 43 ); 44 45 return 4; 46 } 47 }

You see these UpdateFromX() methods? These are “upgrades” to your module. Whenever ou deploy a new version to the Orchard Gallery and someone updates the widget in their Orchard site, these methods will be used to upgrade the database schema and other things, if needed. Because I started small, I have some upgrades there already…

The UpdateFrom1() is actually a required one (although I could have done this in the Create() method as well): I’m telling Orchard that my TwitterWidget is a new content type, that it contains a TwitterWidgetPart, is a WidgetPart and can be typed as a Widget. A lot of text, but basically I’m just telling Orchard to treat my TwitterWidgetPart as a widget rather than anything else.

Drivers and handlers

We need a handler. It is a type comparable with ASP.NET MVC’s filters and is executed whenever content containing your widget is requested. Why do we need a handler? Easy: we need to tell Orchard that we’re actually making use of a persitence store for our widget. Here’s the code:

1 public class TwitterWidgetRecordHandler : ContentHandler 2 { 3 public TwitterWidgetRecordHandler(IRepository<TwitterWidgetRecord> repository) 4 { 5 Filters.Add(StorageFilter.For(repository)); 6 } 7 }

There’s really no magic to this: it’s just telling Orchard to use a repository fo accessing TwitterWidgetRecord data.

Next, we need a driver. This is something that you can compare with an ASP.NET MVC controller. It’s used by Orchard to render administrative views, handle posts from the admin interface, … Here’s the code:

1 public class TwitterWidgetDriver 2 : ContentPartDriver<TwitterWidgetPart> 3 { 4 protected ITweetRetrievalService TweetRetrievalService { get; private set; } 5 6 public TwitterWidgetDriver(ITweetRetrievalService tweetRetrievalService) 7 { 8 this.TweetRetrievalService = tweetRetrievalService; 9 } 10 11 // GET 12 protected override DriverResult Display( 13 TwitterWidgetPart part, string displayType, dynamic shapeHelper) 14 { 15 return ContentShape("Parts_TwitterWidget", 16 () => shapeHelper.Parts_TwitterWidget( 17 Username: part.Username ?? "", 18 Tweets: TweetRetrievalService.GetTweetsFor(part), 19 ShowAvatars: part.ShowAvatars, 20 ShowTimestamps: part.ShowTimestamps)); 21 } 22 23 // GET 24 protected override DriverResult Editor(TwitterWidgetPart part, dynamic shapeHelper) 25 { 26 return ContentShape("Parts_TwitterWidget_Edit", 27 () => shapeHelper.EditorTemplate( 28 TemplateName: "Parts/TwitterWidget", 29 Model: part, 30 Prefix: Prefix)); 31 } 32 33 // POST 34 protected override DriverResult Editor( 35 TwitterWidgetPart part, IUpdateModel updater, dynamic shapeHelper) 36 { 37 updater.TryUpdateModel(part, Prefix, null, null); 38 return Editor(part, shapeHelper); 39 } 40 }

What you see is a Display() method, used for really rendering my widget on the Orchard based website. What I do there is building a dynamic model consisting of the username, the list of tweets and some of the options that I have configured. There’s a view for this one as well, located in Views/Parts/TwitterWidget.cshtml:

1 <ul class="latest-twitter-list"> 2 @foreach (var tweet in Model.Tweets) { 3 <text><!-- ... --></text> 4 } 5 </ul>

The above is the actual view rendered on the page where you place the LatestTwitter widget. Note: don’t specify the @model here or it will crash. Simple because the model passed in to this view is nothing you’d expect: it’s a dynamic object.

Next, there’s the two Editor() implementations, one to render the “settings” and one to persist them. Prettyu standard code which you can just duplicate from any tutorial on Orchard modules. The view for this one is in Views/EditorTemplates/Parts/TwitterWidget.cshtml:

1 @model LatestTwitter.Models.TwitterWidgetPart 2 3 <fieldset> 4 <legend>Latest Twitter</legend> 5 6 <div class="editor-label"> 7 @T("Twitter username"): 8 </div> 9 <div class="editor-field"> 10 @@@Html.TextBoxFor(model => model.Username) 11 @Html.ValidationMessageFor(model => model.Username) 12 </div> 13 14 <!-- ... --> 15 </fieldset> 16

Done! Or not? Wel, there’s still some logic left: querying Twitter and making sure we don’t whistle for the fail whale to come over by querying it too often.

Implementing ITweetRetrievalService

Being prepared for change is injecting dependencies rather than hard-coding them. I’ve created a ITweetRetrievalService interface responsible for querying Twitter. The implementation will be injected by Orchard’s dependency injection infrastructure later on. Here’s the code:

1 public interface ITweetRetrievalService 2 : IDependency 3 { 4 List<TweetModel> GetTweetsFor(TwitterWidgetPart part); 5 }

See the IDependency interface I’m inheriting? That’s the way to tell Orchard to look for an implementation of this interface at runtime. Who said dependency injection was hard?

Next, the implementation. Let’s first look at the code:

1 [UsedImplicitly] 2 public class CachedTweetRetrievalService 3 : ITweetRetrievalService 4 { 5 protected readonly string CacheKeyPrefix = "B74EDE32-86E4-4A58-850B-016E6F595CF9_"; 6 7 protected ICacheManager CacheManager { get; private set; } 8 protected ISignals Signals { get; private set; } 9 protected Timer Timer { get; private set; } 10 11 public CachedTweetRetrievalService(ICacheManager cacheManager, ISignals signals) 12 { 13 this.CacheManager = cacheManager; 14 this.Signals = signals; 15 } 16 17 public List<TweetModel> GetTweetsFor(TwitterWidgetPart part) 18 { 19 // Build cache key 20 var cacheKey = CacheKeyPrefix + part.Username; 21 22 return CacheManager.Get(cacheKey, ctx => 23 { 24 ctx.Monitor(Signals.When(cacheKey)); 25 Timer = new Timer(t => Signals.Trigger(cacheKey), part, TimeSpan.FromMinutes(part.CacheMinutes), TimeSpan.FromMilliseconds(-1)); 26 return RetrieveTweetsFromTwitterFor(part); 27 }); 28 } 29 30 protected List<TweetModel> RetrieveTweetsFromTwitterFor(TwitterWidgetPart part) 31 { 32 // ... query Twitter here ... 33 } 34 35 protected string ToFriendlyDate(DateTime sourcedate) 36 { 37 // ... convert DateTime to "1 hour ago" ... 38 } 39 }

I’ll leave the part wher I actually query Twitter for you to discover. I only want to focus on two little things here: caching and signaling. The constructor of the CachedTweetRetrievalService is accepting two parameters that will be injected at runtime: an ICacheManager used for caching the tweet list for a certain amount of time, and an ISignals which is used to fire messages through Orchard. In order to cache the list of tweets, I will have to combine both. Here’s the caching part:

1 // Build cache key 2 var cacheKey = CacheKeyPrefix + part.Username; 3 4 return CacheManager.Get(cacheKey, ctx => 5 { 6 ctx.Monitor(Signals.When(cacheKey)); 7 Timer = new Timer(t => Signals.Trigger(cacheKey), part, TimeSpan.FromMinutes(part.CacheMinutes), TimeSpan.FromMilliseconds(-1)); 8 return RetrieveTweetsFromTwitterFor(part); 9 });

First, I’m building a cache key to uniquely identify the data for this particular widget’s Twitter stream by just basing it on the Twitter username. Next, I’m asking the cachemanager to get the data with that particular cacheKey. No data available? Well, in that case our lambda will be executed: a monitor is added for a signal with my cache key. Sounds complicated? I’m just telling Orchard to monitor for a particular message that can be triggered, and once it’s triggered, the cache will automatically expire.

I’m also starting a new timer thread, which I just ask to send a signal through the application at a specific point in time: the moment where I want my cache to expire. And last but not least: data is returned.

Conclusion

To be honest: I have had to read quite some tutorials to get this up and running. But once you get the architecture and how components interact, Orchard is pretty sweet to develop against. And all I’m asking you now: go write some modules and widgets, and make Orchard a rich platform with a rich module ecosystem.

Want to explore my code? Here’s the download: LatestTwitter.zip (1.56 mb)
Want to install the widget in your app? Just look for “LatestTwitter” in the modules.


Categories: ASP.NET | C# | General | ICT | MVC | Orchard

ASP.NET MVC and jQuery Mobile

jQuery MobileWith the release of Windows Phone 7 last year, I’m really interested in mobile applications. Why? Well, developing for Windows Phone 7 did not require me to learn new things. I can use my current skill set and build cool apps for that platform. But what about the other platforms? If you look at all platforms from a web developer perspective, there’s one library that also allows you to use your existing skill set: jQuery Mobile.

Know HTML? Know jQuery? Know *any* web development language like PHP, RoR or ASP.NET (MVC)? Go ahead and build great looking mobile web apps!

I’ll give you a very short tutorial, just enough to sparkle some interest. After that, it’s up to you.

Getting jQuery Mobile running in ASP.NET MVC

This one is easy. Start a new project and strip out anything you don’t need. After that, modify your master page to something like this:

1 <%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %> 2 3 <!DOCTYPE html> 4 <html> 5 <head runat="server"> 6 <title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title> 7 <link href="../../Content/Site.css" rel="stylesheet" type="text/css" /> 8 9 <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.css" /> 10 <script src="http://code.jquery.com/jquery-1.4.4.min.js"></script> 11 <script src="http://code.jquery.com/mobile/1.0a2/jquery.mobile-1.0a2.min.js"></script> 12 </head> 13 14 <body> 15 <asp:ContentPlaceHolder ID="MainContent" runat="server" /> 16 </body> 17 </html> 18

That’s it: you download all resources from jQuery’s CDN. Optionally, you can also download and host jQuery Mobile on your own server.

Creating a first page

Pages have their own specifics. If you look at the docs, a page typically consists of a div element with a HTML5 data attribute “data-role” “page”. These data attributes are used for anything you would like to accomplish, which means your PC or device needs a HTML5 compatible browser to render jQuery Mobile content. Here’s a simple page (using ASP.NET MVC):

1 <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<RealDolmenMobile.Web.Models.ListPostsModel>" %> 2 3 <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> 4 Page title 5 </asp:Content> 6 7 <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> 8 <div data-role="page"> 9 10 <div data-role="header"> 11 <h1>Title here</h1> 12 </div> 13 14 <div data-role="content"> 15 <p>Contents here</p> 16 </div> 17 18 <div data-role="footer"> 19 <h4>Footer here</h4> 20 </div> 21 </div> 22 </asp:Content>

Building a RSS reader

I’ve been working on a simple sample which formats our RealDolmen blogs into jQuery Mobile UI. Using Argotic as the RSS back-end, this was quite easy to do. First of all, here’s a HomeController that creates a list of posts in a view model. MVC like you’re used to work with:

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Web; 5 using System.Web.Mvc; 6 using Argotic.Syndication; 7 using RealDolmenMobile.Web.Models; 8 using System.Web.Caching; 9 10 namespace RealDolmenMobile.Web.Controllers 11 { 12 [HandleError] 13 public class HomeController : Controller 14 { 15 private static readonly Uri Feed = new Uri("http://microsoft.realdolmenblogs.com/Syndication.axd"); 16 17 public ActionResult Index() 18 { 19 // Create model 20 var model = new ListPostsModel(); 21 22 GenericSyndicationFeed feed = GenericSyndicationFeed.Create(Feed); 23 foreach (GenericSyndicationItem item in feed.Items) 24 { 25 model.Posts.Add(new PostModel 26 { 27 Title = item.Title, 28 Body = item.Summary, 29 PublishedOn = item.PublishedOn 30 }); 31 } 32 33 return View(model); 34 } 35 }

Next, we need to render this. Again, pure HTML goodness that you’re used working with:

1 <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<RealDolmenMobile.Web.Models.ListPostsModel>" %> 2 3 <asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"> 4 RealDolmen Blogs 5 </asp:Content> 6 7 <asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"> 8 <div data-role="page"> 9 10 <div data-role="header"> 11 <h1>RealDolmen Blogs</h1> 12 </div> 13 14 <div data-role="content"> 15 <ul data-role="listview"> 16 <li data-role="list-divider">Posts by employees<span class="ui-li-count"><%:Model.Posts.Count()%></span></li> 17 <% 18 foreach (var post in Model.Posts) 19 { 20 %> 21 <li> 22 <h3><%:Html.ActionLink(post.Title, "Post", new { title = post.Title })%></h3> 23 <p class="ui-li-aside">Published: <%:post.PublishedOn.ToString()%></p> 24 </li> 25 <% 26 } 27 %> 28 </ul> 29 </div> 30 31 <div data-role="footer"> 32 <h4>&nbsp;</h4> 33 </div> 34 </div> 35 </asp:Content>

The result? Not very stunning when looked at with IE8… But fire up Chrome or any other HTML5 capable browser, and here’s what you get:

RealDolmen Blogs Mobile

I’ve asked some people to check http://rd.cloudapp.net (may be offline by the time you read this), and I’ve received confirmation that it looks good on iSomething-devices, a Nokia and some Opera Mobile versions. Nice!

Goodies

The above example may not be that spectacular. The framework does hold some spectacular things! Think dialogs, forms, gestures, animations and a full-blown navigation framework that replaces any form or hyperlink with an AJAX call that is executed in the back-end, displays a nice “loading” screen and automatically generates a “back” button for you.

More examples? Check the manual itself over at http://jquerymobile.com/demos/1.0a2/docs/: this has been built using jQuery Mobile and looks nice!

Conclusion

It’s great! Really, I can just go ahead and build cool mobile web sites / web apps. Unfortunately, the WIndows-market of devices has bad support (due to a lack of HTML 5 support on their devices). This should get fixed in a coming upgrade, but untill then you will not have any luck running these apps on Windows Phone 7… For a complete list of compatible browsers and platforms, check the compatibility matrix.

For those interested, I’ve uploaded my small test app here: RealDolmenMobile.zip (420.38 kb) (note that I've built this as a Windows Azure solution)


Categories: ASP.NET | C# | General | ICT | JavaScript | MVC

Tools for the lazy: Templify and NuGet

In this blog post, I will cover two interesting tools that, when combined, can bring great value and speed at the beginning of any new software project that has to meet standards that are to be re-used for every project. The tools? Templify and NuGet.

You know the drill. Starting off with a new project usually consists of boring, repetitive tasks, often enforced by (good!) practices defined by the company you work for (or by yourself for that company). To give you an example of a project I’ve recently done:

  1. Create a new ASP.NET MVC application in Visual Studio
  2. Add 2 new projects: <project>.ViewModels and <project>.Controllers
  3. Do some juggling by moving classes into the right project and setting up the correct references between these projects

Maybe you are planning to use jQuery UI?

  1. Add the required JavaScript and CSS files to the project.

Oh right and what was that class you needed to work with MEF inside ASP.NET MVC? Let’s add that one as well:

  • Add the class for that
  • Add a reference to System.ComponentModel.Composition to the project

Admit it: these tasks are boring, time consuming and boring. Oh and time consuming. And boring. What if there were tools to automate a lot of this? And when I say a lot, I mean a LOT! Meet Templify and NuGet.

Introduction to Templify and NuGet

Well, let me leave this part to others. Let’s just do the following as an introduction: Templify is a tool that automates solution setup for Visual Studio in a super simple manner. It does not give you a lot of options, but that’s OK. Too much options are always bad. Want to read more on Templify? Check Howard van Rooijen’s introductory post.

NuGet (the package manager formerly known as NuPack) is a package manager for Visual Studio. It’s simple and powerful. Check Scott Hanselman’s excellent introduction post on this.

Scenario

Let’s go with the scenario I started my blog post with. You want to automate the boring tasks that are required at every project start. Here’s a simple one, usually it’s even more.

  1. Create a new ASP.NET MVC application in Visual Studio
  2. Add 2 new projects: <project>.ViewModels and <project>.Controllers
  3. Do some juggling by moving classes into the right project and setting up the correct references between these projects

Oh right and what was that class you needed to work with MEF inside ASP.NET MVC? Let’s add that one as well:

  • Add the class for that
  • Add a reference to System.ComponentModel.Composition to the project

Let’s automate the first part using Templify and the second part using NuGet.

Creating the Templify package

I have some bad news for you: you’ll have to take all project setup steps one more time! Create a new solution with a common name, e.g. “templateproject”. Add project references, library references, anything you need for this project to be the ideal base solution for any new project. Here’s an overview of what I am talking about:

Create new Templify project

Next, close Visual Studio and browse to the solution’s root folder location. After installing Templify, a new contect-menu item will be available: “Templify this folder”. Click it!

Templify this folder

After clicking it, a simple screen will be presented, asking you for 4 simple things: Name, Token, Author and Version. Easy! Name is the name for the package. Token is the part of the project name / namespace name / whatever you want to have replace with the next project’s common name. In my case, this will be “templateproject”. Author and version are easy as well.

Templify main screen

Click “Templify”, and behold! Nothing seems to have happened! Except for a small notification in your systray area. But don’t fear: a package has been created for your project and you can now execute the first steps of the scenario described above.

Templify package created

That’s basically it. If you want to redistribute your Templify package, check the C:\Users\%USERNAME%\AppData\Roaming\Endjin\Templify\repo  folder for that.

Creating a NuGet package

For starters, you will need the nuget.exe command-line utility. If that prerequisite is on your machine, you are already half-way. And to be honest: if you read the documentation over at the NuGet CodePlex project page you are there all the way! But I’ll give you a short how-to. First, create a folder structure like this:

  • content (optional)
  • lib (optional)
  • <your package name>.nuspec

In the content folder, simply put anything you would like to add into the project. ASP.NET MVC Views, source code, anything. In the lib folder, add all assembly references tatshould be added.

Next, edit the <your package name>.nuspec file and add relevant details for your package:

1 <?xml version="1.0"?> 2 <package xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 4 <metadata xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> 5 <id>MefDependencyResolver</id> 6 <version>0.0.1</version> 7 <authors>Maarten Balliauw</authors> 8 <requireLicenseAcceptance>false</requireLicenseAcceptance> 9 <description>MefDependencyResolver</description> 10 <summary>MefDependencyResolver</summary> 11 <language>en-US</language> 12 </metadata> 13 </package>

Once that’s done, simply call nuget.exe like so: nuget pack MefDependencyResolver\mefdependencyresolver.nuspec
Note that this can also be done using an MSBUILD command in any project.

If NuGet is finished, a new package should be available, in my above situation the MefDependencyResolver.0.0.1.nupkg file is generated.

Creating a NuGet feed

This one’s easy. You can use an OData feed (see here and here), but what’s even easier: just copy all packages to a folder or network share and point Visual Studio there. Fire up those Visual Studio settings, find the Package Manager node and add your local or network package folder:

Creating a NuGet feed

Done!

Behold! A new project!

So you took all the effort in creating a Templify and NuGet package. Good! Here’s how you can benefit. Whenever a new project should be started, open op an Explorer window, create a new folder, right-click it and select “Templify here”. Fill out the name of the new project (I chose “ProjectCool” because that implies I’m working on a cool project and cool projects are fun!). Select the template to deploy. Next, click “Deploy template”.

Templify Deploy Template

Open up the folder you just created and behold: “ProjectCool” has been created and my first 3 boring tasks are now gone. If I don’t tell my boss I have this tool, I can actually sleep for the rest of the day and pretend I have done this manually!

ProjectCool has been Templified!

Next, open up “ProjectCool” in Visual Studio. Right-click the ASP.NET MVC project and select “Add library package reference…”.

Add library package reference

Select the feed you just created and simply pick the packages to install into this application. Need a specific set of DiaplayTemplates? Create a package for those. Need the company CSS styles for complex web applications? Create a package for that! Need jQuery UI? Create a package for that!

Install NuGet package

Conclusion

I’m totally going for this approach! It speeds up initial project creation without the overhead of maintaining automation packages and such. Using simple tooling that is easy to understand, anyone on your project team can take this approach, both for company-wide Templify and NuGet packages, as well as individual packages.

Personally, I would like to see these two products combined into one, in the scenario outlined here. However I would already be happy if I could also create a company-wide “Templify” feed, ideally integrated with the NuGet tooling.

For fun and leasure, I packaged everything I created in this blog post: TemplifyNuGet.zip (508.23 kb)


Categories: ASP.NET | C# | General | ICT | MVC | Projects | Software

MvcSiteMapProvider 2.2.0 released

I’m proud to announce that MvcSiteMapProvider 2.2.0 has just been uploaded to CodePlex. It should also be available through NuPack in the coming hours. This release has taken a while, but that’s because I’ve been making some important changes...

MvcSiteMapProvider is, as the name implies, an ASP.NET MVC SiteMapProvider implementation for the ASP.NET MVC framework. Targeted at ASP.NET MVC 2, it provides sitemap XML functionality and interoperability with the classic ASP.NET sitemap controls, like the SiteMapPath control for rendering breadcrumbs and the Menu control.

In this post, I’ll give you a short update on what has changed as well as some examples on how to use newly introduced functionality.

Changes in MvcSiteMapProvider 2.2.0

  • Increased stability
  • HtmlHelpers upgraded to return MvcHtmlString
  • Templated HtmlHelpers have been introduced
  • New extensibility point: OnBeforeAddNode and OnAfterAddNode
  • Optimized sitemap XML for search engine indexing

Templated HtmlHelpers

The MvcSiteMapProvider provides different HtmlHelper extension methods which you can use to generate SiteMap-specific HTML code on your ASP.NET MVC views like a menu, a breadcrumb (sitemap path), a sitemap or just the current node’s title.

All helpers in MvcSiteMapProvider are make use of templates: whenever a node in a menu is to be rendered, a specific partial view is used to render this node. This is based on the idea of templated helpers. The default templates that are used can be found on the downloads page. Locate them under the Views/DisplayTemplates folder of your project to be able to customize them.

When creating your own templates for MvcSiteMapProvider's helpers, the following model objects are used and can be templated:

  • Html.MvcSiteMap().Menu() uses:
    MvcSiteMapProvider.Web.Html.Models.MenuHelperModel and MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
  • Html.MvcSiteMap().SiteMap() uses:
    MvcSiteMapProvider.Web.Html.Models.SiteMapHelperModel and MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
  • Html.MvcSiteMap().SiteMapPath() uses:
    MvcSiteMapProvider.Web.Html.Models.SiteMapPathHelperModel and MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel
  • Html.MvcSiteMap().SiteMapTitle()
    MvcSiteMapProvider.Web.Html.Models.Situses:eMapTitleHelperModel

The following template is an example for rendering a sitemap node represented by the MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel model.

1 <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<MvcSiteMapProvider.Web.Html.Models.SiteMapNodeModel>" %> 2 <%@ Import Namespace="System.Web.Mvc.Html" %> 3 4 <% if (Model.IsCurrentNode && Model.SourceMetadata["HtmlHelper"].ToString() != "MvcSiteMapProvider.Web.Html.MenuHelper") { %> 5 <%=Model.Title %> 6 <% } else if (Model.IsClickable) { %> 7 <a href="<%=Model.Url %>"><%=Model.Title %></a> 8 <% } else { %> 9 <%=Model.Title %> 10 <% } %>

New extensibility point

In the previous release, a number of extensibility points have been introduced. I blogged about them before. A newly introduced extensibility point is the ISiteMapProviderEventHandler . A class implementing MvcSiteMapProvider.Extensibility.ISiteMapProviderEventHandler can be registered to handle specific events, such as when adding a SiteMapNode.

Here’s an example to log all the nodes that are being added to an MVC sitemap:

1 public class MySiteMapProviderEventHandler : ISiteMapProviderEventHandler 2 { 3 public bool OnAddingSiteMapNode(SiteMapProviderEventContext context) 4 { 5 // Should the node be added? Well yes! 6 return true; 7 } 8 9 public void OnAddedSiteMapNode(SiteMapProviderEventContext context) 10 { 11 Trace.Write("Node added: " + context.CurrentNode.Title); 12 } 13 }

Optimized sitemap XML for SEO

Generating a search-engine friendly list of all nodes in a sitemap was already possible. This functionality has been vastly improved with two new features:

  • Whenever a client sends an HTTP request header with Accept-encoding set to a value of gzip or deflate, the XmlSiteMapResult class (which is also used internally in the XmlSiteMapController) will automatically compress the sitemap using GZip compression.
  • Whenever a sitemap exceeds 50.000 nodes, the XmlSiteMapController will automatically split your sitemap into a sitemap index file (sitemap.xml) which references sub-sitemaps (sitemap-1.xml, sitemap-2.xml etc.) as described on http://www.sitemaps.org/protocol.php.

For example, if a website contains more than 50.000 nodes, the sitemap XML that is generated will look similar to the following:

1 <?xml version="1.0" encoding="utf-8" ?> 2 <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> 3 <sitemap> 4 <loc>http://localhost:1397/sitemap-1.xml</loc> 5 </sitemap> 6 <sitemap> 7 <loc>http://localhost:1397/sitemap-2.xml</loc> 8 </sitemap> 9 </sitemapindex>

This sitemap index links to sub-sitemap files where all nodes are included.


Categories: ASP.NET | C# | General | ICT | MVC | Projects

Using MvcSiteMapProvider throuh NuPack

NuPackProbably you have seen the buzz around NuPack, a package manager for .NET with thight integration in Visual Studio 2010. NuPack is a free, open source developer focused package management system for the .NET platform intent on simplifying the process of incorporating third party libraries into a .NET application during development. If you download and install NuPack into Visual Studio, you can now reference MvcSiteMapProvider with a few simple clicks!

From within your ASP.NET MVC 2 project, right click the project file and use the new “Add Package Reference…” option.

Add package reference

Next, a nice dialog shows up where you can just pick a package and click “Install” to download it and add the necessary references to your project. The packages are retrieved from a central XML feed, but feel free to add a reference to a directory where your corporate packages are stored and install them through NuPack. Anyway: MvcSiteMapProvider. Just look for it in the list and click “Install”.

MvcSiteMapProvider in NuPack

Next, MvcSiteMapProvider will automatically be downloaded, added as an assembly reference, a default Mvc.sitemap file is added to your project and all configuration in Web.config takes place without having to do anything! I’m sold :-)

Disclaimer for some: I’m not saying NuPack is the best package manager out there nor that it is the best original idea ever invented. I do believe that the tight integration in VS2010 will make NuPack a good friend during development: the process of downloading and including third party components in your application becomes frictionless. That’s the aim for NuPack, and also the reason why I believe this tool matters and will matter a lot!


Categories: ASP.NET | C# | General | ICT | MVC | Projects | Software

Remix 2010 slides and sample code

As promised during my session on Remix 10 yesterday in Belgium, here's the slide deck and sample code.

Building for the cloud: integrating an application on Windows Azure

Abstract: “It’s time to take advantage of the cloud! In this session Maarten builds further on the application created during Gill Cleeren’s Silverlight session. The campaign website that was developed in Silverlight 4 still needs a home. Because the campaign will only run for a short period of time, the company chose for cloud computing on the Windows Azure platform. Learn how to leverage flexible hosting with automated scaling on Windows Azure, combined with the power of a cloud hosted SQL Azure database to create a cost-effective and responsive web application.”

Thanks for joining and bearing with me during this tough session with very sparse bandwidth!

Source code used in the session: TDD.ChristmasCreator.zip (686.86 kb)


BlogEngine.NET comment spam filtering

SpamIt’s been a month or three since I was utterly fed up with comment spam on my blog. Sure, I did turn on comment moderation so you, as a visitor, would not notice this spam if I did not approve it as a valid comment. However, I found myself cleaning up comment spam from in between legitimate comments in the BlogEngine.NET admin interface.

In an effort of trying to reduce comment spam, I tried the following:

  • Close comments after 90 days – This effort worked for a few days, but afterwards I was just seeing more comment spam on the topics that were still open to comments.
  • Use a CAPTCHA – This effort reduced some comment spam, but not all. Which makes me believe there are people actually making a living by just sending out comment spam and filling out CAPTCHA’s out there.
  • Whining and cursing while again cleaning out comments manually – This effort worked, until I found out that this was what I’ve been doing before the other 2 efforts. Back to start…

Luckily, the latest version of BlogEngine.NET (and also earlier version if you go down the hacky road) featured a new comment system, including spam filtering. After using it for a few months, I must say I’m very close to zero comment spam!

The results

I have configured BlogEngine.NET as follows:

  • Comments enabled, never closed
  • Comment moderation: “on” and “automatic”
  • Whitelisting rules enabled (if you have 5 legitimate comments, you are probably OK)
  • Spam filters enabled: AkismetFilter, StopForumSpam and TypePadFilter

Now if you look at the results, there’s an interesting difference between the spam filter services being used:

image

The accuracy of the spam filters is mostly > 90%, for Akismet it’s even 97.30 %. Which I also feel: a small check every week on whether there are spam filter mistakes is quite enough. Only the TypePadFilter is letting me down there, and I will probably disable this one and rely on only two filters.


MvcSiteMapProvider 2.1.0 released!

MvcSiteMapProvider The release for MvcSiteMapProvider 2.1.0 has just been posted on CodePlex. MvcSiteMapProvider is, as the name implies, an ASP.NET MVC SiteMapProvider implementation for the ASP.NET MVC framework. Targeted at ASP.NET MVC 2, it provides sitemap XML functionality and interoperability with the classic ASP.NET sitemap controls, like the SiteMapPath control for rendering breadcrumbs and the Menu control.

Next to a brand new logo, the component has been patched up with several bugfixes, the visibility attribute is back (in a slightly cooler reincarnation) and a number of new extension points have been introduced. Let’s give you a quick overview…

kick it on DotNetKicks.com

Extension points

MvcSiteMapProvider is built wih extensibility in mind. All extension point contracts are defined in the MvcSiteMapProvider.Extensibility namespace. The sample application on the downloads page contains several custom implementations of these extension points.

Global extension points (valid for the entire provider and all nodes)

These extension points can be defined when Registering the provider.

  Node key generator

Keys for sitemap nodes are usually automatically generated by the MvcSiteMapProvider core. If, for reasons of accessing sitemap nodes from code, the generated keys should follow other naming rules, a custom MvcSiteMapProvider.Extensibility.INodeKeyGenerator implementation can be written.

  Controller type resolver

In order to resolve a controller type and action method related to a specific sitemap node, a MvcSiteMapProvider.Extensibility.IControllerTypeResolver is used. This should normally not be extended, however if you want to make use of other systems for resolving controller types and action methods, this is the logical extension point.

  Action method parameter resolver

Action method parameters are resolved by using ASP.NET MVC's ActionDescriptor class. If you want to use a custom system for this, a MvcSiteMapProvider.Extensibility.IActionMethodParameterResolver implementation can be specified.

  ACL module

To determine whether a sitemap node is accessible to a specific user, a MvcSiteMapProvider.Extensibility.IAclModule implementation is used. MvcSiteMapProvider uses two of these modules by default: access is granted or denied by checking for [Authorize] attributes on action methods, followed by the roles attribute that can be specified in the sitemap XML.

  URL resolver

URLs are generated by leveraging a MvcSiteMapProvider.Extensibility.ISiteMapNodeUrlResolver implementation. If, for example, you want all URLs generated by MvcSiteMapProvider to be in lowercase text, a custom implementation can be created.

  Visibility provider

In some situations, nodes should be visible in the breadcrumb trail but not in a complete sitemap. This can be solved using the MvcSiteMapProvider.Extensibility.ISiteMapNodeVisibilityProvider extension point that can be specified globally for every node in the sitemap or granularly on a specific sitemap node. A sample is available on the Advanced node visibility page.

Node-specific extenion points (valid for a single node)

These extension points can be defined when Creating a first sitemap.

  Dynamic node provider

In many web applications, sitemap nodes are directly related to content in a persistent store like a database.For example, in an e-commerce application, a list of product details pages in the sitemap maps directly to the list of products in the database. Using dynamic sitemaps, a small class implementing MvcSiteMapProvider.Extensibility.IDynamicNodeProvider or extending MvcSiteMapProvider.Extensibility.DynamicNodeProviderBase can be provided to the MvcSiteMapProvider offering a list of dynamic nodes that should be incldued in the sitemap. This ensures the product pages do not have to be specified by hand in the sitemap XML.

A sample can be found on the Dynamic sitemaps page.

  URL resolver

URLs are generated by leveraging a MvcSiteMapProvider.Extensibility.ISiteMapNodeUrlResolver implementation. If, for example, you want the URL for a sitemap node generated by MvcSiteMapProvider to be in lowercase text, a custom implementation can be created.

  Visibility provider

In some situations, nodes should be visible in the breadcrumb trail but not in a complete sitemap. This can be solved using the MvcSiteMapProvider.Extensibility.ISiteMapNodeVisibilityProvider extension point that can be specified globally for every node in the sitemap or granularly on a specific sitemap node. A sample is available on the Advanced node visibility page.

Conclusion

Only one conclusion: grab the latest bits and start playing with them! And feel free to bug me with feature requests and issues found.

Also, follow me on Twitter for updates on this project.


Categories: ASP.NET | C# | General | MVC | Projects

ASP.NET MVC 3 and MEF sitting in a tree...

As I stated in a previous blog post: ASP.NET MVC 3 preview 1 has been released! I talked about some of the new features and promised to do a blog post in the dependency injection part. In this post, I'll show you how to use that together with MEF.

Download my sample code: Mvc3WithMEF.zip (256.21 kb)

kick it on DotNetKicks.com

Dependency injection in ASP.NET MVC 3

First of all, there’s 4 new hooks for injecting dependencies:

  • When creating controller factories
  • When creating controllers
  • When creating views (might be interesting!)
  • When using action filters

In ASP.NET MVC 2, only one of these hooks was used for dependency injection: a controller factory was implemented, using a dependency injection framework under the covers. I did this once, creating a controller factory that wired up MEF and made sure everything in the application was composed through a MEF container. That is, everything that is a controller or part thereof. No easy options for DI-ing things like action filters or views…

ASP.NET MVC 3 shuffled the cards a bit. ASP.NET MVC 3 now contains and uses the Common Service Locator’s IServiceLocator interface, which is used for resolving services required by the ASP.NET MVC framework. The IServiceLocator implementation should be registered in Global.asax using just one line of code:

MvcServiceLocator.SetCurrent(new SomeServiceLocator());

This is, since ASP.NET MVC 3 preview 1, the only thing required to make DI work. In controllers, in action filters and in views. Cool, eh?

Leveraging MEF with ASP.NET MVC 3

First of all: a disclaimer. I already did posts on MEF and ASP.NET MVC before, and in all these posts, I required you to explicitly export your controller types for composition. In this example, again, I will require that, just for keeping code a bit easier to understand. Do note that are some variants of a convention based registration model available.

As stated before, the only thing to build here is a MefServiceLocator that is suited for web (which means: an application-wide catalog and a per-request container). I’ll still have to create my own controller factory as well, because otherwise I would not be able to dynamically compose my controllers. Here goes…

Implementing ServiceLocatorControllerFactory

Starting in reverse, but this thing is the simple part :-)

[Export(typeof(IControllerFactory))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class ServiceLocatorControllerFactory
    : DefaultControllerFactory
{
    private IMvcServiceLocator serviceLocator;

    [ImportingConstructor]
    public ServiceLocatorControllerFactory(IMvcServiceLocator serviceLocator)
    {
        this.serviceLocator = serviceLocator;
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controllerType = GetControllerType(requestContext, controllerName);
        if (controllerType != null)
        {
            return this.serviceLocator.GetInstance(controllerType) as IController;
        }

        return base.CreateController(requestContext, controllerName);
    }

    public override void ReleaseController(IController controller)
    {
        this.serviceLocator.Release(controller);
    }
}

Did you see that? A simple, MEF enabled controller factory that uses an IMvcServiceLocator. This thing can be used with other service locators as well.

Implementing MefServiceLocator

Like I said, this is the most important part, allowing us to use MEF for resolving almost any component in the ASP.NET MVC pipeline. Here’s my take on that:

[Export(typeof(IMvcServiceLocator))]
[PartCreationPolicy(CreationPolicy.Shared)]
public class MefServiceLocator
    : IMvcServiceLocator
{
    const string HttpContextKey = "__MefServiceLocator_Container";

    private ComposablePartCatalog catalog;
    private IMvcServiceLocator defaultLocator;

    [ImportingConstructor]
    public MefServiceLocator()
    {
        // Get the catalog from the MvcServiceLocator.
        // This is a bit dirty, but currently
        // the only way to ensure one application-wide catalog
        // and a per-request container.
        MefServiceLocator mefServiceLocator = MvcServiceLocator.Current as MefServiceLocator;
        if (mefServiceLocator != null)
        {
            this.catalog = mefServiceLocator.catalog;
        }

        // And the fallback locator...
        this.defaultLocator = MvcServiceLocator.Default;
    }

    public MefServiceLocator(ComposablePartCatalog catalog)
        : this(catalog, MvcServiceLocator.Default)
    {
    }

    public MefServiceLocator(ComposablePartCatalog catalog, IMvcServiceLocator defaultLocator)
    {
        this.catalog = catalog;
        this.defaultLocator = defaultLocator;
    }

    protected CompositionContainer Container
    {
        get
        {
            if (!HttpContext.Current.Items.Contains(HttpContextKey))
            {
                HttpContext.Current.Items.Add(HttpContextKey, new CompositionContainer(catalog));
            }

            return (CompositionContainer)HttpContext.Current.Items[HttpContextKey];
        }
    }

    private object Resolve(Type serviceType, string key = null)
    {
        var exports = this.Container.GetExports(serviceType, null, null);
        if (exports.Any())
        {
            return exports.First().Value;
        }

        var instance = defaultLocator.GetInstance(serviceType, key);
        if (instance != null)
        {
            return instance;
        }

        throw new ActivationException(string.Format("Could not resolve service type {0}.", serviceType.FullName));
    }

    private IEnumerable<object> ResolveAll(Type serviceType)
    {
        var exports = this.Container.GetExports(serviceType, null, null);
        if (exports.Any())
        {
            return exports.Select(e => e.Value).AsEnumerable();
        }

        var instances = defaultLocator.GetAllInstances(serviceType);
        if (instances != null)
        {
            return instances;
        }

        throw new ActivationException(string.Format("Could not resolve service type {0}.", serviceType.FullName));
    }

    #region IMvcServiceLocator Members

    public void Release(object instance)
    {
        var export = instance as Lazy<object>;
        if (export != null)
        {
            this.Container.ReleaseExport(export);
        }

        defaultLocator.Release(export);
    }

    #endregion

    #region IServiceLocator Members

    public IEnumerable<object> GetAllInstances(Type serviceType)
    {
        return ResolveAll(serviceType);
    }

    public IEnumerable<TService> GetAllInstances<TService>()
    {
        var instances = ResolveAll(typeof(TService));
        foreach (TService instance in instances)
        {
            yield return (TService)instance;
        }
    }

    public TService GetInstance<TService>(string key)
    {
        return (TService)Resolve(typeof(TService), key);
    }

    public object GetInstance(Type serviceType)
    {
        return Resolve(serviceType);
    }

    public object GetInstance(Type serviceType, string key)
    {
        return Resolve(serviceType, key);
    }

    public TService GetInstance<TService>()
    {
        return (TService)Resolve(typeof(TService));
    }

    #endregion

    #region IServiceProvider Members

    public object GetService(Type serviceType)
    {
        return Resolve(serviceType);
    }

    #endregion
}

HOLY SCHMOLEY! That is a lot of code. Let’s break it down…

First of all, I have 3 constructors. 2 for convenience, one for MEF. Since the MefServiceLocator will be instantiated in Global.asax and I only want one instance of it to live in the application, I have to do a dirty trick: whenever MEF wants to create a new MefServiceLocator for some reason (should in theory only happen once per request, but I want this thing to live application-wide), I’m giving it indeed a new instance which at least shares the part catalog with the one I originally created. Don’t shoot me for doing this…

Next, you will also notice that I’m using a “fallback” locator, which in theory will be the instance stored in MvcServiceLocator.Default, which is ASP.NET MVC 3’s default MvcServiceLocator. I’m doing this for a reason though… read my disclaimer again: I stated that everything should be decorated with the [Export] attribute when I’m relying on MEF. Now since the services exposed by ASP.NET MVC 3, like the IFilterProvider, are not decorated with this attribute, MEF will not be able to find those. When I find myself in that situation, the MefServiceLocator is simply asking the default service locator for it. Not a beauty, but it works and makes my life easy.

Wiring things

To wire this thing, all it takes is adding 3 lines of code to my Global.asax. For clarity, I’m giving you my entire Global.asax Application_Start method:

protected void Application_Start()
{
    // Register areas

    AreaRegistration.RegisterAllAreas();

    // Register filters and routes

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    // Register MEF catalogs

    var catalog = new DirectoryCatalog(
        Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
    MvcServiceLocator.SetCurrent(new MefServiceLocator(catalog, MvcServiceLocator.Default));
}

Can you spot the 3 lines of code? This is really all it takes to make the complete application use MEF where appropriate. (Ok, that is a bit of a lie since you would still have to implement a very small IFilterProvider if you want MEF in your action filters, but still.)

Hooks

The cool thing is: a lot of things are now requested in the service locator we just created. When browsing to my site index, here’s all the things that are requested:

  • Resolve called for serviceType: System.Web.Mvc.IControllerFactory
  • Resolve called for serviceType: Mvc3WithMEF.Controllers.HomeController
  • Resolve called for serviceType: System.Web.Mvc.IFilterProvider
  • Resolve called for serviceType: System.Web.Mvc.IFilterProvider
  • Resolve called for serviceType: System.Web.Mvc.IFilterProvider
  • Resolve called for serviceType: System.Web.Mvc.IFilterProvider
  • Resolve called for serviceType: System.Web.Mvc.IViewEngine
  • Resolve called for serviceType: System.Web.Mvc.IViewEngine
  • Resolve called for serviceType: ASP.Index_cshtml
  • Resolve called for serviceType: System.Web.Mvc.IViewEngine
  • Resolve called for serviceType: System.Web.Mvc.IViewEngine
  • Resolve called for serviceType: ASP._LogOnPartial_cshtml

Which means that you can now even inject stuff into views or compose their parts dynamically.

Conclusion

I have a strong sense of a power in here… ASP.NET MVC 3 will support DI natively if you want to use it, and I’ll be one of the users happily making use of it. There’s use cases for injecting/composing something in all of the above components, and ASP.NET MVC 3 made this just simpler and more straightforward.

Here’s my sample code with some more examples in it: Mvc3WithMEF.zip (256.21 kb)