Рабочая очередь и пользовательский контекст

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

Я не могу просто сохранить пользователя, потому что, когда HttpContext будет удален, основной объект будет

Код, который может работать в рабочем потоке, требует, чтобы Принципал был корректным для таких вещей, как PrincipalPermissions и т. д.

Кроме того, управление жизненным циклом (IoC) использует области HttpContext для InRequest, возможно ли воссоздать HttpContext с правильным субъектом и т. д.

изменить: подделка HttpContext — это просто хорошая функция для управления временем жизни, и я могу обойти это. Но наш внутренний код сильно зависит от наличия правильного принципала пользователя для потока, поскольку мы используем его для проверки того, имеет ли пользователь доступ к этой части системы. Я бы отметил как ответ, если кто-то может ответить, как сохранить участника-пользователя с идентификатором, ролями и состоянием IsAuthenticated, а затем использовать его в другом потоке.


person Anders    schedule 16.05.2013    source источник


Ответы (3)


Лучшей практикой для использования данных с отслеживанием состояния из HttpContext является создание собственного контекста приложения, который принимает HttpContext в конструкторе (внедренная зависимость).

Ваша бизнес-логика никогда не должна зависеть от HttpContext, а скорее от вашего нового конкретного контекста приложения (который мог быть создан с использованием информации из HttpContext).

Это не только решит ваши вышеуказанные проблемы, но и повысит тестируемость вашего кода.

Пример:

public class MyApplicationContext
{
    public IPrincipal ContextPrincipal { get; set; }

    public MyApplicationContext(HttpContext httpContext)
    {
        // Store the current user principal & identity
        ContextPrincipal = httpContext.User;

        // Need to grab anything else from the HttpContext? Do it here! 
        // That could be cookies, Http request header values, query string 
        // parameters, session state variables, etc.
        //
        // Once you gather up any other stateful data, store it here in 
        // your application context object as the HttpRequest can't be passed 
        // to another thread.
    }

}

public class MyHttpHandler : IHttpHandler
{
    #region IHttpHandler Members

    public bool IsReusable
    {
        // Return false in case your Managed Handler cannot be reused for another request.
        // Usually this would be false in case you have some state information preserved per request.
        get { return true; }
    }

    public void ProcessRequest(HttpContext context)
    {
        // Do some work on another thread using the ThreadPool
        ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), new MyApplicationContext(context));
    }

    public void DoWork(object state)
    {
        // Grab our state info which should be an instance of an 
        // MyApplicationContext.
        MyApplicationContext context = (MyApplicationContext) state;

        // Assign this ThreadPool thread's current principal according 
        // to our passed in application context.
        Thread.CurrentPrincipal = context.ContextPrincipal;

        // Check if this user is authenticated.
        if (context.ContextPrincipal.Identity.IsAuthenticated)
        {
            var userName = context.ContextPrincipal.Identity.Name;
        }

        // Check if this user is an administrator.
        if (context.ContextPrincipal.IsInRole("Administrator"))
        {
        }

        // Do some long-ish process that we need to do on the threadpool 
        // after the HttpRequest has already been responded to earlier.
        //
        // This would normally be some fancy calculation/math, data 
        // operation or file routines.
        for (int i = 0; i < 30; i++)
        {
            Thread.Sleep(1000);
        }
    }

    #endregion
}

Ни интерфейс IPrincipal, ни интерфейс IIdentity явно не предлагают метод удаления. Таким образом, они оба должны быть в порядке, чтобы сохранить ссылку на них. Однако я не тестировал приведенный выше код, я написал его только для этого вопроса.

Если из-за какого-то плохого дизайна они действительно зависят от базового соединения с базой данных для запроса членства в ролях, вам просто нужно будет оценить это ранее в конструкторе контекста вашего приложения, в то время как поставщик проверки подлинности форм HttpContext и asp.net все еще не удален. /закрыто.

Вы всегда можете разобрать принципала и удостоверение и воссоздать новый экземпляр GenericPrincipal и GenericIdentity или даже создать класс удостоверения вашего приложения, который реализует IIdentity. Здесь есть много возможностей для настройки/расширения.

person BenSwayne    schedule 03.06.2013
comment
Проблема в том, что код в спящем цикле вашего потока зависит от принципала, и принципал выйдет из строя после удаления HttpContext. Чтобы прояснить, наша бизнес-логика не имеет какой-либо зависимости от классов System.Web. Но он использует Thread.CurrentPrincipal, который под капотом использует HttpContext (если код запускается под IIS). - person Anders; 03.06.2013
comment
@Anders: еще раз просмотрите код. Первое, что я сделал выше, это назначил Thread.CurrentPrincipal в новом потоке ThreadPool! Я также объяснил, что сам принципал не удален - единственное возможное объяснение состоит в том, что базовый поставщик, который заполняет список ролей принципала, удален, что не позволяет вам проверять роли. Может быть, вам следует опубликовать трассировку стека полученного вами исключения? В любом случае просто выполните эти проверки ранее и сохраните в контексте вашего приложения - или напишите свой собственный IIdentity для хранения дополнительной информации. - person BenSwayne; 03.06.2013

Почему бы вам не использовать вспомогательный класс для хранения необходимой информации? Вы можете создать его во время веб-запроса с соответствующими значениями и передать его в качестве аргумента фоновому рабочему процессу.

Клонирование объекта HTTPContext невозможно из-за внутреннего состояния сеанса сервера. Даже если бы это было возможно, использование его вне реального HTTP-запроса только для проверки значений не кажется хорошим решением.

person nmat    schedule 30.05.2013
comment
Минимальное требование состоит в том, что субъект для потока является правильным, потому что мы используем его для проверки доступа к методу с помощью PrincipalAttributes. Поэтому, если я каким-то образом смогу клонировать участника с идентификатором, ролями и состоянием IsAuthenticated, этого будет достаточно. - person Anders; 30.05.2013

person    schedule
comment
Это не то, что мне нужно, я не хочу создавать http-запрос, я хочу создать httpcontext для фонового рабочего, у которого его нет. На самом деле важнее всего то, что я могу каким-то образом хранить и повторно использовать HttpContext Principal - person Anders; 30.05.2013
comment
вы не можете создать HttpContext в фоновом режиме, поэтому вы можете решить эту проблему, создав httpRequest и посетив страницу, которая находится в вашем приложении. - person Eugene; 31.05.2013
comment
и на вашей логической странице есть HttpContext, как вы хотите. - person Eugene; 31.05.2013
comment
почему вы пытаетесь клонировать основной объект, вы можете запустить свой новый «поддельный httpcontext», используя основной объект, предоставленный пользователем, начать фоновую работу в потоке запроса «ЭТО» и установить свой client.principal = this. запрос.основной - person Eugene; 01.06.2013
comment
Вы не можете, потому что этот принципал удаляется, когда HttpContext - person Anders; 01.06.2013