C #: Добавление контекста в Parallel.ForEach () в ASP.NET - PullRequest
12 голосов
/ 25 сентября 2010

У меня есть статический класс со статическим свойством 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() в 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 участникам Акции нужен HttpContext.Current, я не могу. Я мог бы, вероятно, изменить класс Promotion, чтобы изменить статические члены, которые должны быть членами экземпляра, но они являются статическими по причине - было бы большое снижение производительности, если бы вместо этого были определены все члены, которые были статическими в каждом случае каждый раз, когда new Promotion был создан.

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

Ответы [ 4 ]

11 голосов
/ 25 сентября 2010

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

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

В двух словах: вам нужно прекратить использование HttpContext.Current статически.

4 голосов
/ 11 сентября 2013

Просто передайте любой имеющийся у вас контекст вне вызова 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;

    ...
}

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

0 голосов
/ 19 марта 2015

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

0 голосов
/ 27 сентября 2010

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...