StructureMap, Value Providers and Dependency Injection in ASP.NET MVC 3

The purpose of a ValueProvider is to provide values from a source of data. There are several types of value providers built into the framework. They pull values from form posts, query strings, route data, posted files and other things. They exist to pull data from various data sources to supply to the model binding system so you can get your values on your action methods. But if you are reading this blog post, you probably already know what value providers are in ASP.NET MVC. So we will move on to getting them hooked up with our StructureMapDependencyResolver.

Here is the good news: value providers are quite easy to hook up with dependency inversion support once your dependency resolver is setup. So let’s get started.

Our ValueProvider

So let’s create a very simple value provider. If you want to look for a more practical and useful value provider look elsewhere as this is simply here to show how to hook it up to our dependency resolver. Behold, the RandomSentenceValueProvider, which provides you with random sentences!


public class RandomSentenceValueProvider : IValueProvider
{
    public RandomSentenceValueProvider(RandomSentenceGenerator randomSentenceGenerator)
    {
        _randomSentenceGenerator = randomSentenceGenerator;
    }

    private RandomSentenceGenerator _randomSentenceGenerator;

    public bool ContainsPrefix(string prefix)
    {
        return false;
    }

    public ValueProviderResult GetValue(string key)
    {
        if (key == "getMeASentenceYo")
        {
            string randomSentence = _randomSentenceGenerator.GetRandomSentence();
            var result = new ValueProviderResult(randomSentence, randomSentence, new System.Globalization.CultureInfo(1));
            return result;
        }

        return null;
    }
}

Alright, so the way that works is that if a value is needed named “getMeASentenceYo”, it will return a random sentence. Now, note that the constructor takes an object to actually generate the sentences. When the value is retrieved, the provider uses that dependency and returns the value. And just so you have it, here is our random sentence generator.


public class RandomSentenceGenerator
{
    static RandomSentenceGenerator()
    {
        _beginnings.Add("Jack ate");
        _beginnings.Add("Violet whalloped");
        _beginnings.Add("Bart shot");
        _beginnings.Add("Vince mutilated");
        _beginnings.Add("Lorie slathered");
        _beginnings.Add("Mike talked to");

        _endings.Add("enemies yet loved the lady of the lake still.");
        _endings.Add("kittens before demanding a cherry pie from his wife.");
        _endings.Add("monks because he liked Avatar so much.");
        _endings.Add("ants because they were clearly planning to take over the world.");
        _endings.Add("bottles of rum due to intense hatred of pirates. Argggh ye matey!");
        _endings.Add("a box of gum. After all, he was a nice person.");
    }

    public string GetRandomSentence()
    {
        var random = new Random();
        return _beginnings[random.Next(0, 6)]
            + " "
            + random.Next(5, 5000)
            + " "
            + _endings[random.Next(0, 6)];
    }

    public static List<string> _beginnings = new List<string>();
    public static List<string> _endings = new List<string>();
}

I am sure that is about the niftiest code you have ever seen.

In an ideal world, it would be nice to simply register this value provider and have the IDependencyResolver just pick it up. Unfortunately, you need something else, a ValueProviderFactory for the value provider.


public class RandomSentenceValueProviderFactory : ValueProviderFactory
{
    public RandomSentenceValueProviderFactory(RandomSentenceGenerator gen)
    {
        _randomSentenceGenerator = gen;
    }

    private RandomSentenceGenerator _randomSentenceGenerator;

    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        return new RandomSentenceValueProvider(_randomSentenceGenerator);
    }
}

Note that this takes the same dependency as the value provider. Why is that? Well, the answer lies in how this is setup by the runtime: the framework only gets value providers through value provider factories. This makes for an annoying redundancy here as we create a class that does nothing else than create another class and pass on its dependencies. The ValueProviderFactory is not new with ASP.NET MVC 3, so my assumption is that it existed to give you yet one more extension point to pimp the runtime. But in this case it just gets in the way.

Once that is done, we should probably inform our IoC container about these new dependencies. Our application start is getting pretty big now but we’ll go ahead and add a call to setup the new value provider.


protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

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

    var modelBinderTypeMappingDictionary = new ModelBinderTypeMappingDictionary();
    modelBinderTypeMappingDictionary.Add(typeof(AThing), typeof(AThingModelBinder));

    var globalFilterRegistrationList = new GlobalFilterRegistrationList();
    globalFilterRegistrationList.Add(new GlobalFilterRegistration { Type = typeof(YourMomGlobalFilter), Order = 2 });

    IContainer container = new Container(x =>
    {
        x.For<IControllerActivator>().Use<StructureMapControllerActivator>();
        x.For<IModelBinderProvider>().Use<StructureMapModelBinderProvider>();
        x.For<ModelBinderTypeMappingDictionary>().Use(modelBinderTypeMappingDictionary);
        x.For<IFilterProvider>().Use<StructureMapFilterProvider>();
        x.For<IFilterProvider>().Use<StructureMapGlobalFilterProvider>();
        x.For<GlobalFilterRegistrationList>().Use(globalFilterRegistrationList);
        x.For<IBar>().Use<Bar>();
        x.For<ILogger>().Use<Logger>();
        x.For<ValueProviderFactory>().Use<RandomSentenceValueProviderFactory>();
        x.SetAllProperties(p =>
            {
                p.OfType<ILogger>();
            });
    });

    DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
}

Our only new value there is the x.For<ValueProviderFactory>().Use<RandomSentenceValueProviderFactory>(); bit. Fancy. Okay, so that sets up our value provider. So at this point you are really setup and ready to go. So if I add the following action to one of my controllers, I get the parameter filled in for me via the constructor-injected value provider.


public ActionResult GetASentence(string getMeASentenceYo)
{
    ViewBag.ASentence = getMeASentenceYo;
    return View();
}

Sweet...

Closing Thought

This actually works fine with the exception of that annoying little factory that has to sit between our configuration and our value provider. I bet that can be worked around by creating a generic factory of some sort but I will leave that as an exercise to the reader. Shame that the factory is required. Such is life.

Remember, if you want to get the code, you can get it all up on github. Stay tuned for a bit more dependency inject in ASP.NET MVC 3.

Comments

shawn z 2011-08-02 12:22:46

Cutting and pasting that does not work in MVC3. To get the extension to work, I had to create a class file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace incMvcSite.Classes
{
  public static class HtmlPrefixScopeExtensions
  {
    public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix)
    {
      return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix);
    }

    private class HtmlFieldPrefixScope : IDisposable
    {
      private readonly TemplateInfo templateInfo;
      private readonly string previousHtmlFieldPrefix;

      public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix)
      {
        this.templateInfo = templateInfo;
        previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix;
        templateInfo.HtmlFieldPrefix = htmlFieldPrefix;
      }

      public void Dispose()
      {
        templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix;
      }
    }
  }
}

In the Razor (.cshtml) file, I added the following:

@using incMvcSite.Classes
@using(Html.BeginHtmlFieldPrefixScope("Permission"))
{
  Permission
  // The Html.EditorFor's would go here...
}

Notice the using to bring me extension class into scope. That allows the second using line to work.

Now the problem is that when posting back, the object is not updated. In my controller, I used a second parameter to specify my prefix:

TryUpdateModel(modelUser.Permission, "Permission");

This added the prefix to all field in the HTML, and the TryUpdateModel loaded the object with prefixed control names. Now you can properly namespace your controls for embedded edit lists, and for partial views of models with the same property names.

Shawn Zernik

Internetwork Consulting