Considered making the loader lazy when using CreatePerOwinContext ?

Feb 17 at 11:09 AM
When working with AspNet Identity, is there any reason to that CreatePerOwinContext have to create an instance for each request and not just lazy load it when it actually is needed?

At least that the behaviour I am seeing and I am just wondering if its indented, and if so, why. If this is not the case, sorry for the inconvenience.
Feb 18 at 6:32 PM
Edited Feb 18 at 6:32 PM
All this code is not IOC friendly, I tried using autofac but not very easy and not clear especially concerning the various Options containing handlers which must be set on startup AND EF DbContext....any feed back concerning an implentation of autofac for this highly appreciated.
Feb 18 at 6:43 PM
I assume the idea is that you should not use IOC / autofac for this?

Register it with CreatePerOwinContext and get it again with the Get method on IOwinContext.

I have not tried autofac as I normaly just go with Unity. But what is causing your problems with IOC? You could register the UserManager with your DI framework and use it as normal with a factory of the create method that the CreatePerOwinContext also uses.

If its IdentityFactoryOptions that is giving you problems, its simply a
            IdentityFactoryOptions<T> options = new IdentityFactoryOptions<T> {
                DataProtectionProvider = app.GetDataProtectionProvider()
            };
Feb 18 at 9:14 PM
Edited Feb 18 at 9:18 PM
Yes but it is dommage when 98% of a code already uses IOC !

Yes part of pb is in UserMAnager but also in code like this sample
        static Startup()
        {
            PublicClientId      = "self";

            OAuthOptions        = new OAuthAuthorizationServerOptions
            {
                TokenEndpointPath           = new PathString("/api/v1/Token"),
                Provider                            = newMyOAuthProvider(PublicClientId),
                AuthorizeEndpointPath       = new PathString("/api/v1/Account/ExternalLogin"),
                AccessTokenExpireTimeSpan   = TimeSpan.FromDays(14),
#if DEBUG
                AllowInsecureHttp           = true, // to remove in prod
                ApplicationCanDisplayErrors = true,
#else
                AllowInsecureHttp           = false, 
                ApplicationCanDisplayErrors = false,
#endif

                AuthorizationCodeProvider   = new MyAuthenticationTokenProvider(TokenType.Code),
                AccessTokenProvider         = new MyAuthenticationTokenProvider(TokenType.Access),
                RefreshTokenProvider        = new MyAuthenticationTokenProvider(TokenType.Refresh),
                AuthorizationCodeFormat     = new TicketFormatProvider(),
                AccessTokenFormat           = new TicketFormatProvider(),
                RefreshTokenFormat          = new TicketFormatProvider()
            };

        }


        public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }

        public static string PublicClientId { get; private set; }

        // For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
        public void ConfigureAuth(IAppBuilder app)
        {
            app.CreatePerOwinContext(ConnectorDbContext.Create);
            // Configure the UserManager
            app.CreatePerOwinContext<DatwendoUserManager>(DatwendoUserManager.Create);

            // Enable the application to use a cookie to store information for the signed in user
            // and to use a cookie to temporarily store information about a user logging in with a third party login provider
            // Configure the sign in cookie
            app.UseCookieAuthentication(new CookieAuthenticationOptions {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                Provider = new CookieAuthenticationProvider {
                        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<MyUserManager, MyUser, int>(
                        validateInterval: TimeSpan.FromMinutes(20), 
                        regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                        getUserIdCallback:(id)=>(Int32.Parse(id.GetUserId())))
                }
            });


            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

            // Enable the application to use bearer tokens to authenticate users
            app.UseOAuthBearerTokens(OAuthOptions);
Suppose your ticketFormatProvider has some dependency alreday managed by IOC....
And idem for ConnectorDbContext.Create, if it contains some services normally bought by Ioc....
This is the reason why I say this is not easy to integrate in an already Ioc project.
Feb 18 at 9:42 PM
But you dont have to use the CreatePerOwinContext. Correct me if I am wrong, but its not used internal to get the UserManager, but used in the samples like this:
        public RolesAdminController(ApplicationUserManager userManager,
            RoleManager<IdentityRole> roleManager)
        {
            UserManager = userManager;
            RoleManager = roleManager;
        }

        private ApplicationUserManager _userManager;
        public ApplicationUserManager UserManager
        {
            get
            {
                return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
            }
            set
            {
                _userManager = value;
            }
        }
So if you use your own IOC to put the UserManager into the controller, the one from the owin context wont be used and you dont need to register it. But I am just speculating here.
Feb 19 at 8:29 AM
But you dont have to use the CreatePerOwinContext. Correct me if I am wrong, but its not used internal to get the UserManager, but used in the samples like this:
Who knows ? No code available ?
Developer
Apr 17 at 12:03 AM
Yep pksorensen is correct, you are free to retrieve/manage UserManager instances however you like. We only introduced PerOwinContext since we don't have any IOC we can depend on, so we chose to stash it in the OwinContext. You are free to remove that and use whatever IOC you like to get the UserManager out.
Apr 17 at 9:00 AM
Ok, thanks for the confirmation.
What is the Status of project now that the 2.0 is out ? I see some work still goes on, is it bug fixing or new implementation.
What about going Open ?
May 3 at 10:19 AM
Edited May 3 at 10:23 AM
I tried to use unity and PerRequestLifetimeManager to instanciate UserManager but I don't figure out how to send this line of code in UnityConfig.cs
IdentityFactoryOptions<T> options = new IdentityFactoryOptions<T> {
                DataProtectionProvider = app.GetDataProtectionProvider()
            };
since app is not accessible in UnityConfig.RegisterTypes method.

In this method I have that :
container
                .RegisterType<IDbContextAsync, AppDbContext>(new PerRequestLifetimeManager())               
                .RegisterType<IUnitOfWorkAsync, UnitOfWork>(new PerRequestLifetimeManager())
                .RegisterType<AppUserManager>(new PerRequestLifetimeManager())
                .RegisterType<IUserStore<AppUser, string>, UserStoreRepository>(new PerRequestLifetimeManager())
Anyone know how to do ?
Jun 18 at 4:06 PM
Edited Jun 18 at 4:18 PM
Hi, I was also unhappy with the UserManager being created every single request as well so I split the project into layers and removed the CreatePerOwinContext and then used Autofac to supply a new UserManager when needed.

However the only point I noticed to be a problem was in the UseCookieAuthentication middleware, there is a callback 'OnValidateIdentity' that runs a static method on SecurityStampValidator, this is a generic method that refreshes the claims of the user, to ensure that the claims are refreshed.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                        validateInterval: TimeSpan.FromMinutes(1),
                        regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
This method does an internal call to OwinContext.Get<UserManager>() therefore relies on it being present in the OwinContext object, putting a reliance on the UserManager being in the context prior to running and removing the CreatePerOwinContext means it wont be.

You have a couple of options, first you can roll your own SecurityStampValidator.OnValidateIdentity method so that it can accept a 'UserManagerProvider' type that can be stored in the OwinContext rather than the user manager itself, therefore only creating it if the UserManager is in fact required.

Other than that though it was a simple process for me, once I worked out what it was doing.