c# - '集合已修改;使用 async/await 可能无法执行枚举操作

标签 c# .net entity-framework async-await enumeration

我有一个简单的单元测试:

[TestMethod]
public void TestInitDatabases()
{
    Debug.WriteLine("Testing loading up all databases!");

    Assert.IsTrue(Library.InitDatabase());
}

ClassLibrary 中的一个方法尝试加载具有 58,139 条记录的实体。

public static bool InitDatabase()
{
    Debug.WriteLine("Loading Entities");

    var startTime = DateTime.Now;

    DContext.Database.CommandTimeout = 180;
    DContext.Database.Log = Console.WriteLine;

    Task task = new Task(async () => await DContext.SA_HistoryHeader.LoadAsync());

    Debug.WriteLine("All Tasks Assigned");

    task.Start();
    task.Wait();
    while (!DContext.SA_HistoryHeader.Local.Any())
    {
        Debug.WriteLine("Task Status " + task.Status);
    }

    try
    {
        Debug.WriteLine("We are about to count the records...");

        while (DContext.SA_HistoryHeader.Local.Count != 58139)
        {
            Debug.WriteLine("OrderHistory has " + DContext.SA_HistoryHeader.Local.Count + " Records");
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine("Count Error: " + ex.Message);
        return false;
    }

    Debug.WriteLine("Total Load Time: " + (DateTime.Now – startTime));

    return true;
}

但是 try/catch 返回:

Count Error: Collection was modified; enumeration operation may not execute.

每次运行测试时。

输出显示:

...
OrderHistory has 1 Records
OrderHistory has 1 Records
OrderHistory has 1 Records
OrderHistory has 1 Records
OrderHistory has 1 Records
OrderHistory has 1 Records
OrderHistory has 1 Records
OrderHistory has 1 Records
A first chance exception of type 'System.InvalidOperationException' occurred in mscorlib.dll
Count Error: Collection was modified; enumeration operation may not execute.
A first chance exception of type 'Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException' occurred in Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll
A first chance exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
A first chance exception of type 'Microsoft.VisualStudio.TestTools.TestTypes.Unit.TestFailedException' occurred in Microsoft.VisualStudio.QualityTools.Tips.UnitTest.Adapter.dll
The thread 'Agent: adapter run thread for test 'TestInitDatabases' with id '97fef094-3e43-4bfa-bb1d-02b70eb4b6f7'' (0x19ac) has exited with code 0 (0x0).
The thread 'Agent: test queue thread' (0x3a0) has exited with code 0 (0x0).
The thread 'Agent: state execution thread for test 'TestInitDatabases' with id '97fef094-3e43-4bfa-bb1d-02b70eb4b6f7'' (0x138c) has exited with code 0 (0x0).
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
A first chance exception of type 'System.Threading.ThreadAbortException' occurred in mscorlib.dll
...

偶尔,我会得到:

...
OrderHistory has 1 Records
OrderHistory has 1 Records
OrderHistory has 513 Records

然后同样的错误。我正在尝试找到加载实体表的最快方法。

编辑:我根据 Yuval Itzchakov 的评论更改了我的方法:

    public static async void InitDatabase()
    {
        Debug.WriteLine("Loading Entities");

        var startTime = DateTime.Now;
        DateTime newTime;


        DContext.Database.CommandTimeout = 180;
        DContext.Database.Log = Console.WriteLine;

        try
        {
            await DContext.SA_HistoryHeader.LoadAsync();
            //DContext.SA_HistoryHeader.Load();
            Debug.WriteLine("Orders has " + DContext.SA_HistoryHeader.Local.Count + " records");
            Debug.WriteLine("OrderHistory Loaded: " + (DateTime.Now - startTime));
            newTime = DateTime.Now;
            await DContext.SA_HistoryDetail.LoadAsync();
            //DContext.SA_HistoryDetail.Load();
            Debug.WriteLine("Details has " + DContext.SA_HistoryDetail.Local.Count + " records");
            Debug.WriteLine("OrderDetails Loaded: " + (DateTime.Now - newTime));
        }
        catch (SqlException ex)
        {
            foreach (SqlError error in ex.Errors)
            Debug.WriteLine("Sql Exception Error: " + error.Message);
            return;
        }
        catch (ThreadAbortException ex)
        {
            Debug.WriteLine("Thread - caught ThreadAbortException - resetting.");
            Debug.WriteLine(string.Format("Exception message: {0}", ex.Message));
            Thread.ResetAbort();
        }
        catch (Exception ex)
        {
            Debug.WriteLine("Load Exception Error: " + ex.Message);
            return;
        }

        Debug.WriteLine("All Tasks Complete: " + (DateTime.Now - startTime));

        return;
    }

当我使用普通的旧加载时,订单实体将在大约 47 秒内加载。加载详细信息实体大约需要 19 分钟(358,501 条记录)。

当我将(注释/取消注释)更改为 LoadAsync 时,我得到的只是线程中止异常。

最佳答案

当前发生的情况是,您实际上正在等待 new Task 创建的外部任务,这意味着当您使用 Task.Wait 时,它将立即返回,而不是返回等待内部任务完成。

当您正在执行的操作是 IO 绑定(bind)时,我认为没有理由创建新任务,您只需等待操作即可:

await DContext.SA_HistoryHeader.LoadAsync()

这将保证操作在您查看 Count 属性之前完成数据收集。

关于c# - '集合已修改;使用 async/await 可能无法执行枚举操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26410192/

相关文章:

c# - CancellationTokenSource 与 volatile bool 值

c# - 如何在运行时动态应用 wpf 数据绑定(bind)

c# - NHibernate 的 T4 模板? - 不流利的 NHibernate

c# - UnitTest 一种依赖于时间的方法

c# - DbMigration.AlterstoredProcedure( Entity Framework 迁移): How to represent type smallmoney?

c# - 在 View 模型中过滤数据后,它不显示/刷新 View

c# - 如果datagridview中的复选框被选中,它将保存到数据库,如果没有,它只会提示选择一行

C# WPF - 只允许某些文件扩展名

entity-framework - 将 .edmx 加载到 DbModelBuilder

c# - EF6 CF 中的外键问题