grandchamp / Identity.Dapper

Identity package that uses Dapper instead EntityFramework for use with .NET Core
MIT License
268 stars 62 forks source link

DapperUserStore is never getting disposed #81

Open briankitt opened 5 years ago

briankitt commented 5 years ago

I implemented this with the GitHub project for Identity 4 Server. But my connection pool keeps filling up and hanging the server. I have to recycle the server to clear it up. I've narrowed it down to the UserStore object never getting disposed of, thus, the connections never get released.

Has anyone ever seen this happen? Here is my configuration (non relevant pieces removed).

    public void ConfigureServices(IServiceCollection services)
    {
        services.ConfigureDapperConnectionProvider<SqlServerConnectionProvider>(_configuration.GetSection("DapperIdentity"))
            .ConfigureDapperIdentityCryptography(_configuration.GetSection("DapperIdentityCryptography"))
            .ConfigureDapperIdentityOptions(new DapperIdentityOptions { UseTransactionalBehavior = false }); //Change to True to use Transactions in all operations
        services.AddIdentity<Model_CustomUser, Model_CustomRole>(x =>
        {
            x.Password.RequireDigit = false;
            x.Password.RequiredLength = 4;
            x.Password.RequireLowercase = false;
            x.Password.RequireNonAlphanumeric = false;
            x.Password.RequireUppercase = false;

            x.User.RequireUniqueEmail = false;
            x.User.AllowedUserNameCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 #`~!%^*()+-={}|[]:;<>?s,.'_@&";
        })
        .AddDapperIdentityFor<SqlServerConfiguration>()
        .AddDefaultTokenProviders();

        services.AddTransient<IProfileService, CustomProfileService>();

        var builder = services.AddIdentityServer(options =>
            {
                options.Events.RaiseErrorEvents = true;
                options.Events.RaiseInformationEvents = true;
                options.Events.RaiseFailureEvents = true;
                options.Events.RaiseSuccessEvents = true;
            })
        .AddInMemoryIdentityResources(Config.GetIdentityResources())
        .AddInMemoryApiResources(Config.GetApiResources())
        .AddInMemoryClients(Config.GetClients())
        .AddAspNetIdentity<Model_CustomUser>()
        .AddProfileService<CustomProfileService>();

    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseIdentityServer();
        app.UseMvcWithDefaultRoute();
        app.UseMvc();
    }

Here is the constructor to my login controller: public partial class AccountController : Controller, IDisposable { private readonly UserManager _userManager; private readonly SignInManager _signInManager; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events;

    public AccountController(
        UserManager<Model_CustomUser> userManager,
        SignInManager<Model_CustomUser> signInManager,
        IIdentityServerInteractionService interaction,
        IClientStore clientStore,
        IAuthenticationSchemeProvider schemeProvider,
        IEventService events,
    {
        this._userManager = userManager;
        this._signInManager = signInManager;
        this._interaction = interaction;
        this._clientStore = clientStore;
        this._schemeProvider = schemeProvider;
        this._events = events;
    }
}

}

And here is my login method:

            var result = await _signInManager.PasswordSignInAsync(thisModel.userName, thisModel.password, thisModel.rememberLogin, lockoutOnFailure: true);
            if (result.Succeeded)
            {
                var user = await _userManager.FindByNameAsync(thisModel.userName);
                await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id.ToString(), user.UserName));

                // make sure the returnUrl is still valid, and if so redirect back to authorize endpoint or a local page
                // the IsLocalUrl check is only necessary if you want to support additional local pages, otherwise IsValidReturnUrl is more strict
                if (_interaction.IsValidReturnUrl(thisModel.returnUrl) || Url.IsLocalUrl(thisModel.returnUrl))
                {
                    System.Diagnostics.Trace.WriteLine(thisModel.returnUrl);
                    return Redirect(thisModel.returnUrl);
                }
                return Redirect("~/");
            }

            await _events.RaiseAsync(new UserLoginFailureEvent(thisModel.userName, "invalid credentials"));