Adding Dependency Injection to your ASP.NET MVC Twilio app using MEF

Developer Long Le

This is a guest post by Long Le, a .NET Enterprise Architect and Solutions Architect at Computer Sciences Corporation. Originally posted on Long’s personal blog found here, Long is an avid blogger and a serious Modern Warfare 3 Gamer. Make sure to follow his code and gaming conversations on Twitter @LeLong37 and check out his previous post on the Twilio blog. 

Being accustomed to writing Mvc apps with IoC, specifically with Ninject and Managed Extensibility Framework (Mef), I wanted to see how we could inject most our Twilio instances (types TwilioResponse and the TwilioRestClient) when using them in my Mvc 4 WebApi Controller Actions.

To “Mefify” your Mvc 4 .NET 4.0 app, download the Mvc4.Mef project and reference this in your web app.

Let’s quickly review some of important classes in this project (Mvc.Mef) that do the heavy lifting to wire up Mef in your Mvc app.

  1. MefMvcConfig.cs

    • Creating our CompositionContainer, the container that holds all our exports and imports.
    • AggregateCatalog, which is a collection of catalogs, in our case composed of a ComposableCatalog (we pass in a TypeCatalog later from our our Mvc app, which inherits the Composable Catalog) and DirectoryCatalog which are of the possible classes that are decorated with [Export] and [Import] attributes from the current executing assembly as well as another other assemblies that maybe in the bin from our other projects.

    Note: The RegisterMef method will be invoked from your Application_Start(), Global.asax.cs class to wire integrate and wire up Mef into your Mvc app.

    [sourcecode language="csharp"]

    public static class MefMvcConfig
    {
    public static void RegisterMef(TypeCatalog typeCatalog)
    {
    var compositionContainer = ConfigureContainer(typeCatalog);
    ServiceLocator
    .SetLocatorProvider(() => new MefServiceLocator(compositionContainer));

    ControllerBuilder
    .Current.SetControllerFactory(
    new MefMvcControllerFactory(compositionContainer));

    GlobalConfiguration
    .Configuration
    .DependencyResolver =
    new MefMvcDependencyResolver(compositionContainer);
    }

    private static CompositionContainer ConfigureContainer(
    ComposablePartCatalog composablePartCatalog)
    {
    var path = HostingEnvironment.MapPath("~/bin");
    if (path == null) throw new Exception("Unable to find the path");

    var aggregateCatalog = new AggregateCatalog(new DirectoryCatalog(path));

    if (composablePartCatalog != null)
    aggregateCatalog.Catalogs.Add(composablePartCatalog);

    return new CompositionContainer(
    aggregateCatalog,
    new MefNameValueCollectionExportProvider(
    ConfigurationManager.AppSettings));
    }
    }
    [/sourcecode]

  2. MefMvcControllerFactory.cs, which inherits the Overriding and setting the default Mvc ControllerFactory with our own that we use our DefaultControllerFactory, the MefMvcControllerFactory is used to scan our CompositionContainer when attempting to resolve Controller types.[sourcecode language="csharp"]
    public class MefMvcControllerFactory : DefaultControllerFactory
    {
    private readonly CompositionContainer _compositionContainer;

    public MefMvcControllerFactory(CompositionContainer compositionContainer)
    {
    _compositionContainer = compositionContainer;
    }

    protected override IController GetControllerInstance(
    RequestContext requestContext, Type controllerType)
    {
    var export = _compositionContainer
    .GetExports(controllerType, null, null).SingleOrDefault();

    IController result;

    if (null != export)
    result = export.Value as IController;
    else
    {
    result = base.GetControllerInstance(requestContext, controllerType);
    _compositionContainer.ComposeParts(result);
    }

    return result;
    }

    [/sourcecode]

  3. MefMvcDependencyResolver.cs, which is used, when we override and the default Mvc DependencyResolver, so that when there are dependencies in our WebApi Controllers, the Mvc runtime will use our own MefMvcDependencyResolver to resolve those dependencies[sourcecode language="csharp"]
    public class MefMvcDependencyResolver : IDependencyResolver
    {
    private readonly CompositionContainer _compositionContainer;

    public MefMvcDependencyResolver(CompositionContainer compositionContainer)
    {
    _compositionContainer = compositionContainer;
    }

    #region IDependencyResolver Members

    public IDependencyScope BeginScope()
    {
    return this;
    }

    public object GetService(Type type)
    {
    var export = _compositionContainer
    .GetExports(type, null, null).SingleOrDefault();

    return null != export ? export.Value : null;
    }

    public IEnumerable<object> GetServices(Type type)
    {
    var exports = _compositionContainer.GetExports(type, null, null);
    var exportList = new List<object>();
    if (exports.Any()) exportList.AddRange(exports.Select(export => export.Value));
    return exportList;
    }

    public void Dispose()
    {
    }

    #endregion
    }

    [/sourcecode]

  4. MefNameValueCollectionExportProvider.cs, which enables us to load in to our CompositionContainer our Web.config AppSettings key and values in the case we want to resolve them with injection e.g. Twilio AccountSid and AuthoToken from our Web.config AppSettings.[sourcecode language="csharp"]
    public class MefNameValueCollectionExportProvider : ExportProvider
    {
    private readonly List<Export> _exports;

    public MefNameValueCollectionExportProvider(NameValueCollection settings)
    {
    _exports = new List<Export>();

    foreach (string key in settings)
    {
    var metadata = new Dictionary<string, object> {
    {
    CompositionConstants
    .ExportTypeIdentityMetadataName, typeof (string)
    .FullName
    }};

    var exportDefinition = new ExportDefinition(key, metadata);

    _exports.Add(new Export(exportDefinition, () => settings[key]));
    }
    }

    protected override IEnumerable<Export> GetExportsCore(
    ImportDefinition importDefinition, AtomicComposition atomicComposition)
    {
    return _exports
    .Where(x => importDefinition.IsConstraintSatisfiedBy(x.Definition));
    }
    }

    [/sourcecode]

  5. MefAdapter.cs, which is based from Mark Seemaan’s blog on Resolving Close Types with Mef, this class will be used so that we can add close classes/objects, which we cannot annotate with the [Export] and/or [Import] attributes, in this example I will demonstrate this with Twilio’s objects.[sourcecode language="csharp"]

    public class MefAdapter<T> where T : new()
    {
    private readonly T _typeToExport;

    public MefAdapter()
    {
    _typeToExport = new T();
    }

    [Export]
    public virtual T TypeToExport
    {
    get { return _typeToExport; }
    }
    }

    public class MefAdapter<T1, T2, TResult>
    {
    private static readonly Func<T1, T2, TResult>
    CreateExport = Create<T1, T2, TResult>();

    private readonly TResult _typeToExport;

    [ImportingConstructor]
    public MefAdapter(T1 arg1, T2 arg2)
    {
    _typeToExport = CreateExport(arg1, arg2);
    }

    [Export]
    public virtual TResult TypeToExport
    {
    get { return _typeToExport; }
    }

    internal static Func<T1, T2, TResult> Create<T1, T2, TResult>()
    {
    var constructorArgExpression1 = Expression.Parameter(typeof (T1), "arg1");
    var constructorArgExpression2 = Expression.Parameter(typeof (T2), "arg2");

    var constructorInfo = typeof (TResult).GetConstructor(new[]
    {
    typeof (T1),
    typeof (T2)
    });

    var constructorExpression = Expression
    .New(constructorInfo, constructorArgExpression1, constructorArgExpression2);

    return Expression.Lambda<Func<T1, T2, TResult>>(
    constructorExpression,
    constructorArgExpression1,
    constructorArgExpression2)
    .Compile();
    }
    }
    [/sourcecode]

Now switching to your Mvc 4 app, here are the simple steps to wire this up, first few steps is to simply NuGet CommonServiceLocator, using this we obviously will also be practicing the ServiceLocator Pattern.

Now, download the Mvc4.Mef.Framework project, and reference it in your MVC 4 application and add one line to your Global.asax.cs file to get this all setup. Since we are here let’s go ahead and add some UriPathExtensionMapping configurations so that we can serve up some real Twilio request later on to demonstrate Dependency Injection with the TwilioRequest object from their Api, these UriPathExtensionMappings are not required for Mef’ing your Mvc app.

Note: notice that we are passing in a TypeCatalog when we invoke the RegisterMef method, this is so that we can pass in closed classes (types) that we would like to inject or resolve with Dependency Injection using Mef in this case the TwilioResponse object from their Api.

Global.asax.cs

[sourcecode language="csharp" highlight="7,14,15,16,17,19,20,21,22,23"]

public class MvcApplication : HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();

MefMvcConfig.RegisterMef(new TypeCatalog(typeof(MefAdapter<TwilioResponse>)));

WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);

GlobalConfiguration
.Configuration.Formatters
.XmlFormatter
.AddUriPathExtensionMapping("xml", "text/xml");

GlobalConfiguration
.Configuration
.Formatters
.XmlFormatter
.AddUriPathExtensionMapping("json", "application/json");
}
}
[/sourcecode]

Now if we run our app you should see that the request from the HomeController is now getting resolved through our MefMvcControllerFactory from our CompositionContainer. In this screenshot, I’ve pinned the watch to source (Visual Studio 2012) to illustrate that the Controller type that is being succesfully resolved with DependencyInjection is indeed the HomeController.

In order for Mef to serve up and resolve these Controllers we have to decorate our controllers with the [Export] attribute e.g. HomeController.

Note: The [PartCreationPolicy(CreationPolicy.NonShared)], means that whenever this instance is requested, Mef will new up (instantiate) a new instance every time, the opposite would be CreationPolicy.Shared, which will new one up the first time and keep it alive, and serve this same instance, every time it is requested (which is the behavior we don’t want for Mvc Controllers).

[sourcecode language="csharp" highlight="1,2"]

[Export]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message =
"Modify this template to jump-start your ASP.NET MVC application.";

return View();
}

public ActionResult About()
{
ViewBag.Message = "Your app description page.";

return View();
}

public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";

return View();
}
}
}

[/sourcecode]

Now for the fun part, this Mvc 4 project is from one of my previous blogs Multi-Step (Two-Factor) ASP.NET MVC 4 Registration with SMS using Twilio Cloud Communication and SimpleMembershipProvider for Increased User Validity, where we were using TwilioRestClient from their Api to send SMS messages for Two-Factor registration. Let’s see how we can inject this now with Mef in our Register Action.

Before:

[sourcecode language="csharp" highlight="25,26,27"]

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
var smsVerificationCode =
GenerateSimpleSmsVerificationCode();

WebSecurity.CreateUserAndAccount(
model.UserName,
model.Password,
new
{
model.Mobile,
IsSmsVerified = false,
SmsVerificationCode = smsVerificationCode
},
true);

var twilioRestClient = new TwilioRestClient(
ConfigurationManager.AppSettings.Get("Twilio:AccoundSid"),
ConfigurationManager.AppSettings.Get("Twilio:AuthToken"));

twilioRestClient.SendSmsMessage(
"+19722001298",
model.Mobile,
string.Format(
"Your ASP.NET MVC 4 with Twilio " +
"registration verification code is: {0}",
smsVerificationCode)
);

Session["registrationModel"] = model;

return RedirectToAction("SmsVerification", "Account", model);
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}

// If we got this far, something failed, redisplay form
return View(model);
}

[/sourcecode]

After:

[sourcecode language="csharp" highlight="25"]

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
var smsVerificationCode =
GenerateSimpleSmsVerificationCode();

WebSecurity.CreateUserAndAccount(
model.UserName,
model.Password,
new
{
model.Mobile,
IsSmsVerified = false,
SmsVerificationCode = smsVerificationCode
},
true);

var twilioRestClient = ServiceLocator.Current.GetInstance<TwilioRestClient>();

twilioRestClient.SendSmsMessage(
"+19722001298",
model.Mobile,
string.Format(
"Your ASP.NET MVC 4 with Twilio " +
"registration verification code is: {0}",
smsVerificationCode)
);

Session["registrationModel"] = model;

return RedirectToAction("SmsVerification", "Account", model);
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}

// If we got this far, something failed, redisplay form
return View(model);
}

[/sourcecode]

Notice in line 25, we are now using ServiceLocator to resolve the dependency in our Register action for the TwilioRestClient. Here Mef will take care of activating and setting up our TwilioRestClient instance for us.

Ideally we would want to wrap (Composition Pattern) the TwilioRestClient, implementing our own interface and injecting the interface with our wrapped TWilioRestClient. If time permits, I’ll cover this in another post, for now we’ll just have fun with injecting closed classes with Mef.

Let’s see the TwilioRestClient injection with ServiceLocator in action by running the app and registering.

Register View

Debugging in our Register Action from the AccountController, notice how our TwilioRestClient is successfully being activated, injected and resolved with the ServiceLocator, which really is just scanning our CompositionContainer we had setup earlier, which was made possible with our TwilioRestClientMefAdapter.

Great! We received a test SMS text message with our injected TwilioRestClient.

How did this happen? Will we had to create a TwilioRestClientMefAdapter (this is a variation of the Adapter Pattern) class, this was needed because the TwilioRestClient does not have parameterless constructor, and those parameters were our Twilio AccountSid and AuthToken which were in our Web.config.

[sourcecode language="csharp" highlight="1,6,8,9,14"]

[PartCreationPolicy(CreationPolicy.NonShared)]
public class TwilioRestClientMefAdapter
{
private readonly TwilioRestClient _twilioRestClient;

[ImportingConstructor]
public TwilioRestClientMefAdapter(
[Import("Twilio:AccoundSid")] string accountSid,
[Import("Twilio:AuthToken")] string authToken)
{
_twilioRestClient = new TwilioRestClient(accountSid, authToken);
}

[Export]
public TwilioRestClient TwilioRestClient
{
get { return _twilioRestClient; }
}
}

[/sourcecode]

Let’s debug the activation process, just to understand what’s happening here.

Now, when we inject our TwilioRestClient with

[sourcecode language="csharp"]

var twilioRestClient = ServiceLocator.Current.GetInstance<TwilioRestClient>();

[/sourcecode]

what’s happening is that, what’s marked for Export is the property named TwilioRestClient. In order for Mef to export that property it must instantiate the class that it lives in which in our case is the TwilioRestClientMefAdapter class. The constructor marked with [ImportingConstructor] is what Mef will scan for, to activate this class. In the constructor is where we are also injecting our AccountSid and AuthToken from our web.conf appSettings that was loaded earlier into our CompositionContainer using our MefNameValueCollectionExportProvider.cs class, where we passed in ConfigurationManager.AppSettings to our MefMvcConfig.ConfigureContainer method. Fortunately for us, the ConfigurationManager.AppSettings happens to be a NameValueCollection.

[sourcecode language="csharp" highlight="14,15"]

private static CompositionContainer ConfigureContainer(
ComposablePartCatalog composablePartCatalog)
{
var path = HostingEnvironment.MapPath("~/bin");
if (path == null) throw new Exception("Unable to find the path");

var aggregateCatalog = new AggregateCatalog(new DirectoryCatalog(path));

if (composablePartCatalog != null)
aggregateCatalog.Catalogs.Add(composablePartCatalog);

return new CompositionContainer(
aggregateCatalog,
new MefNameValueCollectionExportProvider(
ConfigurationManager.AppSettings));
}

[/sourcecode]