OpenIddict 的依赖注入错误

Posted

技术标签:

【中文标题】OpenIddict 的依赖注入错误【英文标题】:Dependency Injection error with OpenIddict 【发布时间】:2022-01-13 14:55:47 【问题描述】:

我已经做了很多搜索,但没有找到答案。我在使用 IOpenIddictScopeManagerIOpenIddictApplicationManager 时遇到错误,关于使用依赖注入处理连接的连接。

以下是错误:

无法访问已处置的对象。此错误的一个常见原因是释放从依赖注入中解析的上下文,然后尝试在应用程序的其他地方使用相同的上下文实例。如果您在上下文上调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。如果你使用依赖注入,你应该让依赖注入容器负责处理上下文实例。

堆栈跟踪:

System.ObjectDisposedException
  HResult=0x80131622
  Message=Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'IdentDbContext'.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
   at Microsoft.EntityFrameworkCore.DbContext.<SaveChangesAsync>d__54.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at OpenIddict.EntityFrameworkCore.OpenIddictEntityFrameworkCoreScopeStore`3.<CreateAsync>d__14.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at OpenIddict.Core.OpenIddictScopeManager`1.<CreateAsync>d__15.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ValueTaskAwaiter.GetResult()
   at OpenIddict.Core.OpenIddictScopeManager`1.<CreateAsync>d__16.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at OpenIddict.Core.OpenIddictScopeManager`1.<OpenIddict-Abstractions-IOpenIddictScopeManager-CreateAsync>d__47.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Threading.Tasks.ValueTask`1.get_Result()
   at System.Runtime.CompilerServices.ValueTaskAwaiter`1.GetResult()
   at IdentityApi.WebApi.Controllers.OpenIDManagementController.<CreateScopeAsync>d__4.MoveNext() in E:\Source\Workspaces\Identity\IdentityApi\IdentityApi.WebApi\Controllers\OpenIDManagementController.cs:line 45

  This exception was originally thrown at this call stack:
    [External Code]
    IdentityApi.WebApi.Controllers.OpenIDManagementController.CreateScopeAsync(IdentityApi.WebApi.Models.AdministrationModels.ScopePostInputModel, System.Threading.CancellationToken) in OpenIDManagementController.cs

在我的控制器中,我有以下引发错误的构造函数和方法:

        private readonly IOpenIddictApplicationManager _applicationManager;
        private readonly IOpenIddictScopeManager _scopeManager;

        public OpenIDManagementController(IOpenIddictApplicationManager applicationManager, IOpenIddictScopeManager scopeManager)
        
            _applicationManager = applicationManager;
            _scopeManager = scopeManager;
        

        // Create a scope
        [HttpPost("CreateScopeAsync", Name = nameof(CreateScopeAsync))]        
        public async void CreateScopeAsync(ScopePostInputModel scopeInput, CancellationToken cancellationToken)
                    
            if (await _scopeManager.FindByNameAsync(scopeInput.Name, cancellationToken) is null)
            
                var scopeToAdd = new OpenIddictScopeDescriptor
                
                    Name = scopeInput.Name,
                    Description = scopeInput.Description,
                    DisplayName = scopeInput.DisplayName
                ;

                var createResult = await _scopeManager.CreateAsync(scopeToAdd, cancellationToken);  // This errors out for dependancy injection
            
            else
            
                // error here
            
        

这是我的 Statup.cs 的配置部分:

public void ConfigureServices(IServiceCollection services)
        
            services.AddControllers();
            services.AddRazorPages();

            services.AddDbContext<IdentDbContext>(options =>
            
                options.UseSqlServer(
                    Configuration.GetConnectionString("IdentityDB"));

                options.UseOpenIddict();
            );
            services.AddIdentity<ApplicationUsers, ApplicationRoles>(config =>
            
                config.SignIn.RequireConfirmedEmail = true;
                config.SignIn.RequireConfirmedAccount = true;
                config.User.RequireUniqueEmail = true;
                config.Lockout.MaxFailedAccessAttempts = 3;
            ).AddEntityFrameworkStores<IdentDbContext>()
            .AddUserStore<ApplicationUserStore>()
            .AddRoleStore<ApplicationRoleStore>()
            .AddRoleManager<ApplicationRoleManager>()
            .AddUserManager<ApplicationUserManager>()
            .AddErrorDescriber<ApplicationIdentityErrorDescriber>()
            .AddDefaultTokenProviders()
            .AddDefaultUI();

            services.AddDataLibrary();
            
            services.Configure<IdentityOptions>(options =>
            
                options.ClaimsIdentity.UserNameClaimType = Claims.Name;
                options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
                options.ClaimsIdentity.RoleClaimType = Claims.Role;
            );

            services.AddOpenIddict()
                .AddCore(options =>
                
                    options.UseEntityFrameworkCore()
                    .UseDbContext<IdentDbContext>();
                )
                .AddServer(options =>
                
                    options.AllowClientCredentialsFlow()
                    .AllowAuthorizationCodeFlow()
                    .RequireProofKeyForCodeExchange();
                    options.SetAuthorizationEndpointUris("/api/Authorization/Authorize")
                    .SetTokenEndpointUris("/Token");
                    
                    options.RegisterScopes(Scopes.OpenId, Scopes.Email, Scopes.Profile, Scopes.Roles, "DatabaseName");

                    options.SetIssuer(new Uri(Configuration["Jwt:Issuer"]));

                    options.AcceptAnonymousClients();

                    options.DisableAccessTokenEncryption();

                    
                    options.AddDevelopmentEncryptionCertificate()
                          .AddDevelopmentSigningCertificate();

                    options.UseAspNetCore()
                           .EnableAuthorizationEndpointPassthrough()
                           .EnableTokenEndpointPassthrough();
                )
                .AddValidation(options =>
                
                    options.UseLocalServer();

                    options.UseAspNetCore();
                );
            services.AddHostedService<TestData>();  // For development only.
        

我不确定我错过了什么,但无论我做了什么更改,我仍然收到相同的错误。有关如何纠正此错误的任何想法?

【问题讨论】:

async void 似乎是这里的问题。为什么你使用它而不是返回一个动作结果?当您的代码到达第二次调用时,该操作已超出范围,因为 async void 方法已被触发并忘记。框架不会等待请求被正确处理。 【参考方案1】:

async void 似乎是这里的问题。目前尚不清楚您为什么使用它 (async void) 而不是返回操作结果。

框架不会等待请求被正确处理,因此当您的代码到达注入依赖项的第二次调用时,该操作已超出范围,因为 async void 方法被调用为即发即弃。

控制器动作应该返回请求的相关动作结果

例如

[HttpPost("CreateScopeAsync", Name = nameof(CreateScopeAsync))]        
public async Task<IActionResult> CreateScopeAsync(ScopePostInputModel scopeInput, CancellationToken cancellationToken)  
    if (await _scopeManager.FindByNameAsync(scopeInput.Name, cancellationToken) is null) 
        var scopeToAdd = new OpenIddictScopeDescriptor 
            Name = scopeInput.Name,
            Description = scopeInput.Description,
            DisplayName = scopeInput.DisplayName
        ;

        var createResult = await _scopeManager.CreateAsync(scopeToAdd, cancellationToken);

        return Ok(); //Or some other appropriate response;
     else 
        // error here

        return BadRequest(); //Or some other appropriate response;
    

【讨论】:

好吧,不知道这是怎么发生的……它曾经是一个异步任务。现在把它拿回来,因为它正在工作。

以上是关于OpenIddict 的依赖注入错误的主要内容,如果未能解决你的问题,请参考以下文章

关于Spring依赖注入的问题

Webx框架:依赖注入

无法使用依赖注入解析服务类型

Blazor University (51)依赖注入 —— 拥有多个依赖项:错误的方式

带有身份的asp.net核心中的依赖注入错误

Laravel PhpUnit 依赖注入