Working with Modules and ContainerBuilder's DefaultScope

32 views
Skip to first unread message

Brian J. Cardiff

unread,
May 29, 2009, 8:17:51 AM5/29/09
to aut...@googlegroups.com
Hi,

I am using Autofac with Modules, and want to allow to the bootstrap of the application whether some services should be FactoryScoped or ContainerScoped. This because those services are used in both Web Projects (with FactoryScoped) and Windows services (with ContainerScoped).

My initial try was to define modules as follows:

public class MyModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register<Foo>().As<IFoo>();
}
}

without specifying the InstanceScope. Later I will initialize the container as:

var builder = new ContainerBuilder();
using (builder.SetDefaultScope(InstanceScope.Factory))
{
builder.RegisterModule(new MyModule());
}
var container = builder.Build();

Seems nice, but MyModule.Load is not called until builder.Build(), so the DefaultScope is restored but that time. I removed the using, I don't care:

var builder = new ContainerBuilder();
builder.SetDefaultScope(InstanceScope.Factory);
builder.RegisterModule(new MyModule());
var container = builder.Build();

This doesn't work either. The Foo service was always registered as SingletonScoped. I tried to be more explicit and register Foo as follows:

builder.Register<Foo>().As<IFoo>().WithScope(builder.DefaultScope);

but again, the Foo is registered as SingletonScoped. And it was beacause the builder.DefaultScope == InstanceScope.Singleton.

Digging in the code I found the issue, for each module a new ContainerBuilder builder is created without the "parent" ContainerBuilder configurations.


public class ContainerBuilder
{
public ContainerBuilder()
{
    this._registrars = new List<IModule>();
    this._defaultInstanceOwnership = InstanceOwnership.Container;
    // this._defaultInstanceScope is implicitly assigned to InstanceScope.Singleton
}

public virtual void RegisterModule(IModule module)
{
   Enforce.ArgumentNotNull<IModule>(module, "module");
   this._registrars.Add(module);
}

public virtual IContainer Build()
{
   Container container = new Container();
   this.Build(container);
   return container;
}

public virtual void Build(IContainer container)
{
   Enforce.ArgumentNotNull<IContainer>(container, "container");
   if (this._wasBuilt)
   {
       throw new InvalidOperationException();
   }
   this._wasBuilt = true;
   foreach (IModule registrar in this._registrars)
   {
       registrar.Configure(container);
   }
}
}
 
public abstract class Module : IModule
{
public virtual void Configure(IContainer container)
{
   Enforce.ArgumentNotNull<IContainer>(container, "container");
   ContainerBuilder builder = new ContainerBuilder();
   this.Load(builder);
   builder.Build(container);
   this.AttachToRegistrations(container);
}
}


So, the question is: how is the right way to do this?
The email was long to let you know which were by initial guest and (maybe) detect a problem.
 
I am using version 1.2.7.397 , but with a quick look at the code of 1.4.3.536 seems that the issue is still there.

Thanks in advance,
Brian J. Cardiff
bcardiff(?)gmail.com
.

Chadly

unread,
May 29, 2009, 12:42:14 PM5/29/09
to Autofac
Why not just have your module take an InstanceScope as a parameter
that way you can specify externally when creating the module which
InstanceScope you want the module to use.
> *public ContainerBuilder()*
> {
>     this._registrars = new List<IModule>();
>     this._defaultInstanceOwnership = InstanceOwnership.Container;
> * **    // this._defaultInstanceScope is implicitly assigned
> to InstanceScope.Singleton*
>
> }
>
> public virtual void RegisterModule(IModule module)
> {
>     Enforce.ArgumentNotNull<IModule>(module, "module");
>     this._registrars.Add(module);
>
> }
>
> public virtual IContainer Build()
> {
>     Container container = new Container();
>     this.Build(container);
>     return container;
>
> }
>
> public virtual void Build(*IContainer container*)
> {
>     Enforce.ArgumentNotNull<IContainer>(container, "container");
>     if (this._wasBuilt)
>     {
>         throw new InvalidOperationException();
>     }
>     this._wasBuilt = true;
>     foreach (IModule registrar in this._registrars)
>     {
>         *registrar.Configure(container);*
>     }
>
> }
> }
>
> public abstract class Module : IModule
> {
> public virtual void Configure(IContainer container)
> {
>     Enforce.ArgumentNotNull<IContainer>(container, "container");
>     *ContainerBuilder builder = new ContainerBuilder();*
>     *this.Load(builder);*

Nicholas Blumhardt

unread,
May 29, 2009, 10:28:56 PM5/29/09
to aut...@googlegroups.com
Hi Brian,

The last poster has it figured out - this is a good candidate for a module parameter. For example, you might pass your modules' constructor a boolean indicating whether it is in a web environment or not...

The isolation of default scope between modules is deliberate - in all it should make things more predictable (so long as you're expecting it! :))

Hope you're on the right track now, feel free to ask more questions if you hit snags.

Nick

2009/5/29 Chadly <chad...@gmail.com>

Brian J. Cardiff

unread,
May 31, 2009, 6:51:44 PM5/31/09
to aut...@googlegroups.com
I understand that is a workaround, but it make some noise to me that 

var builder = new ContainerBuilder();
builder.SetDefaultScope(InstanceScope.Factory);

builder.Register<Foo>().As<IFoo>();
var container = builder.Build();

is differente from 

public class MyModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register<Foo>().As<IFoo>();
}
}

var builder = new ContainerBuilder();
builder.SetDefaultScope(InstanceScope.Factory);

builder.RegisterModule(new MyModule());
var container = builder.Build();

Since I see that like a little refactor of the setup of the ContainerBuilder.

Brian J. Cardiff
bcardiff(?)gmail.com
.


Rinat Abdullin

unread,
Jun 1, 2009, 12:24:46 AM6/1/09
to Autofac
Hi Brian,

In the long run, when you get more than 20 reusable components in your
application, it might be worth trying to organize them into modules
despite the code overhead. IMHO.

Best regards,
Rinat Abdullin

http://abdullin.com
> > 2009/5/29 Chadly <chadl...@gmail.com>

Daniel Cazzulino

unread,
Jun 1, 2009, 8:28:22 AM6/1/09
to aut...@googlegroups.com
I don't think he's arguing against that, quite the contrary.

I think he's pointing at an inconsistency in the way the builder works when you are just registering instances (it uses the default scope) vs when you register modules (which don't seem to use the default scope).

Is that correct Brian?

/kzu

--
Daniel Cazzulino | Developer Lead | XML MVP | Clarius Consulting | +1 425.329.3471

Brian J. Cardiff

unread,
Jun 1, 2009, 9:12:55 AM6/1/09
to aut...@googlegroups.com
Yes kzu. That is my point. Although as I already explained, I had solve my need by a module parameter. But the inconsistency I something that make some noise to me.

Brian J. Cardiff
bcardiff(?)gmail.com
.


Rinat Abdullin

unread,
Jun 1, 2009, 1:17:46 PM6/1/09
to Autofac
Brian, kzu,

Mea culpa. I'm sorry for rushing an answer after a cursory glance.

Best regards,
Rinat Abdullin

http://abdullin.com

On Jun 1, 7:12 pm, "Brian J. Cardiff" <bcard...@gmail.com> wrote:
> Yes kzu. That is my point. Although as I already explained, I had solve my
> need by a module parameter. But the inconsistency I something that make some
> noise to me.
> Brian J. Cardiff
> bcardiff(?)gmail.com
> .
>
>
>
> On Mon, Jun 1, 2009 at 9:28 AM, Daniel Cazzulino <kzu....@gmail.com> wrote:
> > I don't think he's arguing against that, quite the contrary.
>
> > I think he's pointing at an inconsistency in the way the builder works when
> > you are just registering instances (it uses the default scope) vs when you
> > register modules (which don't seem to use the default scope).
>
> > Is that correct Brian?
>
> > /kzu
>
> > --
> > Daniel Cazzulino | Developer Lead | XML MVP | Clarius Consulting | +1
> > 425.329.3471
>

Chad Lee

unread,
Jun 1, 2009, 10:35:15 AM6/1/09
to aut...@googlegroups.com
I disagree and don't see this as an inconsistency.

When I do this:


var builder = new ContainerBuilder();
...
var container = builder.Build();

var builder2 = new ContainerBuilder();
...
builder2.Build(container);

I don't expect builder2 to use the same scope that builder1 is using.  And this is what is happening when you create a module.  It creates another builder.

I think it is better this way to avoid weird conflict issues with other modules that are being registered at the same time.  If anything, I see this as just a documentation issue.  The user just needs to be aware of what is going on.

Nicholas Blumhardt

unread,
Jun 1, 2009, 10:46:24 PM6/1/09
to aut...@googlegroups.com
The thinking behind it is that modules should be portable, i.e. if I write a module to support NHibernate and I give it to you, it should still work despite your preferences for defaults.

Any ideas how we might be able to point this out more clearly so that it doesn't surprise anyone else? I can definitely see how it might catch you unawares (even though I'm still in favour of keeping the current way.)

Would something as simple as changing the name of Module.Load()'s parameter from 'builder' to 'moduleSpecificBuilder'? Something else?

Cheers,
Nick

2009/6/1 Chad Lee <chad...@gmail.com>

Brian J. Cardiff

unread,
Jun 2, 2009, 1:17:03 PM6/2/09
to aut...@googlegroups.com
I think that changing the parameter name should be very helpful in order to prevent future occurrences of same issue.

Some alternatives:
* moduleSpecificBuilder
* moduleBuilder
* emptyBuilder
* freshBuilder

since one normally do:

   var builder = new ContainerBuilder();
   builder.RegisterModule(new MyModule());

and the argument in Load method is builder the first thought is that those builder are the same. That should be prevented, and the change of the name seems to be enough.

I understand the different point of view regarding module isolated configuration. http://code.google.com/p/autofac/wiki/StructuringWithModules could be updated to reflect this, since it is the entry point to start using modules.

Brian J. Cardiff
bcardiff(?)gmail.com
.


Nicholas Blumhardt

unread,
Jun 4, 2009, 1:23:55 AM6/4/09
to aut...@googlegroups.com
'moduleBuilder' it is. Thanks for the ideas.

2009/6/2 Brian J. Cardiff <bcar...@gmail.com>
Reply all
Reply to author
Forward
0 new messages