Самостоятельное приложение ServiceStack с областью действия для каждого запроса

Работая с ServiceStack, я столкнулся с проблемой управления временем жизни объектов в собственном веб-приложении.

Мои требования:

  1. Необходимость области действия объектов для каждого запроса.
  2. Я использую Castle Windsor IoC с реализованным адаптером ServiceStack IoC.
  3. Мое приложение размещено на собственном хосте с базовым классом AppHostHttpListenerPoolBase (ServiceStack v4)
  4. Вероятно, когда-нибудь я захочу перейти на IIS, поэтому он должен быть гибким.

Общая проблема:

Castle Windsor IoC реализует собственную стратегию жизненного цикла каждого запроса, но она привязана к http-модулям, поэтому работает только с приложениями, размещенными в IIS. Следовательно, я должен реализовать свой собственный IScopeAccessor (предоставленный Castle Windsor) для обработки времени жизни объектов. Проблема здесь в отсутствии хуков, которые я могу использовать для привязки к текущему запросу.

Данный

public class MyScopeAccessor : IScopeAccessor
{
    public ILifetimeScope GetScope(CreationContext context)
    {
       //implement it
    }
}

Мне нужно реализовать метод GetScope.

Есть две основные идеи, которые я не могу завершить:

Использование [Threadstatic]

В MyScopeAccessor я просто храню

[ThreadStatic] 
private static ILifetimeScope _currentLifetimeScope;

и создайте новую область после первого GetScope, если она еще не инициализирована.

Проблемы:

  1. Трудно утилизировать. Лучший способ избавиться от _currentLifetimeScope — реализовать собственный IServiceRunner (или наследовать от ServiceRunner), переопределяющий метод AfterEachRequest. Но я точно не знаю, действительно ли AfterEachRequest выполняется в потоке запросов.
  2. Переход на IIS может вызвать некоторые проблемы, потому что, насколько я знаю, IIS не гарантирует неизменную привязку между объявлениями и контекстами запросов.

Использование экземпляра IRequest

В MyScopeAccessor я просто храню

private static readonly ConcurrentDictionary<IRequest, ILifetimeScope> LifetimeScopes;

а также создавать и удалять текущую область действия в соответствующих пользовательских методах ServiceRunner (OnBeforeEachRequest, OnAfterEachRequest).

Проблемы:

  1. Я не знаю, как получить доступ к текущему запросу глобально из GetScope, MyScopeAccessor ничего не знает о службах и запросах.

Кроме того, интересно, решает ли эту проблему ServiceStack по умолчанию Funq IoC.


person Valentin P.    schedule 05.10.2014    source источник


Ответы (1)


Funq обрабатывает зависимости RequestScoped, в которых хранится контекст запроса. зависимости в словаре RequestContext.Instance.Items[].

Любые расходные материалы могут быть зарегистрированы в RequestContext.Instance.TrackDisposable() и автоматически удаляются в конце запроса.

В конце каждого запроса запускается AppHost.OnEndRequest(), который проходит и освобождает все зависимости, хранящиеся в RequestContext для этого запроса.

Если ваш Windsor ContainerAdapter реализует интерфейс IRelease, он автоматически вызывается для освобождения любых экземпляров, которые могут быть обработаны самостоятельно. Оба этих API можно переопределить в вашем AppHost, если вы хотите изменить поведение по умолчанию:

public virtual void OnEndRequest()
{
    var disposables = RequestContext.Instance.Items.Values;
    foreach (var item in disposables)
    {
        Release(item);
    }

    RequestContext.Instance.EndRequest();
}

public virtual void Release(object instance)
{
    try
    {
        var iocAdapterReleases = Container.Adapter as IRelease;
        if (iocAdapterReleases != null)
        {
            iocAdapterReleases.Release(instance);
        }
        else
        {
            var disposable = instance as IDisposable;
            if (disposable != null)
                disposable.Dispose();
        }
    }
    catch { /*ignore*/ }
}
person mythz    schedule 05.10.2014
comment
Спасибо. Теперь понятно, как работает Funq. Но главный вопрос заключается в том, как сопоставить текущий контекст запроса из произвольного места кода (включая MyScopeAccessor). С этой способностью я могу делать что угодно и распоряжаться тоже. Кроме того, я не хочу реализовывать часть Castle Windsor, которая уже реализована. Правильно ли я понимаю, что RequestContext.Instance.Items — лучший выбор для идентификации контекста запроса? Это использует его как static ConcurrentDictionary<object, ILifetimeScope> LifetimeScopes; с объектом RequestContext.Instance.Items в качестве ключа. - person Valentin P.; 05.10.2014
comment
И не подходит ли RequestContext.Instance, потому что он кажется синглтоном? - person Valentin P.; 05.10.2014
comment
@ValentinP.RequestContext.Instance является синглтоном, вы можете использовать RequestContext.Instance.Items словарь объектов, который содержит зависимости области запроса для идентификации запроса. За кулисами Items либо использует HttpContext.Current.Items (ASP.NET), либо CallContext.LogicalData для самостоятельного размещения по умолчанию, иначе RequestContext.RequestItems, если RequestContext.UseThreadStatic=true. Подробнее см. в RequestContext. - person mythz; 05.10.2014