Autofac: any way to allow the innermost reach?

I am currently testing Autofac in a new ASP.NET MVC project after using Ninject, Castle Windsor and other IoC containers in recent years. Therefore, although I know about IoC containers in general, I'm pretty new to Autofac, and I'm still looking for some best practices.

I'm currently trying to figure out if there is a way to resolve the innermost nesting area.

I have the following situation: a component registered as SingleInstance () has a method that creates a nested area of ​​life, providing a configuration action to configure some components as InstancePerLifetimeScope, and inside this nested region allows registered components to do something useful, for example:

ILifetimeScope currentScope = ???; using (var scope = currentScope.BeginLifetimeScope(cb => { cb.RegisterType<X>().InstancePerLifetimeScope(); // ... })) { var comp = scope.Resolve<X>(); // ... } 

The problem is that I would like currentScope to be the innermost region of the lifetime, because I know that X depends on the components inside the innermost region. In the simplest case, which would be, for example, the current scope of the current request. I can, of course, get it using AutofacDependencyResolver.Current.RequestLifetimeScope, but I do not want to use it, since it is not very well tested. Moreover, this coverage of life is not necessarily the most secret.

So, is there a way to find the innermost scope, indicated, for example, by the root container or another ILifetimeScope?

+4
source share
1 answer

At Autofac, the innermost reach is always a container. Using AutofacDependencyResolver, this will be AutofacDependencyResolver.Current.ApplicationContainer

There is no way from the nested area (if all you have is ILifetimeScope ) to β€œgo back” to get to the container. In any case, I'm not necessarily sure that you want to do this.

It looks like your SingleInstance component makes its location mostly with manual registration / resolution of certain components. If the fixed set of types is fixed, I could recommend (if possible) some redesign of your system, so the SingleInstance component will no longer be registered as SingleInstance and instead will be registered as InstancePerDependency, and then take these other elements as constructor parameters.

Instead...

 // 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(); 

You can change it a bit:

 // 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(); 

The idea is that you do not need to do things "on the fly" and "immediate resolution".

If you are stuck with maintenance, you need to use AutofacDependencyResolver.Current.ApplicationContainer if you need an absolute inner scope, but keep in mind that any objects you register before InstancePerHttpRequest will not be resolved if you do so, so you may get into trouble. Instead, it is recommended that you use AutofacDependencyResolver.Current.RequestLifetimeScope . This will do your method:

 var requestScope = AutofacDependencyResolver.Current.RequestLifetimeScope; using (var scope = requestScope.BeginLifetimeScope(cb => { cb.RegisterType<X>().InstancePerLifetimeScope(); // ... })) { var comp = scope.Resolve<X>(); // ... } 

In a test environment, AutofacDependencyResolver allows you to exchange in the provider, which determines how the query time is generated. You can implement a simple / stub, for example:

 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(); } } 

Again, just a stub for unit testing, not what you have ever used in the manufacturing process.

Then, when you plug in DependencyResolver in your test, you provide your lifespan provider:

 var lsProvider = new TestLifetimeScopeProvider(container); var resolver = new AutofacDependencyResolver(container, lsProvider); DependencyResolver.SetResolver(resolver); 

This allows you to use InstancePerHttpRequest and such internal tests without having a real request context. This also means that you should be able to use the scope of the request in your registration / permission method and should not return to the application container.

+4
source

Source: https://habr.com/ru/post/1398257/


All Articles