在先前的异步操作完成之前,第二操作在此上下文上开始。使用“等待”来确保

时间:2019-09-23 17:53:14

标签: c# winforms entity-framework-6

我在将EntityFrmework 6.2异步方法应用于winform应用程序时遇到了问题。 我们使用自定义类来管理基于单个活动设置的同步或异步EntityFrmework访问。当我们使用同步访问时,一切正常。当使用异步调用时,我们会遇到此问题:在先前的异步操作完成之前,第二个操作是在此上下文上启动的。使用“ await”来确保在此上下文上调用另一个方法之前,所有异步操作都已完成。不保证任何实例成员都是线程安全的。 我们已经阅读了很多有关此问题的文章,但没有一个可以帮助我们解决问题。

我们知道问题与从同步之一调用异步方法有关,不幸的是,我们无法将调用方法从同步转换为异步。 我们从项目中提取了两种方法,以证明所描述的行为

public void AddLoadingRequest(Func<Task> fnAsync, Action fnSync, Action bsa)
{
    try
    {
        Task task = Task.Run(async () => await DoDataSource(fnAsync, fnSync, bsa));
        _lstTask.Add(task);
        if (task.IsFaulted)
            throw task.Exception;
    }
    catch (Exception ex)
    {
        MessageBox.Show($"Error {ex.Message}");
    }
}

protected async Task DoDataSource(Func<Task> fnAsync, Action fnSync, Action bsa)
{
    try
    {
        if (_bLoadingFKTableAsync == true)
            await fnAsync();
        else
            fnSync();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Loading Error");
        throw;
    }
}

调用方法:

private void btnTest_Click(object sender, EventArgs e)
{
    NWContext wContext = new NWContext();
    Customers cust = wContext.Customers.FirstOrDefault(w => w.CustomerID == "RATTC");
    BindingSource bsCustomer = new BindingSource();
    BindingSource bsOrders = new BindingSource();
    BindingSource bsCustomerDemographics = new BindingSource();
    bsCustomer.DataSource = cust;
    _bLoadingFKTableAsync = true;
    AddLoadingRequest(
            () => wContext.Entry(cust).Collection(typeof(Orders).Name).LoadAsync(),
            () => wContext.Entry(cust).Collection(typeof(Orders).Name).Load(),
            () =>
            {
                bsOrders.SuspendBinding();
                bsOrders.DataSource = cust.Orders;
                bsOrders.ResumeBinding();
            });
    AddLoadingRequest(
            () => wContext.Entry(cust).Collection(typeof(CustomerDemographics).Name).LoadAsync(),
            () => wContext.Entry(cust).Collection(typeof(CustomerDemographics).Name).Load(),
            () =>
            {
                bsCustomerDemographics.SuspendBinding();
                bsCustomerDemographics.DataSource = cust.CustomerDemographics;
                bsCustomerDemographics.ResumeBinding();
            });
}

我们期望EntityFramework在异步模式下加载对象的两个子级集合,同时出现错误:在先前的异步操作完成之前,第二个操作在此上下文上启动。使用“ await”来确保在此上下文上调用另一个方法之前,所有异步操作都已完成。不保证任何实例成员都是线程安全的。

1 个答案:

答案 0 :(得分:0)

  

问题是:我们如何从现有同步方法内部调用两个或多个异步EF请求?

您需要两个或多个数据库上下文。每个人一次只能发送一个请求。

另一方面,我强烈建议您将方法更改为async。像这样跳过await是一种“失火”的方法,它有两个主要问题:

  1. 您的代码无法知道操作何时完成。完成数据库操作后,是否可以重用此数据库上下文?我是否可以在知道所有数据库更新均已应用的情况下安全退出应用程序?这些问题无法用“一劳永逸”的代码来回答。
  2. 您的代码无法知道该操作是否成功。使用“即发即忘”功能,您的代码必须假设有效。