上下文上的任务和多项操作
Posted
技术标签:
【中文标题】上下文上的任务和多项操作【英文标题】:Task and multiple operations on Context 【发布时间】:2015-04-07 14:15:01 【问题描述】:我有这段代码可以从我的数据库中检索数据:
public async Task<IEnumerable<Member>> GetAllMembersAsync()
var members = Repository.GetAll<Member>();
return await Task.Run(() => members.ToListAsync());
由于未知原因(可能是错误?),我必须使用 Task.Run
才能完成这项工作(存储库只返回一个 DbSet<Member>
。如果我不这样做,我的 UI 将永远挂起。
问题是我不能以这种方式同时进行 2 个数据库操作。如果我这样做,我会收到此错误:
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.
如你所见,我已经在使用await
。
有什么办法可以解决这个问题,让我实际上可以同时进行 2 个数据库操作,从而让它们按顺序运行?
编辑: 调用它的代码:
private async void LoadMembers()
try
var members = await MemberService.GetAllMembersAsync();
Members = new ObservableCollection<Member>(members);
catch (EntityException)
// connection lost?
【问题讨论】:
你能显示调用你的GetAllMembersAsync()
方法的代码吗?
好的,我已经添加了,谢谢:)
如何使用.ContinueWith
进行第二次异步调用?您必须在后续中启动另一项任务,但至少这些任务将是菊花链式的。不过,给这只猫剥皮的方法有很多……
问题是我不知道下一个异步调用是什么——用户可以决定做多件事。它可能是再次检索所有成员,或者做一些完全不同的事情;
然后保留对正在运行的最后一个任务的引用,如果用户继续附加数据获取操作,则将它们添加为延续。只是不要丢弃您对上次任务运行的引用。
【参考方案1】:
答案是否定的。正如错误所示,您不能同时进行 2 个并发 EF 操作。这是一个更彻底的答案:Does Entity Framework support parallel async queries?
除此之外,您无需使用Task.Run
来解决看似死锁的问题。只需使用 ConfigureAwait
确保 async
操作不需要 UI SynchronizationContext
即可完成。 (另外,请确保您不要使用Task.Wait
或Task.Result
阻止async
代码):
public async Task<IEnumerable<Member>> GetAllMembersAsync()
return await Repository.GetAll<Member>().ToListAsync().ConfigureAwait(false);
private async void LoadMembers()
try
var members = await MemberService.GetAllMembersAsync();
Members = new ObservableCollection<Member>(members);
catch (EntityException)
// connection lost?
【讨论】:
那么这个呢:我创建了一个名为 SIngleTask 的 Singleton 用于数据库操作。这个 Singleton 确保只有一个 Task 处于活动状态。如果没有,它使用 ContinueWith。这是一个好的选择吗? @Bv202 有更简单的方法可以序列化async
操作。您可以简单地使用配置为 1 的 SemaphoreSlim.WaitAsync
。
我会通过删除肯定隐藏在某处的等待来解决死锁。 ConfigureAwait(false) 是错误的处理方式。
@Bv202 @usr,你应该两者都做。这与并发的async
EF 操作无关。
非常感谢使用 SemaphoreSlim 的提示!这似乎正是我想要的!【参考方案2】:
我认为您处于死锁状态,因为您没有正确使用 configureawait(true)。 你可以这样做
public async Task<IEnumerable<Member>> GetAllMembersAsync()
var members = Repository.GetAll<Member>();
return await Task.Run(() => members.ToListAsync());
private async void LoadMembers()
try
var members = await MemberService.GetAllMembersAsync().ConfigureAwait(true);
Members = new ObservableCollection<Member>(members);
catch (EntityException)
// connection lost?
操作完成后,如果从GUI线程执行,GUI线程将恢复。
【讨论】:
非常感谢,这似乎可以解决问题!为什么我需要使用这个额外的方法?这不应该是默认行为吗? var members = await MemberService.GetAllMembersAsync().ConfigureAwait(true);更正确。原因是您的 observablecollection 排在下一个并用于 GUI 线程上的绑定操作。结合 var list = await members.ToListAsync().ConfigureAwait(false) 也可以。即使不需要,我也喜欢明确地对 configureawait 进行这些调用,有一个默认值,因为它显示了意图。如果这解决了您的问题,请标记为答案 ;-) 不幸的是,它使我的 UI 在检索数据时挂起... :( 答案很好,但不适用于所提出的问题。问题是如何在同一上下文中同时运行多个数据库操作。 不幸的是,它不起作用。我仍然遇到同样的错误:对上下文的操作已经在运行...以上是关于上下文上的任务和多项操作的主要内容,如果未能解决你的问题,请参考以下文章