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

时间:2016-11-01 15:59:38

标签: c# .net asynchronous parallel-processing

消息:

"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); 
  1. 其他尝试和错误尝试,我现在不记得
  2. 我做错了什么?

1 个答案:

答案 0 :(得分:32)

这是你的问题:

userLangs.ForEach(async

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

ForEach更改为foreach,您应该做得很好:

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

有关详细信息,请参阅&#34;避免异步无效&#34;我Async Best Practices article中的指导。