在先前的异步操作完成之前,在该上下文上开始第二操作

Ger*_*hes 16 .net c# parallel-processing asynchronous

信息:

"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."
Run Code Online (Sandbox Code Playgroud)

码:

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;
}
Run Code Online (Sandbox Code Playgroud)

我试过的:

我不确定我做错了什么,我尝试了不同的东西,比如:

1.

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

2.

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

3.

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

await ctx.SaveChangesAsync().ConfigureAwait(false); 
Run Code Online (Sandbox Code Playgroud)
  1. 其他尝试和错误尝试,我现在不报复

我究竟做错了什么?

Ste*_*ary 35

这是你的问题:

userLangs.ForEach(async
Run Code Online (Sandbox Code Playgroud)

这是创建一个async void方法,因为ForEach不了解异步委托.因此,ForEach将同时运行主体,并且Entity Framework不支持并发异步访问.

更改ForEachforeach,你应该是不错的:

foreach (var l in userLangs)
{
  var userLanguage = new UserLang();
  userLanguage.UserId = userId;
  userLanguage.LanguageId = await ...
}
Run Code Online (Sandbox Code Playgroud)

有关更多信息,请参阅我的异步最佳实践文章中的"避免异步无效"指南.