Issues Configuring StructureMap.MVC5 to work with

2019-04-01 01:01发布

问题:

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());
    }

回答1:

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.

    public ApplicationUserManager UserManager { get; private set; }

    public AccountController(ApplicationUserManager userManager)
    {
        UserManager = userManager;
    }

Then, I simply needed to configure the IUserStore and DbContext that Identity uses in DefaultRegistry.cs:

        For<IUserStore<ApplicationUser, int>>()
            .Use<UserStore<ApplicationUser, CustomRole, int, CustomUserLogin, 
                            CustomUserRole, CustomUserClaim>>()
            .LifecycleIs<HttpContextLifecycle>();

        For<DbContext>()
            .Use(() => new ApplicationDbContext())
            .LifecycleIs<HttpContextLifecycle>();  


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.