【问题标题】:UOW - A second operation started on this context before a previous asynchronous operation completedUOW - 在前一个异步操作完成之前在此上下文上启动了第二个操作
【发布时间】:2015-02-04 20:55:18
【问题描述】:

我正在尝试以下代码,它有两个部分,一个是通过棱镜导航。 当允许导航时,我会异步开始深度加载,但每次都使用新的上下文。在以后的代码中,我想取消未完成此加载的挂起导航,但下面的代码甚至不起作用,所以取消是以后的事情;-)

导航逻辑:这里没有问题

public void OnNavigatedTo(NavigationContext navigationContext)
{
    int relatieId = (int)navigationContext.Parameters["RelatieId"];
    if (_relatie != null && _relatie.RelatieId == relatieId) return;

    loadRelatieAsync(relatieId);
}

public void ConfirmNavigationRequest(NavigationContext navigationContext, Action<bool> continuationCallback)
{
    bool navigationAllowed = true;
    continuationCallback(navigationAllowed);
}

深度加载逻辑:

private async Task loadRelatieAsync(int relatieId)
{
    try
    {
        await Task.Run(async () =>
        {

            _unitOfWork = _UnitOfWorkFactory.createUnitOfWorkAsync();

            IEnumerable<Relatie> relaties = await getRelatieAsync(_unitOfWork, relatieId).ConfigureAwait(true);

            _relatieTypeTypes = await getRelatieTypeTypesAsync(_unitOfWork, relatieId).ConfigureAwait(true);
            _relatie = relaties.FirstOrDefault();

            _unitOfWork.Dispose();
        }).ConfigureAwait(true);

        processRelatie(_relatie);

        processRelatieTypes(_relatie, _relatieTypeTypes);
    }
    catch (Exception Ex)
    {

        MessageBox.Show(Ex.Message);
        throw;
    }

}

private async Task<IEnumerable<Relatie>> getRelatieAsync(IUnitOfWorkAsync unitOfWork, int relatieId)
{

    IEnumerable<Relatie> relaties = null;
    try
    {
        IRepositoryAsync<Relatie> relatieRepository = unitOfWork.RepositoryAsync<Relatie>();
        relaties = await relatieRepository
            .Query(r => r.RelatieId == relatieId)
            .Include(i => i.BegrafenisOndernemer)
            .SelectAsync()
            .ConfigureAwait(false);

        IRepositoryAsync<Adres> adresRepository = unitOfWork.RepositoryAsync<Adres>();
        //exception is thrown after executing following line
        var adressen = await adresRepository
            .Query(r => r.RelatieId == relatieId)
            .Include(i => i.AdresType)
            .SelectAsync()
            .ConfigureAwait(false);
        _relatieTypeRepository = unitOfWork.RepositoryAsync<RelatieType>();
        var relatieTypes = await _relatieTypeRepository
            .Query(r => r.RelatieId == relatieId)
            .SelectAsync()
            .ConfigureAwait(false);
    }
    catch (Exception Ex)
    {
        MessageBox.Show(Ex.Message);//exception is shown here
        throw;
    }
    return relaties;
}

private async Task<IEnumerable<RelatieTypeType>> getRelatieTypeTypesAsync(IUnitOfWorkAsync unitOfWork, int relatieId)
{

    IEnumerable<RelatieTypeType> relatieTypeTypes = null;
    try
    {
        IRepositoryAsync<RelatieTypeType> relatieTypeTypeRepository =
            unitOfWork.RepositoryAsync<RelatieTypeType>();

        relatieTypeTypes = await relatieTypeTypeRepository
            .Query()
            .SelectAsync()
            .ConfigureAwait(false);

    }
    catch (Exception Ex)
    {
        MessageBox.Show(Ex.Message);
        throw;
    }
    return relatieTypeTypes;
}

我不断收到异常,好像我忘记等待了,但事实并非如此。 每当我想在 GUI 线程上继续时,我也会正确使用 configureawait(true)。但是我在深度加载逻辑中不断收到这个错误。工作单元和存储库类也使用异步等待机制,但我也在等待。

在前一个异步操作完成之前,在此上下文上启动了第二个操作。使用 'await' 确保在此上下文中调用另一个方法之前所有异步操作都已完成。不保证任何实例成员都是线程安全的。

编辑(删除记录器代码以减少代码大小)

【问题讨论】:

  • 请注意 createUnitOfWorkAsync() 不是异步方法。它只是创建了一个支持异步等待范式的类。
  • createUnitOfWorkAsync 确实创建了一个 new 上下文,对吗?
  • ConfigureAwait(true); 这是第一个计时器..
  • ConfigureAwait(true) 是默认行为,您不需要添加它。
  • 那么对于 SO id 建议删除不必要的代码,以便人们更容易找到代码的相关部分来帮助您(例如,在所有这些日志记录之间很难找到您的问题)。

标签: c# async-await entity-framework-6


【解决方案1】:

问题是这段代码:

_unitOfWork = _UnitOfWorkFactory.createUnitOfWorkAsync();

IEnumerable<Relatie> relaties = await getRelatieAsync(_unitOfWork, relatieId).ConfigureAwait(true);

_relatieTypeTypes = await getRelatieTypeTypesAsync(_unitOfWork, relatieId).ConfigureAwait(true);
_relatie = relaties.FirstOrDefault();

 _unitOfWork.Dispose();

UnitOfWork 是一个实例变量,当场景第二次开始时,它会被一个新实例覆盖。已经执行的场景然后使用新的 UnitOfWork 而不是它自己的,因为它被覆盖了。 不是那么容易发现,而是一个简单的比赛条件。 我通过用局部变量替换所有实例变量找到它,然后问题就消失了。

【讨论】:

  • 当您说“通过将实例变量替换为本地变量来解决此问题”时,您是指方法中的变量并将它们替换为在实例类中作为字段创建的变量吗?只是对知识感到好奇。
  • 我的意思是相反的,局部变量具有声明它的方法的范围,而实例变量的范围与它包含的对象的实例一样长。
  • 好的,我明白你的意思了。我还在学习。我认为我需要更多地了解使用 async/await 编程,因为我在不完全了解的情况下这样做,并且遇到了与上述相同的问题。当我改回直接同步时,我不再有这些问题了。
  • 你救了我的命!!
【解决方案2】:

这可能不是答案,而是对您的代码的一般查看。

async/await 的主要目的是保持当前线程可用。这有助于不阻塞 UI 线程并保持您的应用响应。

您已经确保深度加载发生在 ThreadPool 线程上,因为您完全使用 Task.Run() 来启动它。通过在加载机制中使用 EntityFramework 的默认同步方法,您可能可以解决大部分问题。

乍一看,您的代码在异步调用时看起来很好。也许你的深度加载被触发了多次?

【讨论】:

  • 感谢您的评论,我这样做是因为我希望能够使用取消令牌在下一个增量中取消操作。方法 loadRelatieAsync 被多次调用并同时运行,因为还没有取消,但这并不重要,因为我每次都使用不同的上下文。它是在工作单元创建之下创建的。你说得对,我正在尝试尽可能多地解除对 GUI 线程的阻塞,但我也希望能够取消挂起的导航操作
  • 您仍然只能检查您的操作之间的取消请求,这不会有任何区别。
  • 你为什么不检查加载操作是否已经开始并返回它的任务,如果它已经存在?您还 100% 确定您的上下文吗?因为当正在进行的交易导致您的错误时,这很可能是问题所在。
猜你喜欢
  • 2017-03-14
  • 1970-01-01
  • 2014-12-08
  • 2021-05-04
  • 1970-01-01
  • 2022-01-01
  • 2020-01-22
  • 1970-01-01
  • 2018-11-07
相关资源
最近更新 更多