В Autofac самой внутренней областью всегда является контейнер.Используя AutofacDependencyResolver, это будет AutofacDependencyResolver.Current.ApplicationContainer
Нет способа из вложенной области видимости (если все, что у вас есть - ILifetimeScope
), чтобы «идти назад», чтобы добраться до контейнера.В любом случае, я не обязательно уверен, что вы хотите это сделать.
Похоже, ваш компонент SingleInstance выполняет какую-то службу, в основном с ручной регистрацией / разрешением определенных компонентов.Если зарегистрированный набор типов является фиксированным, я мог бы порекомендовать (если это возможно) некоторую редизайн вашей системы, чтобы компонент SingleInstance больше не регистрировался как SingleInstance и вместо этого регистрировался как InstancePerDependency, а затем эти другие элементы принимают какПараметры конструктора.
Вместо ...
// Consuming class like this...
public class BigComponent
{
public void DoSomethingCool()
{
using(var scope = ...)
{
var c = scope.Resolve<SubComponent>();
c.DoWork();
}
}
}
// ...and container registrations like this...
builder.RegisterType<BigComponent>().SingleInstance();
Вы можете попробовать немного его инвертировать:
// Consuming class like this...
public class BigComponent
{
private SubComponent _c;
public BigComponent(SubComponent c)
{
_c = c;
}
public void DoSomethingCool()
{
_c.DoWork();
}
}
// ...and container registrations like this...
builder.RegisterType<BigComponent>().InstancePerDependency();
builder.RegisterType<SubComponent>().InstancePerLifetimeScope();
Идея состоит в том, чтобы не включатьоперативная регистрация и немедленное разрешение.
Если вы застряли в процессе определения местоположения службы, вам нужно будет использовать AutofacDependencyResolver.Current.ApplicationContainer
, если вам нужна абсолютная внутренняя область действия, но помните о любомобъекты, которые вы регистрируете в области InstancePerHttpRequest
, не будут разрешены, если вы это сделаете, поэтому у вас могут возникнуть проблемы.На самом деле рекомендуется использовать AutofacDependencyResolver.Current.RequestLifetimeScope
вместо этого.Это сделало бы ваш метод:
var requestScope = AutofacDependencyResolver.Current.RequestLifetimeScope;
using (var scope = requestScope.BeginLifetimeScope(cb => {
cb.RegisterType<X>().InstancePerLifetimeScope();
// ...
}))
{
var comp = scope.Resolve<X>();
// ...
}
В тестовой среде AutofacDependencyResolver
позволяет вам поменять провайдера, который определяет, как генерируются времена жизни запроса.Вы можете реализовать простую / заглушку, например, такую:
public class TestLifetimeScopeProvider : ILifetimeScopeProvider
{
readonly ILifetimeScope _container;
private ILifetimeScope _lifetimeScope = null;
public TestLifetimeScopeProvider(ILifetimeScope container)
{
if (container == null) throw new ArgumentNullException("container");
_container = container;
}
public ILifetimeScope ApplicationContainer
{
get { return _container; }
}
public ILifetimeScope GetLifetimeScope()
{
if (_lifetimeScope == null)
{
_lifetimeScope = ApplicationContainer.BeginLifetimeScope("httpRequest")
}
return _lifetimeScope;
}
public void EndLifetimeScope()
{
if (_lifetimeScope != null)
_lifetimeScope.Dispose();
}
}
Опять же, просто заглушка для модульного тестирования, а не то, что вы никогда не будете использовать в производстве.
Затем, когда вы подключитесьDependencyResolver
в вашем тесте, вы предоставляете провайдера своей жизни:
var lsProvider = new TestLifetimeScopeProvider(container);
var resolver = new AutofacDependencyResolver(container, lsProvider);
DependencyResolver.SetResolver(resolver);
Это позволяет вам использовать InstancePerHttpRequest
и такие внутри модульные тесты без реального контекста запроса.Это также означает, что вы должны иметь возможность использовать область действия запроса в методе регистрации / разрешения и не должны использовать контейнер приложения.