XPO and Instance scopes in autofac

One of the appealing ideas in the autofac is scope nesting and explicit life management of the component instances.

Imagine the situation of the task server, where every task depends on a wide variety of the services. Let us take “Session” as an example. It could be registered in the container with the following lambda expression:

this.Register(c => c.Resolve<IServerConnector>().GetSession()).
  As<Session>().
  WithScope(InstanceScope.Container);

Note, that the actual component being registered is a Session factory that resolves some IServerConnector and produces instances with the scope of their container. The container will have only one session and it will be disposed when the container itself get disposed.

Additionally note that this lambda resolution is faster than the reflection, so we can use it for the micro-scopes.

The tasks could be registered in a ITaskRegistrar (just a simple List<Func<IContext, ITask>> for this situation) using the same lambda syntax:

ITaskRegistrar resolve = container.Resolve<ITaskRegistrar>();

resolve.Register(c => new ProcessApprovedApplications(c.Resolve<Session>()));
resolve.Register(c => new RetrieveQueuedResults(c.Resolve<Session>()));

IServerConnector and ITaskRegistrar get registered in the xml configuration file along with modules that contain the component registrations mentioned above.

Then the simple task server itself could be written like this:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConfigurationSettingsReader("core"));

using (Container c = builder.Build())
{
  foreach (Func<IContext, ITask> func in c.Resolve<ITaskRegistrar>())
  {
    using (ITask task = func(c))
    {
      task.Execute();
    }
  }
}

In this scenario all tasks share the same scope and the Session instance within.

In order to make every task use its own session (i.e.: for the purposes of making them thread-safe), we could slightly modify the code:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConfigurationSettingsReader("core"));

using (Container c = builder.Build())
{
  foreach (Func<IContext, ITask> func in c.Resolve<ITaskRegistrar>())
  {
    using (Container taskScope = c.CreateInnerContainer())
    using (ITask task = func(taskScope))
    {
      task.Execute();
    }
  }
}

And if you want to keep the single session per container, but give the tasks separate UnitOfWork instances (provided UnitOfWork inherits from Session), then you could use the first program snippet but change the Session registration routine to:

this.Register(c => c.Resolve<ServerConnector>().GetSession()).
  As<Session>().
  WithScope(InstanceScope.Container).
  Named("session");
this.Register(c => c.Resolve<Session>("session").BeginNestedUnitOfWork()).
  As<Session>().
  WithScope(InstanceScope.Factory);

Basically here we intercept the requests to Session and return transient UnitOfWork instances instead.

1 Response to “XPO and Instance scopes in autofac”


  1. 1 Nicholas Blumhardt

    Hi Rinat,

    I’m not sure if it is any better or even relevant, but you might want to check out accumulating collections - http://code.google.com/p/autofac/issues/detail?id=2&can=1 - this feature might apply to your task example. Instead of ITaskRegistrar you’d do something like:

    builder.RegisterCollection<ITask>()
    .As<IEnumerable<ITask>>()
    .Named(”tasks”);
    builder.Register<ProcessApprovedRegistrations>()
    .MemberOf(”tasks”);
    builder.Register<RetrieveQueuedResults>()
    .MemberOf(”tasks”);

    The advantage might be that you can avoid an ‘early’ resolve, and I’ve already done my best to support scope in line with the other container features. You can use MemberOf(typeof(IEnumerable<ITask>)) rather than a name if that is preferable.

    This isn’t a particularly well-tested feature of the container but one that I think will prove important as it is more easily maintained and extended in applications than a ‘ResolveAll<ITask>’ kind of feature. Hope this is is useful!

    Nick

Leave a Reply