Register callback in Autofac and build container a

2019-08-22 23:47发布

问题:

I have a dotnet core application. My Startup.cs registers types/implementations in Autofac. One of my registrations needs previous access to a service.

var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterSettingsReaders(); // this makes available a ISettingsReader<string> that I can use to read my appsettings.json
containerBuilder.RegisterMyInfrastructureService(options => 
{
   options.Username = "foo" //this should come from appsettings
});
containerBuilder.Populate(services);
var applicationContainer = containerBuilder.Build();

The dilemma is, by the time I have to .RegisterMyInfrastructureService I need to have available the ISettingsReader<string> that was registered just before (Autofac container hasn't been built yet).

I was reading about registering with callback to execute something after the autofac container has been built. So I could do something like this:

builder.RegisterBuildCallback(c =>
{
     var stringReader = c.Resolve<ISettingsReader<string>>();
     var usernameValue = stringReader.GetValue("Username");
     //now I have my username "foo", but I want to continue registering things! Like the following:
     containerBuilder.RegisterMyInfrastructureService(options => 
     {
         options.Username = usernameValue 
     });
     //now what? again build?
});

but the problem is that after I want to use the service not to do something like starting a service or similar but to continue registering things that required the settings I am now able to provide.

Can I simply call again builder.Build() at the end of my callback so that the container is simply rebuilt without any issue? This seems a bit strange because the builder was already built (that's why the callback was executed).

What's the best way to deal with this dilemma with autofac?

UPDATE 1: I read that things like builder.Update() are now obsolete because containers should be immutable. Which confirms my suspicion that building a container, adding more registrations and building again is not a good practice.

In other words, I can understand that using a register build callback should not be used to register additional things. But then, the question remain: how to deal with these issues?

回答1:

This discussion issue explains a lot including ways to work around having to update the container. I'll summarize here, but there is a lot of information in that issue that doesn't make sense to try and replicate all over.

  • Be familiar with all the ways you can register components and pass parameters. Don't forget about things like resolved parameters, modules that can dynamically put parameters in place, and so on.
  • Lambda registrations solve almost every one of these issues we've seen. If you need to register something that provides configuration and then, later, use that configuration as part of a different registration - lambdas will be huge.
  • Consider intermediate interfaces like creating an IUsernameProvider that is backed by ISettingsReader<string>. The IUsernameProvider could be the lambda (resolve some settings, read a particular one, etc.) and then the downstream components could take an IUsernameProvider directly.

These sorts of questions are hard to answer because there are a lot of ways to work around having to build/rebuild/re-rebuild the container if you take advantage of things like lambdas and parameters - there's no "best practice" because it always depends on your app and your needs.

Me, personally, I will usually start with the lambda approach.