zcz527 / autofac

Automatically exported from code.google.com/p/autofac
Other
0 stars 0 forks source link

AutofacDependencyResolver.Current Only Works in Request Lifetime #400

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
To fix issue 351 AutofacDependencyResolver.Current was fixed to be

DependencyResolver.Current.GetService<AutofacDependencyResolver>()

To get the instance of the resolver into the DependencyResolver, a registration 
is added on the fly during the creation of the RequestLifetimeScope:

builder.RegisterInstance(this).AsSelf();

However, this creates a circular dependency for components that execute BEFORE 
the request lifetime is instantiated by a call to 
DependencyResolver.Current.GetService<T>().

For example, if you have a component that executes 
AutofacDependencyResolver.Current.RequestLifetimeScope during a request but 
BEFORE DR.C.GetService is called, an exception will happen because 
AutofacDependencyResolver.Current is null - the on-the-fly registration hasn't 
happened because it happens during RequestLifetimeScope.

To illustrate how this might work in a unit test, first create a simple request 
lifetime scope provider that isn't tied to the actual request:

public class SimpleLifetimeScopeProvider : ILifetimeScopeProvider
{
  private readonly IContainer _container;
  private ILifetimeScope _scope;

  public SimpleLifetimeScopeProvider(IContainer container)
  {
    if (container == null)
    {
      throw new ArgumentNullException("container");
    }
    this._container = container;
  }

  public ILifetimeScope ApplicationContainer
  {
    get { return this._container; }
  }

  public void EndLifetimeScope()
  {
    if (this._scope != null)
    {
      this._scope.Dispose();
      this._scope = null;
    }
  }

  public ILifetimeScope GetLifetimeScope(Action<ContainerBuilder> configurationAction)
  {
    if (this._scope == null)
    {
      this._scope = (configurationAction == null)
             ? this.ApplicationContainer.BeginLifetimeScope("AutofacWebRequest")
             : this.ApplicationContainer.BeginLifetimeScope("AutofacWebRequest", configurationAction);
    }
    return this._scope;
  }
}

Then create a new AutofacDependencyResolver with that lifetime scope provider:

DependencyResolver.SetResolver(
  new AutofacDependencyResolver(container,
    new SimpleLifetimeScopeProvider(container)));

Finally, try getting the request scope:

var scope = AutofacDependencyResolver.Current.RequestLifetimeScope;

Technically this SHOULD instantiate the lifetime scope on the first request, 
but instead AutofacDependencyResolver.Current comes back null.

Original issue reported on code.google.com by travis.illig on 7 Jan 2013 at 9:05

GoogleCodeExporter commented 8 years ago
I'm curious if AutofacDependencyResolver.Current is valuable as a static. 
Should it be an extension on IDependencyResolver? 
DependencyResolver.Current.AutofacResolver() or something, so the request 
lifetime scope can be created first.

Alternatively... should a "base" lifetime scope be inserted between the 
container and the request lifetime scope? For example, you create the resolver 
and it does something like:

public AutofacDependencyResolver(IContainer container)
{
  this._rootLifetimeScope = container.BeginLifetimeScope(b => b.RegisterInstance(this).AsSelf());
}

and the request scope is from the root lifetime:

public ILifetimeScope GetLifetimeScope()
{
  return this._rootLifetimeScope.BeginLifetimeScope("AutofacWebRequest");
}

Of course, that would throw a wrench in the works for folks resolving things 
right out of the container and expecting sharing between non-request lifetime 
stuff in the resolver and resolutions right out of the container. That could be 
a breaking change for a few edge cases.

Original comment by travis.illig on 7 Jan 2013 at 9:16

GoogleCodeExporter commented 8 years ago
(I'll fix it if it gets assigned to me but I need another set of eyes on it 
first.)

Original comment by travis.illig on 7 Jan 2013 at 9:17

GoogleCodeExporter commented 8 years ago
Any on-the-fly registration should also be As<AutofacDependencyResolver>() 
rather than AsSelf() so folks can derive/override and still have proper 
behavior.

Original comment by travis.illig on 7 Jan 2013 at 9:48

GoogleCodeExporter commented 8 years ago
My bad. I found the problem and it all boiled down to the AsSelf vs 
As<AutofacDependencyResolver> registration. It should have occurred to me that 
AutofacDependencyResolver.Current calling DR.Current.GetService would 
automatically instantiate the request lifetime scope.

Technically this still means you can only get the current ADR in a request 
lifetime, but changing the registration type will fix the issue for me. I'll 
commit the fix shortly. Sorry for the panic mode. :)

Original comment by travis.illig on 7 Jan 2013 at 10:03

GoogleCodeExporter commented 8 years ago
Pushed, including unit tests.

Original comment by travis.illig on 7 Jan 2013 at 10:22