当前位置: 首页 > 知识库问答 >
问题:

在上一个异步操作完成之前,在此上下文上启动了第二个操作

霍襦宗
2023-03-14

消息:

"System.NotSupportedException was unhandled
Message: An unhandled exception of type 'System.NotSupportedException' occurred in mscorlib.dll
Additional information: A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe."

代码:

public async Task<IEnumerable<UserLangDTO>> ImportLang(int userId)
{
    var userLangs = new List<UserLangDTO>();
    using (FirstContext ctx = new FirstContext())
    {
        if (await (ctx.UserLang.AnyAsync(u => u.UserId == userId)) == false)
            //some exception here

        userLangs = await ctx.UserLang.AsNoTracking()
                                .Where(ul => ul.UserId == userId)
                                .Join(ctx.Language,
                                    u => u.LangID,
                                    l => l.LangID,
                                    (u, l) => new { u, l })
                                .Join(ctx.Level,
                                    ul => ul.u.LevelID,
                                    le => le.LevelID,
                                    (ul, le) => new { ul, le })
                                .Select(r => new UserLangDTO
                                {
                                UserId = r.ul.u.UserId,
                                Language = r.ul.l.Language,
                                Level = r.le.Level,
                                }).ToListAsync().ConfigureAwait(false);

    }
    using (SecondContext ctx = new SecondContext())
    {
        if ( await (ctx.UserLangs.AnyAsync(u => u.UserId == userId)) == true && userLangs.Any())
            ctx.UserLangs.RemoveRange(ctx.UserLangs.Where(u => u.UserId == userId));
        if (await hasUserLangs && userLangs.Any())
        {
            userLangs.ForEach(async l =>
            {
                var userLanguage = new UserLang();
                userLanguage.UserId = userId;
                userLanguage.LanguageId = await ctx.Languages.AsNoTracking()
                                                 .Where(la => la.NameEn == l.Language)
                                                 .Select(la => la.Id).FirstOrDefaultAsync().ConfigureAwait(false);
                userLanguage.LevelId = await ctx.Levels.AsNoTracking()
                                                .Where(la => la.NameEn == l.Language)
                                                .Select(la => la.Id).FirstOrDefaultAsync().ConfigureAwait(false);

                ctx.UserLangs.Add(userLanguage);
            });
        }
        await ctx.SaveChangesAsync().ConfigureAwait(false);
    }
    return userLangs;
}

我尝试的:

我不知道我做错了什么,我尝试了不同的方法,比如:

1.

await Task.Run(() => Parallel.ForEach(strings, s =>
{
    DoSomething(s);
})); 

2.

var tasks = userLangs.Select(async l =>
{
    //rest of the code here
}
await Task.WhenAll(tasks); 

3.

var tasks = userLangs.Select(async l =>
{
    //rest of the code here
}
await Task.WhenAll(tasks);

await ctx.SaveChangesAsync().ConfigureAwait(false); 

我做错了什么?

共有2个答案

宇文和同
2023-03-14

对于那些有能力使用的人。NET 6,现在有一个并行。ForEachAsync()

https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.parallel.foreachasync?view=net-6.0 https://www.hanselman.com/blog/parallelforeachasync-in-net-6

林冥夜
2023-03-14

这是你的问题:

userLangs.ForEach(async

这将创建一个异步void方法,因为ForEach不理解异步委托。因此,ForEach的主体将并发运行,实体框架不支持并发异步访问。

将ForEach更改为ForEach,您应该表现良好:

foreach (var l in userLangs)
{
  var userLanguage = new UserLang();
  userLanguage.UserId = userId;
  userLanguage.LanguageId = await ...
}

有关更多信息,请参阅我的异步最佳实践文章中的“避免异步无效”指导。

 类似资料:
  • 在winform应用程序中使用EntityFrmework 6.2异步方法时,我遇到了一个问题。我们使用自定义类来管理基于单个活动设置的同步或异步EntityFrmework访问。当我们使用同步访问时,一切正常。当我们使用异步调用时,我们遇到了这个问题:在上一个异步操作完成之前,在此上下文上启动了第二个操作。使用“await”确保在该上下文上调用另一个方法之前已完成任何异步操作。任何实例成员都不能

  • 在我的控制器中,我在保存到DB的日期之后执行另一个Void函数。但有时会出现这种错误: 在上一个异步操作完成之前,在此上下文上启动了第二个操作。使用“await”确保在该上下文上调用另一个方法之前已完成任何异步操作。任何实例成员都不能保证线程安全。 ...下面是我的控制器示例: 下面是Void方法: 我怎么解决这个问题?

  • 我是Selenium的新手。我正在尝试使用Selenium自动执行网页中表格的过滤(按type=New)和排序(按日期降序)步骤。我使用函数sendKeys()和Click()进行这些操作。 我在sendKeys和click函数之间使用了Thread.sleep(1000 ),以便在对列表进行排序之前按照类别对其进行过滤。 有没有办法让代码等到页面在sendKeys()之后被过滤,然后继续点击操作

  • 我得到了关于在上一个异步操作结束之前在上下文上启动第二个异步操作的错误,但我没有看到它发生在哪里。这是我正在使用的代码。每个异步调用都使用await,那么我做错了什么? 这是Web API 2调用上的一种方法。 当它到达最后一行时,就是抛出异常的地方。 异常消息: 在上一个异步操作完成之前,在此上下文上启动了第二个操作。使用“await”确保在该上下文上调用另一个方法之前已完成任何异步操作。任何实

  • 我有一个带有Vaadin集成(v14)的Spring Boot项目。我希望我的应用程序做一些后台操作,并在基于Vaadin的前端上表示结果。为此,我有一个视图,它是用Vaadin Designer(.js)生成的聚合物模板,并连接到Java companion类。在这个视图中,我只是简单地添加了一个用以下监听器初始化的按钮: 我正试图访问文档中所说的UI。然而,当它被执行时,它只到达第一个“等待”

  • 问题内容: 我不确定如何处理这种情况,因为我是iOS开发和Swift的新手。我正在像这样执行数据获取: 我的loadShows()函数解析从加载到UIWebView的网站中获取的大量数据。问题是我在loadShows函数中有一个等待10秒钟左右的计时器。这允许页面中的javascript在开始解析数据之前完全加载。我的问题是完成处理程序在我的loadShows()之前完成。 我想做的是为“ isC