Archive for the ‘Object-Oriented concepts’ Category

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)

Supporting multiple submit buttons on an ASP.NET MVC view

Multiple buttons on an ASP.NET MVC view A while ago, I was asked for advice on how to support multiple submit buttons in an ASP.NET MVC application, preferably without using any JavaScript. The idea was that a form could contain more than one submit button issuing a form post to a different controller action.

The above situation can be solved in many ways, one a bit cleaner than the other. For example, one could post the form back to one action method and determine which method should be called from that action method. Good solution, however: not standardized within a project and just not that maintainable… A better solution in this case was to create an ActionNameSelectorAttribute.

Whenever you decorate an action method in a controller with the ActionNameSelectorAttribute (or a subclass), ASP.NET MVC will use this attribute to determine which action method to call. For example, one of the ASP.NET MVC ActionNameSelectorAttribute subclasses is the ActionNameAttribute. Guess what the action name for the following code snippet will be for ASP.NET MVC:

public class HomeController : Controller
{
    [ActionName("Index")]
    public ActionResult Abcdefghij()
    {
        return View();
    }
}

That’s correct: this action method will be called Index instead of Abcdefghij. What happens at runtime is that ASP.NET MVC checks the ActionNameAttribute and asks if it applies for a specific request. Now let’s see if we can use this behavior for our multiple submit button scenario.

kick it on DotNetKicks.com

The view

Since our view should not be aware of the server-side plumbing, we can simply create a view that looks like this.

<%@ Page Language=“C#” Inherits=“System.Web.Mvc.ViewPage<MvcMultiButton.Models.Person>” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “<a href="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"”>http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” >
<head runat=”server”>
    <title>Create person</title>
    <script src=”<%=Url.Content(“~/Scripts/MicrosoftAjax.js”)%>” type=”text/javascript”></script>
    <script src=”<%=Url.Content(“~/Scripts/MicrosoftMvcAjax.js”)%>” type=”text/javascript”></script>
</head>
<body>

    <% Html.EnableClientValidation(); %>
    <% using (Html.BeginForm()) {%>

        <fieldset>
            <legend>Create person</legend>
            <p>
                <%= Html.LabelFor(model => model.Name) %>
                <%= Html.TextBoxFor(model => model.Name) %>
                <%= Html.ValidationMessageFor(model => model.Name) %>
            </p>
            <p>
                <%= Html.LabelFor(model => model.Email) %>
                <%= Html.TextBoxFor(model => model.Email) %>
                <%= Html.ValidationMessageFor(model => model.Email) %>
            </p>
            <p>
                <input type=“submit” value=“Cancel” name=“action” />
                <input type=“submit” value=“Create” name=“action” />
            </p>
        </fieldset>

    <% } %>

    <div>
        <%=Html.ActionLink(“Back to List”, “Index”) %>
    </div>

</body>
</html>

Note the two submit buttons (namely “Cancel” and “Create”), both named “action” but with a different value attribute.

The controller

Our controller should also not contain too much logic for determining the correct action method to be called. Here’s what I propose:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View(new Person());
    }

    [HttpPost]
    [MultiButton(MatchFormKey="action", MatchFormValue="Cancel")]
    public ActionResult Cancel()
    {
        return Content(“Cancel clicked”);
    }

    [HttpPost]
    [MultiButton(MatchFormKey = "action", MatchFormValue = "Create")]
    public ActionResult Create(Person person)
    {
        return Content(“Create clicked”);
    }
}

Some things to note:

  • There’s the Index action method which just renders the view described previously.
  • There’s a Cancel action method which will trigger when clicking the Cancel button.
  • There’s a Create action method which will trigger when clicking the Create button.

Now how do these last two work… You may also have noticed the MultiButtonAttribute being applied. We’ll see the implementation in a minute. In short, this is a subclass for the ActionNameSelectorAttribute, triggering on the parameters MatchFormKey and MatchFormValues. Now let’s see how the MultiButtonAttribute class is built…

The MultiButtonAttribute class

Now do be surprised of the amount of code that is coming…

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class MultiButtonAttribute : ActionNameSelectorAttribute
{
    public string MatchFormKey { get; set; }
    public string MatchFormValue { get; set; }

    public override bool IsValidName(ControllerContext controllerContext, string actionName, MethodInfo methodInfo)
    {
        return controllerContext.HttpContext.Request[MatchFormKey] != null &&
            controllerContext.HttpContext.Request[MatchFormKey] == MatchFormValue;
    }
}

When applying the MultiButtonAttribute to an action method, ASP.NET MVC will come and call the IsValidName method. Next, we just check if the MatchFormKey value is one of the request keys, and the MatchFormValue matches the value in the request. Simple, straightforward and re-usable.

kick it on DotNetKicks.com