Подтвердить что ты не робот

С#: добавление контекста в Parallel.ForEach() в ASP.NET

У меня есть статический класс со статическим свойством get, и в этом свойстве я делаю это:

// property body
{
    // HttpContext.Current is NOT null
    ...

    Parallel.ForEach(files, file =>
    {
        // HttpContext.Current is null
        var promo = new Promotion();
        ...
    });
    ...

    // HttpContext.Current is NOT null
}

Этот статический класс не подвергается инициализации типа до тех пор, пока представление не использует это свойство.

Проблема состоит в том, что статический конструктор Promotion, который инициализируется при создании new Promotion() при создании new Promotion() при использовании Parallel.ForEach(), использует HttpContext.Current. Когда promo создается в рамках этого Parallel.ForEach(), HttpContext.Current - null, и поэтому new Promotion() вызывает исключение.

HttpContext.Current не является нулевым в статическом свойстве get, потому что он не вызывается до тех пор, пока его не использует (и поэтому существует HttpContext.Current).

Если Promotion использовал HttpContext.Current в своих экземплярах вместо своих статических членов, я мог бы просто передать HttpContext.Current в конструктор new Promotion():

 var context = HttpContext.Current;
 Parallel.ForEach(files, file =>
 {
     var promo = new Promotion(context);
 });

Но так как static членам Promotion нужен HttpContext.Current, я не могу. Я мог бы, вероятно, перепроектировать класс Promotion, чтобы изменить статические члены, которым он нужен, чтобы быть членами экземпляра, но они являются статичными по какой-либо причине - было бы большой штраф за производительность, если бы все члены, которые были статичными, должны были быть определены вместо на каждом экземпляре каждый раз, когда был создан экземпляр new Promotion.

Каковы возможные обходные пути для этого? Я не понял, что HttpContext.Current будет пустым в пределах Parallel.ForEach().

4b9b3361

Ответ 1

HttpContext.Current имеет значение null, потому что он работает в "не-веб-потоках". Если вы разветвляли какой-то код с помощью new Thread(...), он был бы точно таким же. TPL несколько скрывает это, но вам все же необходимо понять, что каждая итерация в вашем Parallel.ForEach может потенциально запускаться в другом потоке и обрабатывать ее соответствующим образом.

В частности, если вы хотите использовать какой-либо класс или метод из веб-запроса (и Parallel.ForEach - это такое использование), вы просто не можете использовать HttpContext.Current. Обходной путь заключается в том, чтобы явно передать HttpContext (или HttpContextBase для улучшения проверки) в конструкторе (или в качестве параметра метода)

Вкратце: вам нужно выйти из использования HttpContext.Current статически.

Ответ 2

Просто передайте любой контекст, который у вас есть за пределами Parallel.ForEach, вызовите любые функции, которые вы вызываете внутри, которые полагаются на указанный контекст.

var context = HttpContext.Current;
Parallel.ForEach(items, item =>
    {
        DoSomething(item, context);
    }
);



private static void DoSomething(item, context = null) {
    if (context == null) context = HttpContext.Current;

    ...
}

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

Ответ 3

Как указывает Маурисио, HttpContext.Current зависит от текущего исполняемого потока. Мне кажется необычным, что статический конструктор зависит от такого неотъемлемо переходного значения, как HttpContext.Current, но, возможно, это была не ваша идея.

Если вы можете изменить класс Promotion, это будет первый вариант, который я бы рассмотрел.

Если нет, вам нужно как-то принудительно инициализировать тип для Promotion в точке, где HttpContext.Current все еще действителен. Чтобы узнать, что инициализирует тип силы, прочитайте этот пост в блоге Jon Skeet.

Опцией может быть создание объекта фиктивного Promotion (только один раз во всей программе должно быть достаточно). Если это не вариант, вы можете попробовать, прочитав свойство с отражением. Я не знаю, инициализирует ли этот тип инициализации, но я так думаю.

Ответ 4

Это не работает, потому что внутри foreach создается новый поток, поэтому контекст имеет значение null. Даже создавая метод DoSomething, чтобы установить контекст curren, контекст по-прежнему является нулевым.