Многопоточные подзапросы с использованием Entity Framework выдают ошибки

#multithreading #entity-framework #iqueryable #task-parallel-library #objectcontext

#многопоточность #entity-framework #iqueryable #задача-параллельная-библиотека #objectcontext

Вопрос:

Я пытался обновить производительность своего кода в отношении запросов к базе данных. Проблема, с которой я в настоящее время сталкиваюсь, заключается в том, что, похоже, я не могу найти способ получить новый контекст для каждого подзапроса.

Использование приведенного ниже упрощенного кода приведет к непоследовательному появлению сообщения «Сбой базового поставщика при открытии».

 using (var context = getNewContextObject())
{
    var result = new SomeResultObject();
    var parentQuery = context.SomeTable.Where(x => x.Name = "asdf");

    Parallel.Invoke(() =>
        {
            result.count1 = parentQuery.Where(x => x.Amount >= 100 amp; x.Amount < 2000).Count();
        }, () =>
        {
            result.count2 = parentQuery.Where(x => x.Amount < 100).Count();
        }
        , () =>
        {
            result.count3 = parentQuery.Where(x => x.Amount >= 2000).Count();
        }
    );

}
  

Пока единственным способом обойти это, по-видимому, является перестроение всего запроса для каждого подзапроса с новым контекстом. Есть ли какой-либо способ избежать построения каждого запроса снизу вверх с новым контекстом? Могу ли я вместо этого просто присоединять каждый запрос подзапроса к новому контексту? Я ищу что-то вроде приведенного ниже.

     Parallel.Invoke(() =>
        {
            var subQuery = parentQuery.Where(x => x.Amount >= 100 amp; x.Amount < 2000).Count();
            subQuery.Context = getNewContextObject();
            result.count1 = subQuery.Count();

        }, () =>
        {
            var subQuery = parentQuery.Where(x => x.Amount < 100).Count();
            subQuery.Context = getNewContextObject();
            result.count2 = subQuery.Count();
        }
        , () =>
        {
            var subQuery = parentQuery.Where(x => x.Amount >= 2000).Count();
            subQuery.Context = getNewContextObject();
            result.count3 = subQuery.Count();
        }
    );

}
  

Ответ №1:

Я не уверен, как именно это связано с вашей проблемой, но ни одна из функций EF не является потокобезопасной, поэтому я ожидаю, что параллельное выполнение нескольких запросов к одному экземпляру контекста может привести к сбоям по любой причине. Например, они обращаются к одному и тому же свойству соединения в контексте, но потоки не знают друг о друге, поэтому они могут закрыть соединение с другим потоком или заменить экземпляр другим экземпляром соединения (вы можете попытаться открыть соединение вручную перед запуском параллельных потоков и закрыть его, как только все потоки будут выполнены, но это не обязательно должно быть единственной проблемой).

Комментарии:

1. Это именно та проблема; Для работы OP потребуется 3 объекта контекста, или, как вы предлагаете, вручную открыть соединение и закрыть … но я все равно чувствовал бы себя лучше в отношении 3 контекстов или объектов подключения.