I am currently trying to reconfigure StructureMap in our application after upgrading from an old version (2.6) that was never correctly implemented in the first place. I am new to using DI Containers to begin with, and am finding documentation for newer StructureMap versions hard to find. I uninstalled the old 2.6 version of StructureMap and installed StructureMap.MVC5 (since I am using MVC5).
What I am having issues with is the AccountController. I have StructureMap set up to use the parameterless constructor, but when my application tries to create the UserManager, I get an InvalidOperationException
, "No owin.Environment item was found in the context."
Obviously I need additional configuration for StructureMap, but I have no idea what/how. I can find a million sources for this error, all suggesting adding a tag in web.config, but none of them seem to be DI container specific - and I only have this issue when I use StructureMap vs letting the framework create the controller.
Below is the relevant code; that section of the AccountController is just stock template code.
AccountController.cs
private ApplicationUserManager _userManager;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager)
{
UserManager = userManager;
}
public ApplicationUserManager UserManager
{
get
{
// This is where the exception is thrown
return _userManager ??
HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
DefaultRegistry.cs
public DefaultRegistry()
{
Scan(
scan =>
{
scan.TheCallingAssembly();
scan.WithDefaultConventions();
scan.With(new ControllerConvention());
});
For<IBasicRepository>()
.Use<EntityRepository>()
.LifecycleIs<HttpContextLifecycle>()
.Ctor<string>("ConnectionString")
.Is(ConfigurationManager.ConnectionStrings["MyContext"].ConnectionString);
For<AccountController>()
.Use<AccountController>()
.SelectConstructor(() => new AccountController());
}
As @Erik Funkenbusch pointed out, I was doing competing things. I ended up making UserManager an auto-property, removed the parameterless constructor, and let StructureMap inject the ApplicationUserManager.
Then, I simply needed to configure the IUserStore and DbContext that Identity uses in DefaultRegistry.cs:
This was all I needed to do to get StructureMap.MVC working with Identity.
Part of my initial hangup was that I didn't realize the way StructureMap.MVC (and other DI containers) worked. (See my related question.) I was expecting it to just work with my stock AccountController which got initialized by the framework (and thought it magically intercepted object creation to inject anything I had configured), not realizing that StructureMap must initialize the controllers itself in order for it to perform constructor injection. So when I ran into issues, I was A. Surprised that StructureMap had anything to do with my AccountController in the first place (since I wasn't explicitly configuring injection for any of its parameters - only for my Repository used in other controllers), and B. I wasn't thinking about changing my stock code but rather thinking about how to configure StructureMap. It turned out I needed to do both. Luckily, it was an easy modification and I learned a little more about how DI containers work.