Skip to content

Instantly share code, notes, and snippets.

@hyrmn
Created April 1, 2014 20:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hyrmn/9922449 to your computer and use it in GitHub Desktop.
Save hyrmn/9922449 to your computer and use it in GitHub Desktop.
//This is for your web api controllers.
public class HttpControllerActivator : IHttpControllerActivator
{
private readonly IContainer _container;
public HttpControllerActivator(IContainer container)
{
_container = container;
}
public IHttpController Create(
HttpRequestMessage request,
HttpControllerDescriptor controllerDescriptor,
Type controllerType)
{
var nestedContainer = _container.GetNestedContainer();
nestedContainer.Inject(typeof(HttpRequestMessage), request);
nestedContainer.Inject(RequestKeys.BusinessKey, request.BusinessId());
request.RegisterForDispose(nestedContainer);
return (IHttpController)nestedContainer.GetInstance(controllerType);
}
}
public static class IoC
{
public static IContainer Initialize()
{
//... do the IOC type things in here.
}
}
//This is for asp.net mvc controllers. Based on Stig Christensen's example at
// https://groups.google.com/forum/#!topic/structuremap-users/ELY2g0dwUIo
public class MvcControllerFactory : DefaultControllerFactory
{
private const string NestedContainerKey = "NestedContainer";
private readonly IContainer _container;
public MvcControllerFactory(IContainer container)
{
_container = container;
}
public static void DisposeNestedContainer()
{
var nestedContainer = HttpContext.Current.Items[NestedContainerKey] as IContainer;
if (nestedContainer != null)
{
nestedContainer.Dispose();
}
}
private IContainer CreateOrGetNestedContainer()
{
var nestedContainer = HttpContext.Current.Items[NestedContainerKey] as IContainer;
if (nestedContainer == null)
{
nestedContainer = _container.GetNestedContainer();
HttpContext.Current.Items[NestedContainerKey] = nestedContainer;
}
return nestedContainer;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
return CreateOrGetNestedContainer().GetInstance(controllerType) as Controller;
}
}
[assembly: PreApplicationStartMethod(typeof(StructuremapMvc), "Start")]
namespace TheThing.Web.App_Start
{
public static class StructuremapMvc
{
public static void Start()
{
var container = IoC.Initialize();
ControllerBuilder.Current.SetControllerFactory(new MvcControllerFactory(container));
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new HttpControllerActivator(container));
}
}
}
//in your global asax, you'll want to ensure you're disposing of your nested container at the end of the request.
//Note, this is for mvc controllers only. The web api http controller resolver registers itself for disposal.
public class MvcApplication : HttpApplication
{
protected void Application_EndRequest(object sender, EventArgs e)
{
MvcControllerFactory.DisposeNestedContainer();
}
}
@khalidabuhakmeh
Copy link

So in ASP.NET MVC we have the IDependencyResolver interface that needs to be set globally.

DependencyResolver.SetResolver(new SmDependencyResolver(container));

This can normally be found in my Global.asax. Now the issue is that my SmDependencyResolver class looks like this.

public class SmDependencyResolver : IDependencyResolver
    {

        private readonly IContainer _container;

        public SmDependencyResolver(IContainer container)
        {
            _container = container;
        }

        public object GetService(Type serviceType)
        {
            if (serviceType == null) return null;
            try
            {
                return serviceType.IsAbstract || serviceType.IsInterface
                         ? _container.TryGetInstance(serviceType)
                         : _container.GetInstance(serviceType);
            }
            catch (Exception ex)
            {
                var message = "Error trying to resolve type of {0}".With(serviceType.FullName);
                ErrorSignal.FromCurrentContext().Raise(new Exception(message, ex));
                return null;
            }
        }

        public IEnumerable<object> GetServices(Type serviceType)
        {
            return _container.GetAllInstances(serviceType).Cast<object>();
        }
    }

Note that it is using the parent container.

Questions I have so far:

  1. How would you create a SmDependencyResolver to plug into this architecture.
  2. Accessing DependencyResolver.Current.GetService() should use the child container, but is it safe to change out the DependencyResolver constantly?
  3. If SmDependencyResolver is a singleton, like I normally use SM as, then should it resolve HttpContextBase and look for existing child containers before attempting to create one?
  4. Could you have race conditions where multiple containers are created? What would be the side effects?

@hyrmn
Copy link
Author

hyrmn commented Apr 1, 2014

I don't have a use case that requires DependencyResolver and the MvcControllerFactory. Are you using that for resolving attribute dependencies?

@hyrmn
Copy link
Author

hyrmn commented Apr 1, 2014

@khalidabuhakmeh Want to spin up a small repo with an example filter? Or, I could.

I was using a similar approach.

My Sm web activator used to look like:

[assembly: PreApplicationStartMethod(typeof(StructuremapMvc), "Start")]

namespace TheThing.Web.App_Start
{
    public static class StructuremapMvc
    {
        public static void Start()
        {
            var container = IoC.Initialize();

            DependencyResolver.SetResolver(new StructureMapDependencyResolver(container));
            GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator), new HttpControllerActivator(container));
        }
    }
}

However, the details behind the SM resolver I was using are a bit more involved. I believe it was the StructureMap.MVC4 nuget package (code-only package so worth looking at)

I suspect it'd be fine to set both the dependency resolver and a controller activator. However, we'd want to see if we can notice any weird behavior where it's creating a separate child container.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment