Simple Injector with ASP.NET MVC 5 Identity 2.0

One reason I love .NET Core is the out-of-box dependency injection it provides. Unfortunately I’m not allowed to use .NET Core due to some backward compatibility requirements.

Simple Injector comes to the rescue, but there was a catch when it comes to dynamically injecting DbContext.

Problem

The default template with Identity generated by MVC 5 (mainly Startup_auth.cs, AccountController and ManageController), Simple Injector just refused to work with it. While there are some solutions available online 1 2, none of them worked for me.

Solution

Although the solutions online didn’t work for me, I managed to gather enough information to make my own solution. This GitHub issue1 provides almost everything I need, except it’s based on an older version of Simple Injector, so some syntax changes were required.

Follow the post you should be able to:

  1. Prepare ApplicationUserManager, UserStore and ApplicationSignInManager for AccountController and ManageController, by removing some badly written (and anti-pattern, and violating SOLID, and…) codes generated
  2. Get an OwinContext for ApplicationUserManager in Startup_auth.cs
  3. Expect some errors:
'SimpleInjectorWebExtensions.RegisterPerWebRequest<TService>(Container, Func<TService>)' is obsolete: 'RegisterPerWebRequest has been deprecated. Please use Register<TService>(Func<TService>, Lifestyle.Scoped) instead. See: https://simpleinjector.org/mvc'

'Container' does not contain a definition for 'RegisterSingle' and no extension method 'RegisterSingle' accepting a first argument of type 'Container' could be found (are you missing a using directive or an assembly reference?)

To fix the above errors, I used the latest syntax found on from Simple Injector’s documentation

Key Changes

Some key changes are:

  • container.RegisterSingle<IAppBuilder>(app); was simplified to container.RegisterInstance(app);

  • container.RegisterPerWebRequest<ApplicationUserManager>(); became container.Register<ApplicationUserManager>(Lifestyle.Scoped);, where Lifestyle.Scoped is defined beforehand container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

  • ApplicationUserManager needs a static methodf or initialisation, see code below

  • The same applies for UserStore

container.RegisterPerWebRequest<IUserStore<
            ApplicationUser>>(() => 
            new UserStore<ApplicationUser>(
                container.GetInstance<ApplicationDbContext>()));

// is now ⬇️

container.Register<IUserStore<
            ApplicationUser>>(() =>
            new UserStore<ApplicationUser>(
                container.GetInstance<ApplicationDbContext>()), Lifestyle.Scoped);
  • A little hack to bypass container.verfiy(), because OwinContext doesn’t exist at the verification stage due to the lack ofHttpContext.Current
container.Register(() =>
    container.IsVerifying
        ? new OwinContext(new Dictionary<string, object>()).Authentication
        : HttpContext.Current.GetOwinContext().Authentication, Lifestyle.Scoped);

Complete Solution

The complete SimpleInjectorInitializer.cs(or you can put the code in Startup.cs, I just don’t like adding stuff to it) file looks like this:

    public static class SimpleInjectorInitializer
    {
        public static Container Initialize(IAppBuilder app)
        {
            var container = GetInitializeContainer(app);

            container.Verify();

            DependencyResolver.SetResolver(
                new SimpleInjectorDependencyResolver(container));

            return container;
        }

        public static Container GetInitializeContainer(
                  IAppBuilder app)
        {
            var container = new Container();
            container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();

            container.RegisterInstance(app);

            container.Register<
                   ApplicationUserManager>(Lifestyle.Scoped);

            var connString =
                @"some connection string";

            container.Register(typeof(IDbRepository<>), typeof(IDbRepository<>).Assembly);

            container.Register(()
              => new ApplicationDbContext(
                    connString), Lifestyle.Scoped);

            container.Register<IUserStore<
              ApplicationUser>>(() =>
                new UserStore<ApplicationUser>(
                  container.GetInstance<ApplicationDbContext>()), Lifestyle.Scoped);

            container.RegisterInitializer<ApplicationUserManager>(
                manager => InitializeUserManager(manager, app));

            container.Register<SignInManager<ApplicationUser, string>, ApplicationSignInManager>(Lifestyle.Scoped);

            container.Register(() =>
                container.IsVerifying
                    ? new OwinContext(new Dictionary<string, object>()).Authentication
                    : HttpContext.Current.GetOwinContext().Authentication, Lifestyle.Scoped);


            container.RegisterMvcControllers(
                    Assembly.GetExecutingAssembly());

            return container;
        }

        private static void InitializeUserManager(
            ApplicationUserManager manager, IAppBuilder app)
        {
            manager.UserValidator =
             new UserValidator<ApplicationUser>(manager)
             {
                 AllowOnlyAlphanumericUserNames = false,
                 RequireUniqueEmail = true
             };

            manager.PasswordValidator = new PasswordValidator()
            {
                RequiredLength = 6,
                RequireNonLetterOrDigit = false,
                RequireDigit = false,
                RequireLowercase = false,
                RequireUppercase = false,
            };

            var dataProtectionProvider =
                 app.GetDataProtectionProvider();

            if (dataProtectionProvider != null)
            {
                manager.UserTokenProvider =
                 new DataProtectorTokenProvider<ApplicationUser>(
                  dataProtectionProvider.Create("ASP.NET Identity"));
            }
        }
    }
Last updated