如何在 ASP.NET 核心中使用异步 'ForEach'?
How to use async 'ForEach' in ASP.NET Core?
在ASP.NETCore WebAPI项目中,我写了如下界面。第一次请求正常,第二次请求会报错。应该是我对异步编程了解不够。不知道为什么会报错。希望有朋友能给我指点一下。
[HttpGet("role_claims")]
public async Task<IActionResult> role_claims()
{
// var user = await userManager.GetUserAsync(User);
// var roles = await userManager.GetRolesAsync(user);
List<Claim> claims = new List<Claim>();
var roles = this.User.FindAll(ClaimTypes.Role).ToList();
roles.ForEach(async x => {
MyRole role = await roleManager.FindByNameAsync(x.Value);
IList<Claim> list = await roleManager.GetClaimsAsync(role);
Console.WriteLine($"{x.ValueType} {x.Value} {list.Count}");
list.ToList().ForEach(x => claims.Add(x));
});
return Ok(claims.Select(claim => new { claim.Type, claim.Value }).ToArray());
}
错误信息如下
fail: Microsoft.EntityFrameworkCore.Query[10100]
An exception occurred while iterating over the results of a query for context type 'OnePlan.Conf.IdentityDb'.
System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance 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 instance, or wrapping it 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: 'IdentityDb'.
at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Internal.IDbContextDependencies.get_StateManager()
at Microsoft.EntityFrameworkCore.Query.QueryContextDependencies.get_StateManager()
at Microsoft.EntityFrameworkCore.Query.QueryContext.InitializeStateManager(Boolean standAlone)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken) at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func
4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable
1.AsyncEnumerator.MoveNextAsync()
这么少的代码行,你的问题多得令人印象深刻。
从在 EF.Core(可能?)上下文中调用 ToList()
开始,您绝对不应该这样做。异步版本负责工作线程之间的任何 multi-threaded 同步。
那么您正在使用的 ForEach
函数不支持 async
调用,否则您将等待它。它所做的实际上是 运行 什么都没有,然后 return 你的 Ok()
结果,然后处理你的上下文,最后实际上 运行 你的代码在 ForEach
,在处理上下文之后的方式,因此你的问题。
要么在您的实体上使用常规 foreach
,要么编写一个 ForEachAsync
扩展函数并在您的调用中等待它。
在ASP.NETCore WebAPI项目中,我写了如下界面。第一次请求正常,第二次请求会报错。应该是我对异步编程了解不够。不知道为什么会报错。希望有朋友能给我指点一下。
[HttpGet("role_claims")]
public async Task<IActionResult> role_claims()
{
// var user = await userManager.GetUserAsync(User);
// var roles = await userManager.GetRolesAsync(user);
List<Claim> claims = new List<Claim>();
var roles = this.User.FindAll(ClaimTypes.Role).ToList();
roles.ForEach(async x => {
MyRole role = await roleManager.FindByNameAsync(x.Value);
IList<Claim> list = await roleManager.GetClaimsAsync(role);
Console.WriteLine($"{x.ValueType} {x.Value} {list.Count}");
list.ToList().ForEach(x => claims.Add(x));
});
return Ok(claims.Select(claim => new { claim.Type, claim.Value }).ToArray());
}
错误信息如下
fail: Microsoft.EntityFrameworkCore.Query[10100]
An exception occurred while iterating over the results of a query for context type 'OnePlan.Conf.IdentityDb'.
System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance 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 instance, or wrapping it 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: 'IdentityDb'.
at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Internal.IDbContextDependencies.get_StateManager()
at Microsoft.EntityFrameworkCore.Query.QueryContextDependencies.get_StateManager()
at Microsoft.EntityFrameworkCore.Query.QueryContext.InitializeStateManager(Boolean standAlone)
at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable1.AsyncEnumerator.InitializeReaderAsync(AsyncEnumerator enumerator, CancellationToken cancellationToken) at Pomelo.EntityFrameworkCore.MySql.Storage.Internal.MySqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func
4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.Internal.SingleQueryingEnumerable
1.AsyncEnumerator.MoveNextAsync()
这么少的代码行,你的问题多得令人印象深刻。
从在 EF.Core(可能?)上下文中调用 ToList()
开始,您绝对不应该这样做。异步版本负责工作线程之间的任何 multi-threaded 同步。
那么您正在使用的 ForEach
函数不支持 async
调用,否则您将等待它。它所做的实际上是 运行 什么都没有,然后 return 你的 Ok()
结果,然后处理你的上下文,最后实际上 运行 你的代码在 ForEach
,在处理上下文之后的方式,因此你的问题。
要么在您的实体上使用常规 foreach
,要么编写一个 ForEachAsync
扩展函数并在您的调用中等待它。