Вторая операция началась в этом контексте до завершения предыдущей асинхронной операции.Используйте «ждать», чтобы обеспечить - PullRequest
0 голосов
/ 23 сентября 2019

Я борюсь с проблемой использования асинхронного метода 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 Ответ

1 голос
/ 24 сентября 2019

Вопрос: как мы можем вызвать два или более асинхронных EF-запроса из существующего метода синхронизации?

Вам нужны два или более контекста базы данных.У каждого из них может быть только один запрос за один раз.

С другой стороны, я настоятельно рекомендую изменить метод (ы) на async.Пропуск await, подобный этому, является способом «выстрелить и забыть», который имеет две основные проблемы:

  1. Ваш код не может знать, когда операция завершена.Могу ли я повторно использовать этот контекст базы данных сейчас, когда он завершил эту операцию БД?Могу ли я безопасно выйти из приложения, зная, что все обновления базы данных были применены?Это вопросы, на которые нельзя ответить кодом «запусти и забудь».
  2. Ваш код не может знать, была ли операция выполнена успешно.С помощью «огонь и забудь» ваш код должен просто предполагать , что он работал.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...