I cannot get StructureMap to inject a value into the constructor of the MvcApplication of the Global.asax.cs file. I have created a brand new and clean project and used StructureMap.MVC5 package to generate the necessary structures inside the DependencyResolution subfolder.
My class to be injected is simple:
namespace SMTest.Models {
public interface ITestSM
{
int OnePlusTwo();
}
public class TestSM : ITestSM
{
public int OnePlusTwo()
{
return 1 + 2;
}
} }
IoC.cs is:
namespace SMTest.DependencyResolution {
using StructureMap;
public static class IoC {
public static IContainer Initialize() {
return new Container(c => c.AddRegistry<DefaultRegistry>());
}
}
}
StructuremapMvc.cs contains:
public static void Start() {
IContainer container = IoC.Initialize();
StructureMapDependencyScope = new StructureMapDependencyScope(container);
DependencyResolver.SetResolver(StructureMapDependencyScope);
DynamicModuleUtility.RegisterModule(typeof(StructureMapScopeModule));
}
And DefaultRegistry.cs contains:
public DefaultRegistry() {
Scan(
scan => {
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
For<ITestSM>().Singleton().Use<TestSM>();
}
I can easily inject it into the HomeController:
public class HomeController : Controller
{
private ITestSM _testsm;
public HomeController(ITestSM testsm):base()
{
_testsm = testsm;
}
public ActionResult Index()
{
return View();
}
But when I try to do that in Global.asax.cs:
public class MvcApplication : System.Web.HttpApplication
{
private ITestSM _testsm;
public MvcApplication(ITestSM testsm)
{
_testsm = testsm;
}
protected void Application_Start()
{...
I get the error: Compiler Error Message: CS7036: There is no argument given that corresponds to the required formal parameter 'testsm' of 'MvcApplication.MvcApplication(ITestSM)'
Despite the fact that IoC init triggers before the Application_Start, StructuremapMvc.cs:
[assembly: PreApplicationStartMethod(typeof(StructuremapMvc), "Start")]
[assembly: ApplicationShutdownMethod(typeof(StructuremapMvc), "End")]
Any idea?
Edit: I was able to inject it into Global.asax.cs explicitly:
protected void Application_EndRequest(object sender, System.EventArgs e)
{
if (!this.Request.Path.Contains("Content") && !this.Request.Path.Contains("Scripts") && !this.Request.Path.Contains("less"))
{
ISession currentSession = SMTest.App_StartStructuremapMvc.StructureMapDependencyScope.Container.GetInstance<ISession>();
currentSession.Flush();
currentSession.Close();
}
}
However, the session I get here is not the one injected into the controller so I am flushing and closing the wrong one, leaving data not commited to the DB.
Unfortunately, it's not possible to inject dependencies into your MvcApplication
class via constructor injection. The only way to do this is as you've discovered, accessing the container via its static instance (also known as the Service Locator pattern).
Let me explain my understanding of why this is the case.
After you setup your container you then call the following line:
DependencyResolver.SetResolver(StructureMapDependencyScope);
This line sets hooks your IoC Container (in this instance, StructureMap) up to ASP.NET MVC's dependency resolver. Ultimately acting as an interface between ASP.NET and your StructureMap container.
When a controller is instantiated by the MVC framework, dependencies will be resolved via the instance configured within your DependencyResolver
. However on the creation of your MvcApplication
instance, dependencies are not resolved via the DependencyResolver
instance. The only instance type who's dependencies are resolved via the DependencyResolver
are controllers. Ultimately this is a flaw in the ASP.NET MVC framework (which has been rectified in ASP.NET Core MVC).
In addition to this, your container isn't even configured when the constructor is called and is only configured within your Start()
method that's invoked AFTER the controller has been called.