21

I'm new to AutoFac and am currently using custom modules inside my app config to boot up some core F# systems. The code I'm using is

var builder = new ContainerBuilder();
builder.RegisterType<DefaultLogger>().As<IDefaultLogger>();
builder.RegisterModule(new ConfigurationSettingsReader("autofac"));
builder.Build();

And inside my app config I have the appropriate logic to start up the relevant systems. I would like to have access to the DefaultLogger inside my Modules. Metadata for the Module base class has the following options available to me:

protected virtual void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration);

protected virtual void AttachToRegistrationSource(IComponentRegistry componentRegistry, IRegistrationSource registrationSource);

public void Configure(IComponentRegistry componentRegistry);

protected virtual void Load(ContainerBuilder builder);

I've only been using Load so far and I can't see any methods on the builder that would allow me to get at the logging service.

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
Jesse Carter
  • 20,062
  • 7
  • 64
  • 101
  • Is this a duplicate question with http://stackoverflow.com/questions/23413211/autofac-cannot-resolve-dependency-in-module ? – Travis Illig May 02 '14 at 16:22

3 Answers3

12

When registering something within your modules with autofac instead of using RegisterType method you might use Register method:

builder.Register(c =>
   {
       IComponentContext ctx = c.Resolve<IComponentContext();
       IDefaultLogger logger = ctx.Resolve<IDefaultLogger>();
       ...do something with logger...
       return ...return object you want to register...;
    });
mr100
  • 4,340
  • 2
  • 26
  • 38
  • 1
    This doesn't really help the scenario I'm describing. I understand that I can resolve dependencies using an IComponentContext but I need access to one inside the Module. – Jesse Carter May 01 '14 at 15:33
  • You are not allowed to leave the focus here to set a variable in the outer scope (the module in his example). This way you can only log inside the registration. Anyway, this snipit shows the "correct way" of using the container/context, so +1. Maybe Jesse need to use lifetime scopes and a container that has a logger and builds another container that gets the logger injected which itself loads the modules provided by the first (hmm, sounds way too complicated). – Beachwalker Feb 05 '16 at 12:48
4

The answer turned out to be incredibly simple. I just added IComponentContext as a dependency to my Module's implementation

public class LocalActorSystemModule : Module {
    private IComponentContext m_ComponentContext; // A service for resolving dependencies required by this module

    public LocalActorSystemModule(IComponentContext componentContext) { 
        m_ComponentContext = componentContext;
    }

And let AutoFac inject the IComponentContext for me. That way I can resolve any dependencies I require inside the module.

Jesse Carter
  • 20,062
  • 7
  • 64
  • 101
  • 3
    how would you register LocalActorSystemModule? – Rushi Soni Dec 09 '15 at 05:53
  • 4
    This did not work for me. I injected IComponentContext via the Module constructor, but when I tried to use it in `AttachToComponentRegistration` it could not resolve what I wanted it to because the components had not yet been registered. – emragins Dec 24 '15 at 21:44
  • This can only work for a module if the module is registered itself and after loading the (required dependencies, e.g. modules are loaded), otherwise you are unable resolve. IMHO, the IComponentContext is supposed to be injected into registrations of services (automatically), but not for use in modules. I think the behaviour is not really predictable and I would not recommend doing that. Think about using the IComponentContext while the module is not actually loaded... what would be the result before and after loading? – Beachwalker Feb 05 '16 at 12:41
  • 1
    One improvement might be to explicitly declare each dependency that module needs in it's ctor - instead of just passing the entire IComponentContext in. – Jack Ukleja Nov 16 '16 at 01:15
  • Injecting the IComponentContext into your module telling us that are you're doing something wrong – Damir Beylkhanov Nov 13 '20 at 11:07
1

Rule of thumb for using every IoC/DI Container: Resolve once! => then you get all dependencies resolved for your requested object. If you try to resolve multiple times, register other objects (in the meantime) you're stuck in hell. Really. If you want to retrieve objects for different purposes at different places and time points (resolved from central registration) you may be looking for the Service Locator Pattern instead (but this is often described as an Anti-Pattern, too).

Modules have the purpose to bundle related registrations (conditionally) as statet in the Autofac documentation:

A module is a small class that can be used to bundle up a set of related components behind a ‘facade’ to simplify configuration and deployment.

... so if they are just a sum of registrations and the container has not yet been build you are not able to resolve and use an (even previously registered) component immediately (except calling a method on the registrant itself through OnActivate* hooks or when using instance registration, but I think this is not the case for your example). The components are just in the state of registration but the complete context is not ready for resolving. What would happen if you override the registration in another Module? Then you would have injected different objects... bad idea. Maybe you should rethink your application design and which objects have which responsibilities.

By the way: Logging is a cross cutting concern that is often "injected / resolved" by calling a separate static factory or service instead of doing constructor / property injection (see usage of Common.Logging for example).

public class MyModule : Module
{
    private static readonly ILog Log = LogManager.GetLogger<MyModule>();

    protected override void Load(ContainerBuilder builder)
    {
        Log.Debug(msg => msg("Hello")); // log whatever you want here
    }
}

You can also try to use AOP libraries and weave the dependency into the Module (using reflection). But I don't think it's worth to try just for logging in a Module.

Anyway: @mr100 has already shown the right usage during registration. There you can also handle activation etc. but not do logging for the Module itself.

Beachwalker
  • 7,685
  • 6
  • 52
  • 94