Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AspIdentiy ApplicationUserManager is Static, how to extend so it participates in my IoC framework?

In a new ASPNET MVC application you now get the AspIdentity goodies for free.

There's a harmless little line 'plug in your email service here'.

So I did:

public class EmailService : IIdentityMessageService
{
    private static My.Services.IEmailService _emailservice;

    public EmailService(Insolvency.Services.IEmailService emailservice)
    {
        _emailservice = emailservice;
    }

    public Task SendAsync(IdentityMessage message)
    {
        _emailservice.SendEmail(message);
       return Task.FromResult(0);
    }
}

and now the joy:

public class ApplicationUserManager : UserManager<ApplicationUser>
{
    private My.Services.IEmailService _emailservice;

    public ApplicationUserManager(IUserStore<ApplicationUser> store, My.Services.IEmailService emailservice): base(store)        
    {
        _emailservice = emailservice;
    }

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

as Owin kicks in it calls the Create on ApplicationUserManager in Startup.Auth.cs:

public partial class Startup 
   {
    public void ConfigureAuth(IAppBuilder app) 
     {
        ...
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);

and as I'm using AutoFac as my IoC container, in global.asax.cs

builder.RegisterType<My.Services.EmailService>().As<IEmailService>();

if the Create method is static so I get:
_emailService is null

I've looked here:http://forums.asp.net/post/5293670.aspx, and How do i create an instance of UserManager and Using Autofac to provide types exported by static factory. but no luck.

If I change:

private My.Services.IEmailService _emailservice;

to be public non-static I feel IoC gods shaking their heads, and I can't build 'object reference required'

like image 556
OzBob Avatar asked May 09 '14 09:05

OzBob


2 Answers

I fought with this for a long while today, and in the end, the easiest thing to do was this.

Inside of the

public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)

method, find these lines of code.

manager.EmailService = new EmailService();
manager.SmsService = new SmsService();

And change it to this.

manager.EmailService = new EmailService(DependencyResolver.Current.GetService<Insolvency.Services.IEmailService>());

I don't like the Service Locator pattern, but all of the MVC templated code uses it and it's best not to fight the framework.

like image 35
RubberDuck Avatar answered Oct 07 '22 11:10

RubberDuck


Today I was struggling with the same issue (still working on it since I'm new to Asp.Identity). I did it this way:

  1. Startup.cs (use your own container)

    public class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var authConfigurator = new AuthConfigurator();
            authConfigurator.ConfigureAuth(app);
            var unityContainer = UnityConfig<MvcUnityDependencyContainer>.UseContainer();
            //Asp identity registration
            IDataProtectionProvider dataProtectionProvider = app.GetDataProtectionProvider();
            unityContainer.RegisterInstance(dataProtectionProvider);
            unityContainer.RegisterType<DbContext, ApplicationDbContext>(new HierarchicalLifetimeManager());
            unityContainer.RegisterType<UserManager<ApplicationUser, int>>(new HierarchicalLifetimeManager());
            unityContainer.RegisterType IIdentityMessageService, EmailService>();
            unityContainer.RegisterType<IUserStore<ApplicationUser, int>,
                UserStore<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>>(
                new InjectionConstructor(new ApplicationDbContext()));
            unityContainer.RegisterType<IIdentityMessageService, EmailService>();
        }
    }
    
  2. ApplicationUserManager (I removed static method Create):

    public class ApplicationUserManagerService : UserManager<ApplicationUser, int>
    {
        public ApplicationUserManagerService(IUserStore<ApplicationUser, int> store, 
                                             IIdentityMessageService emailService,
                                             IDataProtectionProvider dataProtectionProvider)
                                             : base(store)
        {
            UserTokenProvider = new EmailTokenProvider<ApplicationUser, int>();
            EmailService = emailService;
            Configure(dataProtectionProvider);
        }
        private void Configure(IDataProtectionProvider dataProtectionProvider)
        {
            // Configure validation logic for usernames
            UserValidator = new UserValidator<ApplicationUser, int>(this)
            {
                AllowOnlyAlphanumericUserNames = false,
                RequireUniqueEmail = true
            };
            // Configure validation logic for passwords
            PasswordValidator = new PasswordValidator
            {
                RequiredLength = 1,
                RequireNonLetterOrDigit = false,
                RequireDigit = false,
                RequireLowercase = false,
                RequireUppercase = false,
            };
            // Register two factor authentication providers. This application uses Phone and Emails as a step of receiving a code for verifying the user
            // You can write your own provider and plug in here.
            RegisterTwoFactorProvider("PhoneCode", new PhoneNumberTokenProvider<ApplicationUser, int>
            {
                MessageFormat = "Your security code is: {0}"
            });
            RegisterTwoFactorProvider("EmailCode", new EmailTokenProvider<ApplicationUser, int>
            {
                Subject = "Security Code",
                BodyFormat = "Your security code is: {0}"
            });
            if (dataProtectionProvider != null)
            {
                UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser, int>(dataProtectionProvider.Create("ASP.NET Identity"));
            }
        }
    }
    
  3. Controller

    public class AccountController : Controller
    {
        private ApplicationUserManagerService _userManagerService;
        public AccountController(ApplicationUserManagerService userManagerService)
        {
            Contract.Requires(userManagerService != null);
            _userManagerService = userManagerService;
        }
        /*....*/
    }
    
  4. ApplicationUser

    public class ApplicationUser : IdentityUser<int, CustomUserLogin, CustomUserRole, CustomUserClaim>
    {
        public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser, int> manager)
        {
            var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
            return userIdentity;
        }
    }
    
    public class CustomRole : IdentityRole<int, CustomUserRole>
    {
        public CustomRole() { }
        public CustomRole(string name) { Name = name; }
    }
    
    public class CustomUserClaim : IdentityUserClaim<int> { }
    public class CustomUserLogin : IdentityUserLogin<int> { }
    public class CustomUserRole : IdentityUserRole<int> { }
    

This works for me but please feel free to suggest better solution.

like image 186
free4ride Avatar answered Oct 07 '22 10:10

free4ride