43

I have been looking into the new features of the new version of ASP.NET Identity 2.1 and one of its enhancements is the new IoC features integrated into the OWIN Middleware. One of the sentences that I looked in the examples is this one:

app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

This sentence receives a function delegate which returns a new instance of a manager implementation provided on the examples:

 public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options,
        IOwinContext context)
    {
        var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>())); 

I personally dont like this implementation because I am not able to use a container to inject any dependency that I want for these managers.

Also there is an "IdentityFactoryOptions" and a "IOwinContext" that are "magically" injected to the function which Im not able to pull out into my IoC container.

Do anyone have a better workaround on this implementation?

renelopez
  • 485
  • 1
  • 5
  • 9
  • 1
    Here is a good walk-through http://tech.trailmax.info/2014/09/aspnet-identity-and-ioc-container-registration/ – Benjamin E. Dec 03 '14 at 11:45

5 Answers5

54

I'm starting from an out-of-the-box MVC5 installation and using AutoFac as an IoC container. It sounds like I am trying to acheive a similar goal as you, so let me explain what I've done. As a disclaimer, I am fairly new to using IoC and to Identity.

I believe the IOwinContext is unnecessary in a role as an IoC if you are using your own - I switched over to registering my ApplicationUserManager with AutoFac. To achieve this I had to:

Remove CreatePerOwinContext lines from Startup.Auth since I'll register ApplicationDbContext and ApplicationUserManager in AutoFac.

//app.CreatePerOwinContext(ApplicationDbContext.Create);
//app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

Modify the ApplicationUserManager constructor arguments and included everything from the Create function.

public ApplicationUserManager(IUserStore<ApplicationUser> store, IdentityFactoryOptions<ApplicationUserManager> options)
        : base(store)
{
    //all the code from the 'Create' function here, using `this` for `manager`
}

Set the AccountController to have a single constructor taking an ApplicationUserManager as an argument and scrapped the UserManager property that grabs the ApplicationUserManager from the OwinContext.

private ApplicationUserManager _userManager; //every thing that needs the old UserManager property references this now
public AccountController(ApplicationUserManager userManager) 
{
    _userManager = userManager;
}

Register everything with AutoFac, including an instance of IdentityFactoryOptions.

var x = new ApplicationDbContext();
builder.Register<ApplicationDbContext>(c => x);
builder.Register<UserStore<ApplicationUser>>(c => new UserStore<ApplicationUser>(x)).AsImplementedInterfaces();
builder.Register<IdentityFactoryOptions<ApplicationUserManager>>(c => new IdentityFactoryOptions<ApplicationUserManager>()
{
    DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ApplicationName")
});
builder.RegisterType<ApplicationUserManager>();

That's the rough summary. I may have missed a couple of other tweaks I had to do along the way.

Deilan
  • 4,740
  • 3
  • 39
  • 52
Ben Bartle
  • 1,080
  • 11
  • 12
  • 18
    Good stuff, but this is probably a better autofac config builder.RegisterType.AsSelf().InstancePerRequest(); builder.Register>().AsImplementedInterfaces().InstancePerRequest(); builder.Register>(c => new IdentityFactoryOptions() { DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ApplicationName") }); builder.RegisterType().AsSelf().InstancePerRequest(); – Derek Flenniken Jun 29 '14 at 23:27
  • 1
    Yeah I ended up doing a similar approach as you.I think this part should be more pluggable and let the instance injection be configurable by yourself. – renelopez Oct 27 '14 at 20:52
  • 1
    I find it better using InstancePerLifeTimeScope() instead of InstancePerRequest(), as they are quite similar in behavior and it also works in unit tests without having to simulate per-request lifetime. http://docs.autofac.org/en/latest/faq/per-request-scope.html#troubleshooting – angularsen May 08 '15 at 19:20
  • 2
    I also found this article very helpful on the same topic: http://www.talksharp.com/configuring-autofac-to-work-with-the-aspnet-identity-framework-in-mvc-5 – angularsen May 08 '15 at 21:24
  • 2
    @angularsen This seems like the current version of the article you referenced: https://developingsoftware.com/configuring-autofac-to-work-with-the-aspnet-identity-framework-in-mvc-5/ . The TalkSharp URL returns a 301 Moved Permanently. – iokevins Nov 08 '17 at 00:12
33

Ben's answer gets the general idea right, but it manually instantiates the DbContext and uses this instance when registering the rest of the types. IMO, that's a bad idea (one shouldn't use the same eternal db context for ALL requests).

Derek's comment is a big improvement, but it doesn't pass the database context to the user store, resulting in errors such as "The entity type ApplicationUser is not part of the model for the current context.".

I've included my code below, for reference - it's really similar to Derek's.

builder.RegisterType<MyApplicationContext>().AsSelf().InstancePerRequest()
//...

builder.RegisterType<ApplicationUserManager>().AsSelf().InstancePerRequest();
builder.RegisterType<ApplicationSignInManager>().AsSelf().InstancePerRequest();
builder.Register(c => new UserStore<ApplicationUser>(c.Resolve<MyApplicationContext>())).AsImplementedInterfaces().InstancePerRequest();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>();
builder.Register(c => new IdentityFactoryOptions<ApplicationUserManager>
{
    DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("Application​")
}); 
Vlad Iliescu
  • 8,074
  • 5
  • 27
  • 23
  • 1
    "c.Resolve()" - ApplicationContext is ObaContext in your particular case. – Sergey Oct 12 '15 at 11:22
  • 1
    This code compiles with the current version of Autofac (3.5.2). Derek's example does not work any more, because ```.Register``` requires an argument. – K0D4 Jun 17 '16 at 17:23
4

For reference here's how you can wire everything up using Unity:

var container = new UnityContainer();

container.RegisterType<MyDbContext>(new InjectionConstructor("ConnectionStringName"));

container.RegisterType<IAuthenticationManager>(
   new InjectionFactory(c => HttpContext.Current.GetOwinContext().Authentication));

container.RegisterType<IUserStore<ApplicationUser>, UserStore<ApplicationUser>>(
   new InjectionConstructor(typeof(MyDbContext)));

container.RegisterType<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>(
   new InjectionConstructor(typeof(MyDbContext)));

container.RegisterType<IdentityFactoryOptions<ApplicationUserManager>>(new InjectionFactory(x =>
   new IdentityFactoryOptions<ApplicationUserManager>
   {
      DataProtectionProvider = new Microsoft.Owin.Security.DataProtection.DpapiDataProtectionProvider("ApplicationName")
   }));

container.RegisterType<ApplicationSignInManager>();

DependencyResolver.SetResolver(new UnityDependencyResolver(container));
Thorsten Westheider
  • 10,572
  • 14
  • 55
  • 97
3

Now detailed for MVC5 Owin integration on Autofac Docs:

"

  • Do all the stuff for standard MVC integration - register controllers, set the dependency resolver, etc.
  • Set up your app with the base Autofac OWIN integration.
  • Add a reference to the Autofac.Mvc5.Owin NuGet package.
  • In your application startup class, register the Autofac MVC middleware after registering the base Autofac middleware.

    public class Startup
    {
      public void Configuration(IAppBuilder app)
      {
       var builder = new ContainerBuilder();
    
      // STANDARD MVC SETUP:
    
      // Register your MVC controllers.
      builder.RegisterControllers(typeof(MvcApplication).Assembly);
    
      // Run other optional steps, like registering model binders,
      // web abstractions, etc., then set the dependency resolver
      // to be Autofac.
      var container = builder.Build();
      DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    
      // OWIN MVC SETUP:
    
      // Register the Autofac middleware FIRST, then the Autofac MVC middleware.
      app.UseAutofacMiddleware(container);
      app.UseAutofacMvc();
      }
    }
    

    "

I also have RoleManager wrapper so added:

builder.RegisterType<RoleStore<IdentityRole>>().As<IRoleStore<IdentityRole, string>>();

as per SO answer

Community
  • 1
  • 1
OzBob
  • 4,227
  • 1
  • 39
  • 48
1

I managed the workaround by using autofac service locator:

app.CreatePerOwinContext(() => DependencyResolver.Current.GetService<ApplicationUserManager>());

Yes, it is not good enough, but in the mean time, we could use object same scope as declared in autofac registration process.

atthakorn
  • 168
  • 1
  • 5