Important Update
We've made significant changes to the IoC support in ASP.NET MVC 3 Beta. Please read Part 5 for more information.
View Engine Registration and View Page Creation
ASP.NET MVC 1.0 introduced View Engines (implementing IViewEngine and IView), shipping with the single WebForms-based view engine. Views, master pages, and partial views in WebForms view engine need to derive from ViewPage,ViewMasterPage, and ViewUserControl (respectively). In MVC 3, we've introduced new dependency injection points for all of these classes (plus the new base class for Razor views, WebViewPage).
Update: (31 July 2010) I've added the source code for UnityMvcServiceLocator to the end of part 2.
Disclaimer
This blog post talks about ASP.NET MVC 3 Preview 1, which is a pre-release version. Specific technical details may change before the final release of MVC 3. This release is designed to elicit feedback on features with enough time to make meaningful changes before MVC 3 ships, so please comment on this blog post or contact me if you have comments.
Location: View Engines
This is a "multiply registered" style service introduced in MVC 1.0. A view engine is registered with theViewEngines.Engines static collection. Order is important with view engines, as the collection facade will ask each view engine in order to render the view; the first one that says it can render it will win.
With MVC 3, we have updated the ViewEngineCollection facade methods (FindView and FindPartialView) to look in both the static registered view engines as well as the view engines it finds in the service locator. To find those view engines, it calls MvcServiceLocator.Current.GetAllInstance<IViewEngine>().
The following is an example of registering a view engine, using Microsoft Unity as the dependency injection container:
MyViewEngine.cs
public class MyViewEngine : IViewEngine { // ... implementation of IViewEngine ... }
Global.asax.cs
protected void Application_Start() { // ... var container = new UnityContainer(); container.RegisterType<IViewEngine, MyViewEngine>("MyViewEngine"); MvcServiceLocator.SetCurrent(new UnityMvcServiceLocator(container)); // ... }
Now when the system resolves views by call FindView and FindPartialView, it will use your view engine. Any view engines the system finds in the service locator will come before any view engines registered with the static registration function. If your container supports explicit ordering, the view engines will be used in the order returned by the service locator.
Location: View Pages
View engines are ultimately responsible for rendering their views. In the case of the WebForms and Razor view engines, this takes the form of classes which are automatically generated from the source of the view (for example, an .aspx file or a .cshtml file). These classes implicit inherit base classes that the view engine is dependent upon (ViewPage, ViewMasterPage, ViewUserControl, or WebViewPage).
Historically, these classes have not had access to dependency injection/service location functionality, because their creation was buried deep inside the implementation of the view engine. In MVC 3, we have updated the built-in view engines to attempt to create the view page classes via the service locator; if that fails, it will fall back to using Activator.CreateInstance, just like in previous versions of MVC.
Again using Unity as our example container, let's presume we have the following code:
InjectedViewPage.cs
using System.Web.Mvc; using Microsoft.Practices.Unity; public abstract class InjectedWebViewPage : WebViewPage { [Dependency] public IMathService MathService { get; set; } }
InjectedView.cshtml
@inherits InjectedWebViewPage @{ LayoutPage = "../Shared/_Layout.cshtml"; View.Title = "Home Page"; } <p>The page says 4 + 8 is @MathService.Add(4, 8).</p>
This gives us a view page that has access to a service that's dynamically injected into the base class for the view page.
Note that we're using property setter injection here rather than constructor injection, as we'd shown in our previous samples. The reason is a fairly technical one that has to do with the way the ASP.NET page compilation system works when it's converting your markup into a class. If you're curious about the specific details, please contact me. The important takeaway is that supporting dependency injection here means using some form other than constructor injection. Our example uses Unity's [Dependency] attribute to get property setter injection; the specific type of injection and how you accomplish it will depend on which service locator you're using.
What's Next?
The next area of service location in MVC 3 that we'll cover is Filters.