我有一个 BlogPost
类,我写了一个用来分页读取的方法,如下:
var postQuery = _dbContext.Set<Entities.BlogPost>()
.AsNoTracking()
.Where(p => p.OwnerId == OwnerId && p.IsValid);
var list = await postQuery.OrderByDescending(p => p.DateAdded)
.Skip(( index - 1 ) * size)
.Take(size)
.ToListAsync();
这段代码单独运行的时候基本没有发现问题,但是当我并行的执行 UI 自动化测试的时候,他就会抛出下面的异常:
An exception occurred while iterating over the results of a query for context type 'BlogDbContext'.
System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
at System.Collections.Generic.Dictionary`2.Enumerator.MoveNext()
at System.Linq.Enumerable.WhereEnumerableIterator`1.ToList()
at Microsoft.EntityFrameworkCore.Query.Expressions.SelectExpression.PushDownSubquery()
at Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal.SqlServerQuerySqlGenerator.RowNumberPagingExpressionVisitor.VisitSelectExpression(SelectExpression selectExpression)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Sql.Internal.SqlServerQuerySqlGeneratorFactory.CreateDefault(SelectExpression selectExpression)
at Microsoft.EntityFrameworkCore.Query.Internal.ShaperCommandContext.GetRelationalCommand(IReadOnlyDictionary`2 parameters)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.BufferlessMoveNext(DbContext _, Boolean buffer, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNext(CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.MoveNext(CancellationToken cancellationToken)
EF Core 版本为 2.2.4。请问为什么会出现“集合被修改”这样的异常呢?
排查发现是 UseRowNumberForPaging
导致的问题,去掉 UseRowNumberForPaging
后这个问题就消失了。那 UseRowNumberForPaging
有替代方案吗?
代码没毛病,修改数据的代码发来瞧瞧
修改数据的地方可就太多了,大多是很直接的使用 EF Ext 的 UpdateAsync 进行操作的。相关链接: https://entityframework-extensions.net/update-from-query
@不如隐茶去: 哦,更新数据查询出来有没有ToList()啊
@Jeffcky: 更新数据的时候没有查询操作,所有的查询操作都用了 ToListAsync()
RowNumberPagingExpressionVisitor 的实现代码在这里,我觉得是 UseRowNumberForPaging
本身问题的可能性比较小,可能是应用的问题触发了这个表现
在并发时会使用 LINQ query translation cache
并行操作是怎样的?是每一页都同时去获取数据吗
– Shendu.CC 5年前@Shendu.CC: 就是浏览器同时打开了多个页面而已,其中有读取数据的,也有修改数据的操作。
– 不如隐茶去 5年前