Как зарегистрировать сервисы с ограниченным образом жизни, ведущие себя как 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
использует область приложения вместо области запроса.