Как зарегистрировать сервисы с ограниченным образом жизни, ведущие себя как PerWebRequest, используя замок Виндзор?

Я работаю над автономным веб-API, используя OWIN, и мне нужно зарегистрировать сервис для каждого стиля жизни, используя Castle Windsor.

Я знаю, что используя HttpContext, я могу добиться этого, используя образ жизни PerWebRequest или HybridPerWebRequestPerThread, но в моем случае у меня нет HttpContext.

Я создал следующее промежуточное ПО owin, которое будет первым, а последнее промежуточное ПО будет выполнено во время запроса:

public class SetupMiddleware : OwinMiddleware
{

    public SetupMiddleware(OwinMiddleware next) : base(next)
    {
        if (next == null)
        {
            throw new ArgumentNullException("next");
        }
    }


    public override async Task Invoke(IOwinContext context)
    {
        IDisposable scope = null;
        try
        {
           //here I am starting new scope
            var container= IoCResolverFactory.GetContainer();
            scope = container.BeginScope();

            await Next.Invoke(context);

        }
        catch (Exception ex)
        {
        }
        finally
        {
            //here I am disposing it
            scope?.Dispose();
        }
    }

И когда я запускаю свое приложение, я регистрирую свой сервис в контейнере IoC следующим образом:

container.BeginScope();
container.Register(Component.For<ICurrentRequestService>().ImplementedBy<CurrentRequestService>().LifestyleScoped());

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

Не могли бы вы подсказать мне, как я могу зарегистрировать сервисы для каждой области, чтобы вести себя как PerWebRequest в моем приложении?

2 ответа

Решение

Как и ожидалось, я должен определить настраиваемую область, которую нужно запустить, и разместить в промежуточном программном обеспечении.

Я создал следующий пользовательский элемент доступа к области действия:

public class OwinWebRequestScopeAccessor : IScopeAccessor
{
    void IDisposable.Dispose() { }

    ILifetimeScope IScopeAccessor.GetScope(CreationContext context)
    {
        IOwinContext owinContext = HttpContext.Current.GetOwinContext();
        string key = SetupMiddleware.EnvironmentKey;
        return owinContext.Environment[key] as ILifetimeScope;
    }
}

Затем я изменил свое SetupMiddleware следующим образом:

public class SetupMiddleware : OwinMiddleware
{
    public const string EnvironmentKey = "WindsorOwinScope";

    public SetupMiddleware(OwinMiddleware next) : base(next)
    {
        if (next == null)
        {
            throw new ArgumentNullException("next");
        }
    }


    public override async Task Invoke(IOwinContext context)
    {

        ILifetimeScope lifetimeScope = new DefaultLifetimeScope();

        try
        {
            context.Environment[EnvironmentKey] = lifetimeScope;

            await Next.Invoke(context);
        }
        catch (Exception ex)
        {
        }
        finally
        {
            context.Environment.Remove(EnvironmentKey);
            lifetimeScope.Dispose();
        }
    }
}

Затем я зарегистрировал свой сервис, используя вышеуказанную область:

container.Register(Component.For<ICurrentRequestService>().ImplementedBy<CurrentRequestService>().LifestyleScoped<OwinWebRequestScopeAccessor>());

PS:

SetupMiddleware должен быть первым промежуточным ПО в конвейере owin.

Я надеюсь, что мой код поможет другим.

Там может быть много областей.

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

Таким образом, SetupMiddleware создаст область, затем в действии контроллера ICurrentRequestService будет решен в этой области, наконец, SetupMiddleware распоряжаться ICurrentRequestService позвонив scope.Dispose(),

Как я понимаю, в настоящее время у вас есть что-то вроде этого:

// when application starts. Executes once
container.BeginScope(); // the 1st (application) scope beginning. 
container.Register(Component.For<ICurrentRequestService>().ImplementedBy<CurrentRequestService>().LifestyleScoped());
var service = IoCResolverFactory.GetContainer().Resolve<ICurrentRequestService>();

//in SetupMiddleware. This code executes on every web request
    var container = IoCResolverFactory.GetContainer(); 
    scope = container.BeginScope(); // the request scope beginning

    // here others middlewares 
    // here you can resolve ICurrentRequestService to use the request scope

    scope?.Dispose(); // the request scope end

Таким образом, service использует область приложения вместо области запроса.

Другие вопросы по тегам