TeaDrivenDev
TeaDrivenDev

Reputation: 6629

Limited singleton lifetime determined by another object

This may be something to be solved with Autofac nested scopes, but I have not been able to make enough of the documentation to figure it out myself.

I think what I am looking for is like a per-HTTP-request singleton, but the place of the request is taken by the lifetime of another object.

There is a class SubSystem, of which a new instance is created (resolved from the container, potentially through a factory class) every time new data is loaded into the application (the old data and SubSystem instance are discarded).

Then there are classes SomeFeature, implementing IFeature, and SomeService, implementing ISomeService.

SubSystem has dependencies on both IFeature and IService, while SomeFeature takes a dependency on IService. So the object graph looks like this:

SubSystem
└> SomeService : IService     <─┐
└> SomeFeature : IFeature       ├─── same instance
   └> SomeService : IService  <─┘

IFeature is only required in one place, so a transient registration is fine here. IService on the other hand must be resolved to the same instance for all dependencies within this subgraph, but when new data is loaded and a new SubSystem instance is created, its subgraph must get its own new "per-request singleton" IService instance.

The reason for discarding the instances is that they cache information from the loaded data for performance reasons, which will not be valid anymore when new data is loaded. I am currently using real singleton instances that have their local state reset via an event raised in the SubSystem constructor, but that is clearly a less than optimal solution.

As I said, I'd like this to work like InstancePerHttpRequest(), but as "instance per SubSystem".

Is there a way to achieve this using the Autofac API?

Upvotes: 2

Views: 387

Answers (1)

Nicholas Blumhardt
Nicholas Blumhardt

Reputation: 31757

The option I think you're looking for is:

.InstancePerOwned<SubSystem>()

If you only consume SubSystem in one place, just take a dependency on Owned<SubSystem> at that point, and make sure you Dispose() the Owned<T> in the consuming component's Dispose() method.

For something a bit more transparent, assuming you can create ISubSystem to go with SubSystem you can do this:

builder.RegisterType<SubSystem>()
  .InstancePerOwned<SubSystem>();

builder.RegisterType<SubSystemGraph>()
  .As<ISubSystem>()
  // Appropriate sharing here...
  ;

Where SubSystemGraph is:

class SubSystemGraph: ISubSystem, IDisposable
{
  readonly Owned<SubSystem> _root;

  public SubSystemGraph(Owned<SubSystem> root)
  {
    _root = root;
  }

  public void Dispose()
  {
    _root.Dispose();
  }

  // Methods of ISubSystem delegate to _root.Value

  public void Foo()
  {
    _root.Value.Foo();
  }
}

(This could be packaged up into a nicer interface on Autofac but it's not all that common in practice.)

Upvotes: 3

Related Questions