autofac / Autofac

An addictive .NET IoC container
https://autofac.org
MIT License
4.5k stars 837 forks source link

Discussion: ContainerBuilder.Update Marked Obsolete #811

Closed tillig closed 4 years ago

tillig commented 7 years ago

In https://github.com/autofac/Autofac/commit/8a89e94ad2ffed0c10ac613b7015c11a56275c99 the ContainerBuilder.Update methods have been marked obsolete.

The plan is to leave them obsolete for a year or more, weaning developers off the use of Update, and in a future major release remove the methods altogether.

This issue is to learn from developers why they believe they need Update and see if there are better ways to handle the situations. If it turns out there is a totally unavoidable situation where Update is literally the only way to handle it, we need to determine how best to fix Update.

If You Want to Keep ContainerBuilder.Update...

If you believe you need ContainerBuilder.Update please provide the following information with your comment - "me too" or "I use it" isn't enough.

We actually need real information to understand the use cases and see if there are different ways your code could be doing things to work around needing Update. If it turns out we're missing a feature, hopefully we can figure out what that is and get you what you need while at the same time removing the need to update the container post-facto.

Why ContainerBuilder.Update Was Marked Obsolete

Here are some of the reasons why the use of ContainerBuilder.Update is generally bad practice and why we're looking at this.

Container Contents Become Inconsistent

Once you resolve something out of a built container, a lot of things get put in motion.

If you change the contents of the container, there's a chance that the change will actually affect these things, rendering cached or tracked items as inconsistent with the current contents of the container.

In unit test form (with a little pseudocode)...

[Fact]
public void InconsistentContainer()
{
  var builder = new ContainerBuilder();

  // In this example, the "HandlerManager" takes a list of message handlers
  // and does some work with them.
  builder.RegisterType<HandlerManager>()
         .As<IHandlerManager>()
         .SingleInstance();

  // Register a couple of handlers that the manager will use.
  builder.RegisterType<FirstHandler>().As<IHandler>();
  builder.RegisterType<SecondHandler>().As<IHandler>();

  using (var container = builder.Build())
  {
    // The manager is resolved, which resolves all of the currently registered
    // handlers, and is cached. This manager instance will have two handlers
    // in it.
    var manager = container.Resolve<IHandlerManager>();

    // Update the container with a new handler.
    var updater = new ContainerBuilder();
    updater.RegisterType<ThirdHandler>().As<IHandler>();
    updater.Update(container);

    // The manager still only has two handlers... which is inconsistent with
    // the set of handlers actually registered in the container.
    manager = container.Resolve<IHandlerManager>();
  }
}

Update Isn't Consistent With Build

When you Build a container, a couple of things happen:

When you Update a container, these things don't happen. We intentionally don't want the base registration sources duplicated; and unless you manually specify it we don't run startable components that may have been added during an update. Even if you specify it, your existing startable components aren't re-run; only the newly added ones would be run.

Why not fix it? There's not a clear "right way" to do that due to the container contents being inconsistent (see above). Most startable components are singletons where the point of starting them is to initialize a cache or execute some other startup logic proactively instead of lazily. If we don't re-run startables, maybe they don't pick up the things that they need. If we do re-run startables, maybe that invalidates even more things... or maybe it doesn't have any effect (in the case of singletons).

Child lifetime scopes spawned from the container get a sort of "copy" of the set of registrations in the base container. Updating the container after a child lifetime scope is spawned doesn't automatically propagate the new registrations into the child scope (Issue #608).

Basically, Update really isn't the same as Build and using it may not be doing 100% of the things you think it's doing.

Diagnostics and Optimizations Difficult to Implement

We see a lot of StackOverflow questions, issues, tweets, etc. about some of the challenges folks have around diagnosing missing dependencies. We'd love to be able to provide some better diagnostics, but one of the challenges in that is with Update: If folks assume they can change the container contents later, we conversely can't assume we can do any sort of proactive analysis or diagnostics when a container is built.

Further, we could potentially implement optimizations that, for example, proactively cache component activation data based on the registered set of components... but doing that and making sure it's all flushed/regenerated on each update doesn't always turn out to be the easiest thing to do.

Why Not Just "Fix" Update?

The question that logically follows is... why not just "fix Update so it behaves correctly?"

Easier Said Than Done

If you think about what would actually have to happen to make Update work "correctly" it includes...

...and so on. Basically, rebuild the whole container. This can really mess with your app if have something that's holding onto a resolved item that gets disposed or becomes invalid.

Locking a Container Isn't Unprecedented

Looking at other containers out there, locking a container after it's built and ready to resolve isn't uncommon. Simple Injector, LightInject, and the Microsoft.Extensions.DependencyInjection containers all disallow updating the container post-facto.

For those that do - StructureMap, Ninject, and Windsor to name a few - it appears they actually do all the work mentioned in "easier said than done" - flushing caches, rebuilding the whole container. And, as mentioned, this can cause inconsistent behavior in the application if it isn't managed very, very carefully.

Possible Workarounds for Update

Instead of using ContainerBuilder.Update, you may be able to...

Pass Around ContainerBuilder Instead of IContainer

Instead of passing the container around and conditionally updating it, change your logic to pass around the ContainerBuilder and conditionally register things correctly the first time. With the new ContainerBuilder.Properties dictionary available, you can add some context and perform business logic during the initial building of the container if you need to rather than wait until afterwards.

Add Registrations to Child Scopes

Occasionally what you need is something available during a child lifetime scope for a specific task, unit of work, or request. You can add registrations to just that child scope using a lambda:

using (var scope = container.BeginLifetimeScope(b =>
  {
    b.RegisterType<NewRegistration>();
  })
{
  // The new registration is available in this scope.
}

You may even want to cache that child lifetime scope and reuse it - like a smaller sub-container with a special purpose. That's how the multitenant integration works - cached lifetime scopes per tenant.

Use Lambdas

If you're trying to change something that's registered based on an environment parameter or some other runtime value, register using a lambda rather than reflection:

builder.Register(ctx =>
{
  if (Environment.GetEnvironmentVariable("env") == "dev")
  {
    return new DevelopmentService();
  }
  else
  {
    return new ProductionService();
  }
}).As<IMyService>();

Use Configuration

Just like with web.config transforms, you may choose to switch deployed Autofac configuration files based on an environment. For example, you may have a development configuration and a production configuration.

Use Modules

If you have a lot of registrations that need to change based on runtime, you can encapsulate that in a module.

public class MyModule : Module
{
  private readonly bool _isProduction;

  public MyModule(bool isProduction)
  {
    this._isProduction = isProduction;
  }

  protected override void Load(ContainerBuilder builder)
  {
    if (this._isProduction)
    {
      builder.RegisterType<FirstProductionService>().As<IMyService>();
      builder.RegisterType<SecondProductionService>().As<IOtherService>();
    }
    else
    {
      builder.RegisterType<FirstDevelopmentService>().As<IMyService>();
      builder.RegisterType<SecondDevelopmentService>().As<IOtherService>();
    }
  }
}

Use Conditional Registrations

Autofac 4.4.0 introduced OnlyIf() and IfNotRegistered extensions. These allow you to execute a specific registration only if some other condition is true. Here's the documentation. Quick example:

// Only ServiceA will be registered.
// Note the IfNotRegistered takes the SERVICE TYPE to
// check for (the As<T>), NOT the COMPONENT TYPE
// (the RegisterType<T>).
builder.RegisterType<ServiceA>()
       .As<IService>();
builder.RegisterType<ServiceB>()
       .As<IService>()
       .IfNotRegistered(typeof(IService));

Handling Application Startup / Bootstrap Items

A common scenario for wanting to update the container is when an app tries to use a container to register plugins or perform app startup actions that generate additional registrations. Ideas for handling that include:

Consider Two Containers

If you are using DI during app startup and then also using it during the execution of the app, it may be that you need two containers: one for each stage in the app lifecycle.

The first is a container that has services used to index plugins (the "assembly scanning" mechanism, logging, that sort of thing); the second is a container into which runtime requirements are registered like the set of plugin assemblies, required common dependencies, and so on.

Don't Over-DI Bootstrap Items

It's good to use DI, but you can easily get into a chicken/egg situation where you try to resolve bootstrap items (like your application configuration system, logging that will run during application startup, and so on) out of the container... that you're trying to set up during app startup.

Don't do that.

If you look at many of the newer ASP.NET Core examples, you'll see a good pattern where app configuration, base logging, and other "bootstrap" elements are actually just directly instantiated or built. Those instances/factories can then later be registered with Autofac for use during runtime, but the initial construction proper isn't done out of Autofac.

Share Instances Across Containers

If you go with the two-container startup, you can always register the same instance of a thing (e.g., application configuration, logging factory, etc.) into two different containers. At that point it's effectively a singleton.

(You can also use Autofac modules to share registrations if you have bunches of them that need to be duplicated, though you'll get different instances of things so be aware.)

Lambdas, Lambdas, Lambdas

Many, many container updates could be worked around using a lambda registration. "I need to register XYZ based on the result of resolving ABC!" - do that with a lambda registration.

Nancy Framework Users

If you use Nancy, it internally uses Update(). There is already an issue filed for Nancy to be updated - you can follow that issue or chime in over there if you're interested in how that is progressing.

Prism (WPF) Framework Users

Prism only supports modules in mutable containers. This is an architectural choice of the Prism project owners. An issue was filed here to alert Prism of the changes around Update() and as part of a major IoC integration refactor the decision was made to only support modules for mutable containers. While it may be possible to enable modules for Autofac via one of the above strategies or something like registration sources, Prism is expecting the community to submit and support that code. Head over there if you'd like to follow up with them; there is no current plan to create an Autofac-project-supported Prism integration library.


Short Term Fix

If your code uses Update and you want to keep using it for the time being, you can disable the warning just around that call.

#pragma warning disable 612, 618
builder.Update(container);
#pragma warning restore 612, 618
tillig commented 7 years ago

@stevejbrother From your note and your question on StackOverflow it sounds like you're fairly new to Xamarin + Prism usage but are finding your way, which is awesome. I will plead a bit of ignorance since I'm primarily an ASP.NET dev so I can't really discuss the ins/outs of how Prism interacts with Xamarin apps and how they dictate their best practices.

Right now, as you surmised, Prism seems to require usage of the Update method in a couple of places, at least.

There is already an issue for this over on the Prism site. The correct solution isn't necessarily for Autofac to never change so much as to give people time to update frameworks to behave correctly. As noted initially, this isn't happening, like, tomorrow or anything, but it's coming.

Other frameworks like Nancy also need to change. As we find frameworks that assume Update will work, we're filing issues there to let folks know they need to update their design to accommodate the notion of the immutable container. They should all have plenty of time to take on those updates.

You mentioned in your SO question that there are other discussions in Prism going on like the questions around IDependencyService. That may work into an overall solution for how to redesign the Prism stuff to better accommodate Autofac best practices. I would encourage you to work with the folks over on the Prism project if you can to help them by offering your expertise and suggestions.

As for the use of IPlatformInitializer in your current solution - I'm glad you found a workaround. For the short term, I think that's fine. If you're worried about build warnings, my first post in this thread shows how to suppress that. Long term the change really needs to be in Prism, and the issue over there is the start of that work happening.

ShadowDancer commented 7 years ago

@tillig IRegistrationSource should be enough in my case. The disadvantage of this approach is that You have to know what services will be loaded from plugin (other than MyFeature).

For instance if my application is using IDbQuery<TResult> to communicate with db, and I want to put more queries in my plugins (used only by plugins ofc). I would like to use IDbQuery, because I have diagnostics, performance counters and other utilities built on top of that interface.

ellisnet commented 7 years ago

@stevejbrother and @tillig - I have been working on the Autofac implementation of Prism for Xamarin.Forms (and next on the Autofac implementation of Prism for UWP and WPF, if those also need to be tweaked). All uses of ContainerBuilder.Update() are being removed, so that this method can be removed from Autofac as planned in the future; and so that Prism will only build the container one time and not try to update it (i.e. the Autofac container used by Prism will be immutable). So, hopefully that should resolve concerns about using Autofac with Prism in the future. Version 6.3.0 of Prism.Autofac.Forms still uses ContainerBuilder.Update() - it is expected that versions higher than that will not.

dhanrajks commented 7 years ago

@tillig we have a slightly different situation, we try to register all the services using AssemblyScanning and after that we want to update the lifetime of the services to either Singleton or PerRequest.. Here is the small snipped

var builder = new ContainerBuilder();

// By default register all the Interfaces in the referenced assemblies var assemblies = BuildManager.GetReferencedAssemblies().Cast().Where(x => x.FullName.StartsWith("MyProject")); builder.RegisterAssemblyTypes(assemblies.ToArray()).AsImplementedInterfaces().InstancePerRequest();

builder.Register(x => new XConfigurationManager()).As().SingleInstance();

But here the second registration would create a duplicate registration, though we understand that AutoFac will always tries to resolve the last one.

I am fairly new to AutoFac moving from StructureMap..

Any ideas are welcome.. Thanks in advance.

nblumhardt commented 7 years ago

@dhanrajks how about Except?

builder.RegisterAssemblyTypes(assemblies.ToArray())
    .AsImplementedInterfaces()
    .InstancePerRequest()
    .Except<XConfigurationManager>(cm =>
        cm.AsImplementedInterfaces().SingleInstance())
    .Except<Other>(other =>
        ...);

http://docs.autofac.org/en/latest/register/scanning.html#filtering-types

If you have more than a few exceptional components, you might consider using Where() with the assembly scanning to exclude them. HTH!

dhanrajks commented 7 years ago

@nblumhardt Thanks that works great.. Also is there a way i can have multiple builders build the container.. essentially i am looking for an equivalent of Registry concept in StructureMap where i have registry for each component or a feature and one default registry at the root.

dammejed commented 7 years ago

This may be a silly question, but is there a way to take an existing container, plus a registration builder, and generate a new IContainer which is not a mutation of the original, but a whole new container? as in...

var container = Context.LazilyBuiltContainerWhichMightHaveAlreadyBeenBuilt;
var builder = new ContainerBuilder();
builder.RegisterAllMyNewThings();
var newContainer = builder.GenerateNewContainerFromOriginalContainer(container);

This is basically the only usecase for which our team was using update. If we can immutably generate a new container (which is still the root scope) based on some other container, we'd be golden.

In our case, we haven't even resolved from the first container yet, so we don't even care about the already resolved components or anything. It was just a really awful design where the container is implicitly built in a few contexts.

The better option is clearly to use the ContainerBuilder all the way though, but if we had an intermediate path where we generate a new immutable container at first, it would help us transition more smoothly.

SlayersAlpha3 commented 7 years ago

We use the Roslyn engine to compile assemblies in memory from CMS stored code files. When code is updated in the CMS, the publish event re-compiles the assembly and updates the Autofac container. Functions are called on the assembly via reflection. This is so that we can modify the business logic of a non-load balanced website without outages and to deliver functional updates where the code and the content are married into a single delivery. We don't want to recompile all the business logic, only the functional assembly that has been updated.

tillig commented 7 years ago

@SlayersAlpha3 If you're updating the container at runtime without restarting the app... that's already got huge potential to create instability in your app due to an inconsistent container - see previous discussion to explain why that's a Really Bad Idea. However, let's assume you're not doing that.

Instead of using containerBuilder.Update, have your re-compilation process change the contents of an Autofac module instead of generating, say, a lot of containerBuilder.Update calls. Register the module during app startup / container build rather than registering individual types. Alternatively, have the code generation process create a JSON configuration file that can be registered. Both of those things can cause the set of registered things to change without having to recompile any business logic.

JackGrinningCat commented 7 years ago

@ellisnet Thanks for the note. For me an important point for our internal discussions. @dammejed I found your note interesting, but wouldn't know how to handle the Startable things with it. I follow this discussion but missing a kind of summary of the use-cases after the lengthy discussions.

TLTR: I exchanged my IContainer explicit dependency, which enforces update behavior, with an second own optionally created container, then to an obligatory created but overwrite-able one, and moved with it from constructor injection to property injection.

This is my story of changes, and should allow how I moved through my changes and what will be at the end: Why did I needed Update? I have a class that creates default instances for the user. As I'm fairly new to the use of our DI container, i started with Constructor Injection of the module container on which I registered the generic types I resolve.

classConstructor( ISomeDependecy, IContainer)

As I followed my train of though and saw the deprecated tag, I accepted, that it is not needed to be resolvable by the DI-Container. But only added a new Constructor

classConstructor( ISomeDependency) -> build my own container if needed.
classConstructor( ISomeDependency, IContainer)

But actually this leads that colleges could discuss that I need the IContainer aka have to Update with itself. Actually taking by bad design as a example for their work

I thought about cycling the dependency and let Autofac create the instances for the user directly and that my class is injected as dependency. but I shy away from this bigger changes in the user interface and the need of explaining the changes. 🐑

The basic reasoning for adding it was to allow that my tests can inject their test classes. So my solution will be to make it a property. It will be initialized in the constructor. will excluded from autowired option, make it basically only changeable by the tests which needs to know the implementation class anyway. Make it something like this.

classConstructor( ISomeDependency)
IContainer DiContainer(get;set)

I'm sure not through every nick in my design decisions but that maybe anyway they don't have that big impact on product quality of the whole. Good enough is best seems to be my answer.

So i do not need the update any longer in my module, but saw that other produced a need, because it was possible.

LordKiRon commented 7 years ago

Ok, here is our scenario for using Update:

Our system load component according to some list, first base components are loaded , then extensions loaded optionally. So first we load base components "bootloader" , register it's modules and activate (start/create/resolve etc) base components (that can be hardware stuff too). Then we go by list of extensions and load them one by one, each one updates container with their own registrations and then starts it's own services (can be hardware as well).

On one hand the extensions need to access to own stuff , on another they need access to base stuff like logger, settings , base (common) hardware and so on. So using scopes is not a really good choice here, as this probably means that each extension will have access to at least 2 containers - base and it's own (at least because we have extensions of extensions too, it's all related to licensing and hardware configurations, so a lot of stuff optional). Don't think having to resolve over several containers is a good idea, especially when they actually do have same lifetime - until the exit of application. Changing the program structure is not an option as it's a huge program with a lot of legacy, Autofac their is our attempt so switch to DI at least C# code (about 40% of code is C++/COM anyway). So updating container is rather useful option here - extension loads, updates container and all registrations are available from that moment on. Having extension re-register base modules into their own container also does not sound good, as this means we need to let extensions somehow to know who and where base modules are, especially in case of extensions of extensions, where we can have entire tree, or better to say bush of them. We are new with Autofac, so if you have other suggestion on how to solve it other way, without Update - you are welcome to suggest, but right now I do not see any other option. Thank you.

tillig commented 7 years ago

@LordKiRon You might want to go back and read through this whole (admittedly long) issue which has several suggestions around how to handle modular projects. Much of it boils down to:

LordKiRon commented 7 years ago

@tillig , the basic problem is that we need some base stuff already working before we can start plugins and we need someone to load assemblies classes are located in.

    • not an option, this is an old system that we try to move to DI, gradually.
  1. Passing ContainerBuilder (and letting each extension to register first) is an option, but require creating a registration system parallel to current, Basically to register class we would have to load its assembly first, right ? ;) When old system loads stuff by it's configuration files (and in order I explained earlier it expect components to run (basically it does "Start" for each class right after "Create") so everything need to be resolved at this point. Moreover currently the old system configuration files are responsible for loading ll assemblies. So in our case if we try to pass the builder question becomes - who will load assemblies ? Both systems have to "live" in parallel for now, we can't just let new DI bootstraper go over old system config files and load all the assemblies with all the conditions apply before registering in builder.
tillig commented 7 years ago

@LordKiRon Unfortunately figuring out literally the exact solution for every person is going to be pretty impossible. Everyone's app is different with different needs so we can only really recommend patterns and practices.

Migrating legacy code to a new pattern is painful. I've done it, I've been there. Switching an app to use DI really means you need to change some old patterns - changing from using new SomeClass() to injecting things in constructors, for example.

When it comes to trying to hook modules/plugins up using DI, it means more. It means you need to split the "register plugins" step away from the "initialize/start plugins" step. It means separating out "bootstrap application setup" into a really minimal set of components - base level configuration, logging, and plugin loading that can be directly instantiated rather than resolved through configuration.

If you can minimize the bootstrap app setup and directly create it, then passing around the ContainerBuilder to register modules (or doing assembly scanning to register Autofac modules) is the next step.

However, if you are in a position where you can't make the necessary architectural changes... I might suggest your time is better spent refactoring things to get to that point than jumping into the deep end and struggling against the patterns. Consider isolating the legacy unchangeable stuff from the stuff you actually can change. Maybe you need to "wrap" legacy modules so they can behave in the old way while you use the new registration mechanism for newer modules.

Again, though, it's not something I can provide super detailed precise guidance that's specific to your app.

nblumhardt commented 7 years ago

Just thinking aloud... is there any way we could wrap up the "bootstrapped-with-plugins" guidance into a package itself, along the lines of the multi-tenant integration?

It seems like the user experience dynamics are vaguely similar - if a package provided some more prescriptive guidance from the start, the question would become "how do I fit this architecture into the Autofac plug-in-bootstrapper API?" - which might yield more concrete discussions than starting from a blank slate with each new app.

LordKiRon commented 7 years ago

@tillig , thanks for your reply. To state thing simple we have a system that:

  1. Loads config file
  2. Loads assemblies mentioned in it.
  3. Start "services" mentioned in config file
  4. Goes by all child extension (from that config file)
  5. Loads their config file one by one and repeat process 2-4 for each extension

So idea was is to have base "thing" to build the container after step 2 each time in case it's not created yet , or update with "ExcludeDefaultModules" if was already created. This works fine and fits design quite nicely. We even do not have "Update" problem you want to kill Update for :) - base stuff have only it's services resolved , it need not know about extension additional service , while each extension on creation point will see only it's base and additional services needed (and added) on it's level (and they are keyed anyway).

Now if you get rid of Update , I need something "parallel" that before this process start , because in order for RegisterType<> to work the type need to be actually known. And as I said assemblies are not referenced by loader module, but loaded dynamically from config file. So some "duplicate" code need to be written that loads assemblies first according to configuration logic used by old system. Does not look pretty to say at least.

tillig commented 7 years ago

@nblumhardt Maybe, though each app has its own "plugin contract" which could make it difficult. Perhaps an example application in the Examples repo? I'm reluctant to specify beyond the use of assembly scanning and Autofac modules what a plugin might look like. System.Addin couldn't be easily ported from .NET full to .NET core and I'm not super interested in trying to throw my hat in that ring. I've also run into situations where, like, plugin assemblies are stored in SQL so folks need super customized ways to locate plugins, etc.

I suppose if there was a lot of common/boilerplate stuff that could be determined, perhaps a whole separate non-Autofac project, similar to OWIN, that defines a very, very generic plugin and application startup pipeline could be warranted. Of course, that also overlaps a lot of the ASP.NET Core "Startup" stuff, where different environments call different initialization methods in a specific order to wire things up...

tillig commented 7 years ago

@LordKiRon What I was saying is that you need to take that step 3 - "Start services mentioned in the config file" - and separate that out. Read the config file, recurse through it to do all the assembly loading and registration, then run the actual start/resolve operations at the end. That may mean some architectural changes. I'm sorry that it could get messy or not necessarily fit into your existing architecture or the original idea. Having plugins that add app config post-start that could add more plugins which add more app config recursively down the chain sounds like something that needs to be split up anyway.

It sounds a lot like NuGet dependency management in a way - you have to be able to walk the full dependency chain before actually running a build to make sure things are in place. It's a two-step process - verify the complete recursive chain of dependencies, then add references. You may be able to take some ideas from them - separate the notion of "retrieving configuration for each plugin" from "invoking the plugin itself." Or simplify the plugin detection mechanism?

LordKiRon commented 7 years ago

This is actually a huge system with huge legacy and it's "live in production", so major redesign is unfortunately out of the question. Yes, this is probably solvable by implementing some "PluginLocator" that will iterate over configuration files instead of old loader code, load assemblies and "provide it's results" both to old plugin loader and Autofac Builder, but this is such huge changes to "years proven and working code" :p) just because you decided to drop Update :) Anyway, thanks, you wanted use case - here it is, hope you reconsider :)

LordKiRon commented 7 years ago

Btw: is making Update deprecated also means you plan to get rid of Adding registrations to lifetime scope? using(var scope = container.BeginLifetimeScope( builder => { builder.RegisterType<Override>().As<IService>(); builder.RegisterModule<MyModule>(); })) { // The additional registrations will be available // only in this lifetime scope. }

tillig commented 7 years ago

@LordKiRon that stays. Please read the first entry in this issue which explains that as one of the alternatives to update.

shinup commented 7 years ago

Which overload do you use? Update(IContainer, What is the scenario (use case) in which you are unable to pass the ContainerBuilder prior to container building? In integration tests update the registrations to use mock objects to avoid external service calls.

var resolver = DependencyResolver.Current as AutofacDependencyResolver;

        var container = resolver.ApplicationContainer as IContainer;
        var builder = new ContainerBuilder();
        builder.RegisterInstance(FooMock.Object).As<IFoo>();
        builder.Update(container);
        _initialized = true;

When you use Update, have you already resolved something from the container before calling Update? Yes

Maktech07 commented 6 years ago

I know this is an older thread but, I have similar scenarios to other's above. I have an application which can download assemblies on demand at runtime and I want to register types out of those assemblies with my Container. I do not want to require every possible assembly (which may or may not be used) to be pulled down and registered before the Container is built.

I guess I could put the entire application in a new lifetime scope everytime I want to register new types.

That being said...

I understand the motivations for the decision but, this thread did not feel like hearing out actual use cases, it was more explaining why the use of the previous functionality was a poor decision and NOW considered a bad practice.

Reading through this thread. I think the thing that stuck out the most for me is the presumption that since Autofac has decided to remove this functionality anyone using Autofac with Update should change their architecture. Please read "should" as different than "will have to". "Should" as in the previous architecture was ill advised or not passing some "smell test".

This comment in particular to @bc3tech , was surprising, the existing working architecture is an "issue" due to Autofac's design choice: "We'll have to file an issue over there to see if we can get them to change their architecture. Thanks!"

I think @LordKiRon hit it on the head:

"but this is such huge changes to "years proven and working code" :p) just because you decided to drop Update :)"

I really enjoy Autofac and found it refreshing compared to other IOC frameworks but, without this functionality I have begun investigating other frameworks, unfortunately.

bc3tech commented 6 years ago

Worth noting that the Microsoft Bot Framework's v4 implementation drops its dependency on Autofac now..

tillig commented 6 years ago

@bc3tech Yup, the Microsoft.Extensions.DependencyInjection stuff for .NET Core is decent. Not quite as flexible as a native DI container registration syntax, but a nice abstraction. Note the Microsoft.Extensions.DependencyInjection default container is also immutable so you can't add or modify registrations in that, either... so it's not addressing the challenges of not having an Update method; instead the architecture/design appears to have changed to not require updating the container. That's consistent with how ASP.NET Core handles the container as well.

For the people still interested in using Autofac, we do have Autofac.Extensions.DependencyInjection to allow you to back your Microsoft.Extensions.DependencyInjection registrations with Autofac.

xperiandri commented 6 years ago

I was in this same situation about OWIN and SignalR until I found a solutions

aleksmelnikov commented 6 years ago

I have done my example of container.update usage: https://github.com/superriva/AUTOFAC_examples/tree/master/test2 A simple server build a container with the lib1 then make update the container with the lib2. The server works with assemblies, json configs and modules. I think it likes production tasks. Because I can make hot reload server logic. The output is:

ContainerBuilder time (ms): 1.4322
ConfigurationBuilder time (ms): 18.7695
config.Build() time (ms): 64.5171
Create module time (ms): 0.3346
Register module time (ms): 2.0207
/at/c#/test2/lib1/bin/Debug/netcoreapp2.0/lib1.dll
Load time (ms): 52.61
Lib1
BUILD
Build or Update time (ms): 276.4384
Container work time (ms): 408.6262
________________________________
ContainerBuilder time (ms): 0.0086
ConfigurationBuilder time (ms): 0.279
config.Build() time (ms): 1.4935
Create module time (ms): 0.005
Register module time (ms): 0.0254
/at/c#/test2/lib2/bin/Debug/netcoreapp2.0/lib2.dll
Load time (ms): 0.716
Lib2
UPDATE
Build or Update time (ms): 9.3741
Container work time (ms): 11.3989

Build operation is expensive (276.4384ms), update operation is cheap (9.3741ms). How I could make this without container.update (fast hot update)? Thank you in advance. In general, I think it's the best Autofac option. If developer uses this option then he understands what he does and can solve any problem with dependencies and others.

vincentparrett commented 6 years ago

Which overload do you use? Update(IContainer)

I have a scenario where I create child lifetime scopes, and I need to inject that lifetimescope in a class, so I do this

    childscope = container.BeginLifetimeScope();
    var builder = new ContainerBuilder();
    builder.Register((ctx, p) =>
    {
      return new WcfUser(userName);
    }).As<IWcfUser>().SingleInstance();

    builder.RegisterInstance<ILifetimeScope>(childscope); <<<<< How to do this???
    builder.Update(childscope.ComponentRegistry);

Changing the IWcfUser registration part was easy using the action in the BeginLifetimeScope method, however it seems BeginLifetimeScope doesn't register itself, so what happens when try to resolve something (yes, I know it's an antipattern, it's unavoidable at the moment) that takes a lifetimescope, it get's the parent scope, and the further later stuff that was registered with the child scope doesn't resolve.

This particular issue could be worked around by registering the lifetimescope with itself.

nblumhardt commented 6 years ago

@vincentparrett ILifetimeScope is already implicitly registered in the child scope; if you resolve ILifetimeScope, you always get the one that you're in. HTH!

tillig commented 6 years ago

@vincentparrett If you add an ILifetimeScope constructor parameter to an object it will be populated with the lifetime scope from which the consuming object is resolved. You should never have to register a scope with itself. You should also be able to register your additional user class as part of a lambda during BeginLifetimeScope.

vincentparrett commented 6 years ago

@nblumhardt it doesn't appear to be behaving that way. When I comment registering the childscope, I then get a resolution issue with the IWcfUser

None of the constructors found with 'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type 'Continua.Session' can be invoked with the available services and parameters: Cannot resolve parameter 'Continua.Shared.Application.Services.IWcfUser wcfUser' of constructor 'Void .ctor(Autofac.ILifetimeScope, Continua.Shared.Data.IDatabaseFactory, Continua.Shared.Logging.ILogger, Continua.Shared.Application.Services.IWcfUser)'. Cannot resolve parameter 'Continua.ISessionPrincipal principal' of constructor 'Void .ctor(Autofac.ILifetimeScope, Continua.Shared.Data.IDatabaseFactory, Continua.Shared.Logging.ILogger, Continua.ISessionPrincipal)'.

This is because the IWcfUser is only registered in the child scope.

vincentparrett commented 6 years ago

@tillig I want to believe that, but the error I posted above was when I commented out registering the childscope with itself. The childscope is the only place where we register IWcfUser.

tillig commented 6 years ago

Cannot resolve parameter 'Continua.Shared.Application.Services.IWcfUser and Cannot resolve parameter 'Continua.ISessionPrincipal principal' are the keys there. It's not failing to resolve the lifetime scope, it's failing to resolve two other things. The code you posted above isn't enough to troubleshoot the issue (eg, we don't know where userName comes from) but it appears you may be looking at the wrong thing.

vincentparrett commented 6 years ago

My apologies, I thought I had reverted another experiement (also trying to remove the use of Update) and that was causing the above resolution error, that instance appears to be working fine now. I still have another few instances to tackle.. should I just remove my comments to avoid muddying waters?

tillig commented 6 years ago

No, it's fine. I'm glad things are working.

vincentparrett commented 6 years ago

@tillig I may have spoke too soon. I got the same error again, but after restarting I'm not able to reproduce it. I'll keep trying!

tillig commented 6 years ago

@vincentparrett If you run into more issues, consider StackOverflow. I'm there, many others are there, and it's a good place to post a bit more code and explanation so we can help you without taking this particular discussion thread into the weeds. If the SO answer results in a good summary of a use case where Update is needed, that would be a good follow up here.

vincentparrett commented 6 years ago

Will do... I'm back to the original error when removing the childscope registration so something is definitely amis.

vincentparrett commented 6 years ago

FWIW, the issue I am seeing only occurs when used with WCF. I created a simple console app to test the theory and in that case the ILifetimescope resolved was indeed correct, however when used with WCF, it's resolving the root container. I'm using a "copy" of the autofac.wcf code so will log a bug there once I figure out how to create a small reproducable case.

Wiesenwischer commented 6 years ago

What to do when using PRISM for WPF with Autofac as DI? It is not a problem to use the ContainerBuilder to register additional services etc in modules which are loaded at startup. But what can I do when a module is loaded on demand (cause the container has already been built up)?

tillig commented 6 years ago

@BadMadDev There is already an issue for this in Prism. It will require work that they may or may not choose to do. I recommend taking it up with the folks owning that project.

evil-shrike commented 6 years ago

Hi.

I'm struggling to implement a Autofac-based solution for building complex object graphs in my app. My issues seem to be related to the "immutable container" idea discussed here. So let me describe my use case. I apologies if it become too overloaded description. I'll try to keep it as clean as I can.

Context

The app is based on our own framework. That framework contains a notion of "XApplication". XApplication contains a set of subsystems. Examples of such subsystems are: Storage, Domain, Security, Reporting, and so on. Kind of enterprise line-of-business stuff.

An application based on the framework should define its structure in terms of XApplication and its subsystems. To do this it's provided with kind of a DSL builder - XApplicationBuilder.

Logically the process of an app initialization looks like:

            ContainerBuilder builder = new ContainerBuilder();
            XApplicationBuilder appBuilder = new XApplicationBuilder(builder);

            // 3. it's kind of user code - defining the structure of application
            new XApplicationFactory().Build(appBuilder);

            appBuilder.Apply();
            var container = builder.Build();
            var app = container.Resolve<IXApplication>();

"User code" means code of an application that uses the framework. The app defines xapplication's structure in its XApplicationFactory using an XApplicationBuilder instance. Here's an example of such definition logic:

public void Build(XApplicationBuilder builder)
{
    builder
        // Storage
        .WithStorageSubsystem()
            .AddStorage(storageKind, conStrDef, conStrInit)
            .AddStorageSqlite("log")
                .AddConnection()
                    .AsFile(Path.Combine(Hosting.ContentRootPath, "log.db"))
                .End()
                .AddInitialization(XStorageInstanceInitializationBehavior.Update)
            .End()
            .WithDiagnostics().CommandExecuted(true).UsePerfCounters(false).End()
        .End()
         // Domain
        .WithDomainSubsystem()
            .WithModel()
                .LoadFrom(Path.Combine(Hosting.ContentRootPath, "Metadata", "metadata.xml"))
            .End()
            .AddLocalSessionFactory()
            .AddInstanceFactoryProvider<DomainObjects.Factories.FactoryProvider>()
            .WithScopes()
                .AddScope()
                    .WithSavePipeline()
                        .Step<SurveyOrganizerUpdateSessionStep>(UpdateSessionStage.BeforeDomainChecks)
                    .End()
                .End()
            .End()
        .End()
        // Security
        .WithSecuritySubsystem()
            .WithUserProvider<UserProvider>().End()
            .WithAzManager<AzManager>().UseUserPrivilegesAsActionPrivileges().End()
        .End()
        // DataSources
        .WithDataSourcesSubsystem()
            .AddMetadataSource(Path.Combine(Hosting.ContentRootPath, "Metadata", "DataSources","data-sources.xml"))
        .End();
}

Here the app defines Domain, Storage, Security and DataSources subsystems. Each subsystem definition contains some specific logic for that subsystem. Let's look at Domain subsystem definition. It says that the subsystem should load domain model from the xml-file, use "local session" (that talks to storage as opposite to remote session that call services), registers "InstanceFactory" - component that will create generated c# classes for types from model (like Department, User and so on), defines "scopes" - it's kind of pipelines inside session (during save and load). Particular details aren't very important. I just wanted illustrate that it's kind a complex graph of objects.

Then user code calls XApplicationBuilder.Apply() where I should do something that will help to build a XApplication when user code ask for it in the subsequent var container = builder.Build(); and container.Resolve<IXApplication>().

As you can see till now we didn't see any Autofac specifics despise the fact that a ContainerBuilder was passed into XApplicationBuilder. In real app it won't be a standalone builder but a nested one:

            containerBuilder.RegisterBuildCallback(container =>
            {
                container.TryResolve(out IXApplicationFactory appFactory);
                container.TryResolve(out IEnumerable<IXApplicationFactoryAddon> addons);

                m_appScope = container.BeginLifetimeScope(nestedBuilder =>
                {
                    var appBuilder = new XApplicationBuilder(nestedBuilder);
                    appFactory.Build(appBuilder);

                    foreach (var addon in addons)
                    {
                        appBuilder.RegisterPreprocessor(addon.PreProcessApplication);
                    }
                });
                {
                }
            });

where containerBuilder is a Builder from ASP.NET Core app's Startup. But I'm afraid it's too overloaded for understanding so let's suppose it's just a ContainerBuilder.

So let's back to XApplicationBuilder. What the problem I have here and how it's related to "immutable container" idea.

Problem

It's hard to build everything as a single resolution. Like when we register everything via ContainerBuilder.RegisterType and resolve a root and the whole object tree is created.

Every subsystem has its own "assembler" - some kind of configuration logic. An subsystem can also have nested structure like "Storage Instances" for Storage subsystem or "Data Sources" for DataSources subsystem.

For example for Storage subsystem its assembler does the following:

    foreach (XStorageInstanceData storageInfo in configData.Storages)
    {
        XStorageInstance instance =
            XStorageInstanceCustomFactory.Instance.Create(context, storageInfo);

        subsystem.AddStorage(instance);
        if (storageInfo.IsDefault)
        {
            subsystem.SetDefaultStorage(storageInfo.Name);
        }
    }

I should tell what configData and configData.Storages are. When user code defines app's structure via fluent-builder like WithStorageSubsystem().AddStorage() then the builder actually creates some object model. It's not final objects themselves and not Autofact's registrations (at least now), it's kind of DSL definitions describing what the app wants. We can call it as "application configuration". So configData above is the configuration object for Storage subsystem (created inside fluent-builder returned by WithStorageSubsystem).

Here you might say "hey, why do you need these assemblers, just register types in container in your builders". Assemblers are the legacy of the current approach that I'm trying to migrated from. It is based on Unity and ObjectBuilder (some internal part of Unity). Idea here is that every component has three participants: config-object, assembler and component - assembler takes config-object and builds component.

At first I tried to replace Unity with Autofac. But it's not easy. I have kind of drill-down building logic. Storage subsystem's assembler calls to its storages' assemblers and so on. At each level assemblers register a component being currently built before going down. For example storage subsystem's assembler registers the subsystem then calls to nested storage's assembler. This allows us in Storage's constructor accept all parent components.

But I can't do the same with Autofac as mixing of registration and resolution logic is forbidden. So it's hard to come up with an alternative approach in "immutable container" world. I'd be appreciated for any hints. Thanks.

tillig commented 6 years ago

There's a lot to parse here and it may be that others can help you with your fairly complex situation. Sadly, while I'm happy to help unblock folks and I realize the challenging situation people are in... there are only two project owners and we're spread pretty thin between support, actual patching/dev on the project, and so on. As such, I can't really write the code for you. The guidance I would give you to start unblocking you is the same as the ideas I mentioned at the top of the discussion:

I see you went straight to container build callbacks and I don't think you need to go that low level.

It sounds like you were using Unity and you may be sort of new to Autofac, so I would recommend looking at our documentation to help you get ideas of how to get dynamic registrations in place:

SlayersAlpha3 commented 6 years ago

My problem remains, simply enough, that my code isn't compiled until after application start. Its on-demand, and staggered. And yes, I can have a new Container per in-memory assembly as they are created, but to me, the container is supposed to be what contains. Instead I have to pool containers.

evil-shrike commented 6 years ago

Let me discuss a more focused problem . Imagine a component that accept some kind of metadata. It can be loaded by the component from disk or supplied from user code. If such metadata contains some extensibility points where CLR types can be specified (think of plugins) then the component will want to create instances via DI (instead of Activator.CreateInstance). Surely, the component can use its own ContainerBuilder but it would be nice to attach a parent container as it can contain some shared services. The component cannot know everything about all services to explicitly re-register them all in the child container. So it'd be very nice to have something this:

ComponentContext containerParent = ..
ContainerBuilder containerBuilder = new ContainerBuilder();
containerBuilder.RegisterSource(new ParentContainerSource(containerParent));

Does it make sense? I found ExternalRegistrySource type that looks as what I need but it's internal.

tillig commented 6 years ago

@evil-shrike Absolutely - rolling your own registration source is a wonderful solution to providing dynamic registrations. That's how we handle things internally like IEnumerable<T> support or "Any Concrete Type Not Already Registered" ("ACTNARS"). There is documentation on that, though exactly what the source should (or shouldn't) provide is totally application dependent so it's not really something we can provide guidance on.

I suggested this very solution to the folks in the Prism project as one option for dynamically loadable modules.

If you try it, let us know how it goes. Perhaps you could create a NuGet package with something generically usable for plugin loading.

Here are some internal ones to get you started:

nelsonlaquet commented 5 years ago

I'm working on a new project and just ran into the deprecation warning that lead me to this issue.

Which overload do you use? I'd like to use Update(IContainer)

Have you already resolved something from the container before calling Update? Yes

What is the scenario? Basically the whole .net core WebHost / HostBuilder deal. I'd like to register a bunch of services via .net core's own IOC API via the ConfigureThing extension methods, and then extend the configuration with autofac specific code before building the final container. I'd like the configuration of the container to depend on services that are injected via the container.

The reason why I discovered this whole issue was that I am writing a simple configuration abstraction. Basically, I want to have classes like:

[BindToConfiguration("Azure:ResourceManager")]
public class AzureResourceManagerOptions
{
    // properties/ctor here...
}

That will be scanned for, populated by the appropriate configuration via .net core's IConfiguration, then injected back into the container AsSelf/SingleInstance. To make matters more complicated, I do not use the built-in IConfiguration.Bind methods - instead, I use a custom service (called ObjectMachine) that gives me a lot more flexibility when populating these types from config. Specifically one feature of ObjectMachine is being able to instantiate/hydrate polymorphic types.

The way in which I implement polymorphic types is via another service called IPolyJsonTypes that is used as a central repository for how types are to be discriminated against. IPolyJsonTypes registers types that have this attribute attached to them:

[PolyJson(typeof(Document), "branch")]
public class BranchDocument : Document
{
    // properties/ctor here...
}

Both the BindToConfiguration and PolyJson attributes inherit from a base attribute called InfoProviderAttribute, which is scanned for during configuration and registers ITypeInfo objects in the container AsSelf/SingleInstance. The concrete implementation of IPolyTypes has a ctor parameter of IEnumerable<PolyJsonInfo> (PolyJsonInfo is provided by PolyJsonAttribute and implements ITypeInfo).

So: PolyJson provides PolyJsonInfo singletons into the container, and those are picked up by IPolyJsonTypes, which is used by ObjectMachine to properly convert from .net core's IConfiguration to my own DTOs.

So with that all out of the way, my solution was simple... Create an interface called IContainerConfigurator that is scanned for after all of the other configuration is done, and have its only method Configure(ContainerBuilder builder) get invoked against a new container builder, that then updates my main IContainer.

The code that would implement the above feature looks like:

[Inject(InjectionScope.Singleton, typeof(IContainerConfigurator))]
public class ConfigBinder : IContainerConfigurator
{
    private readonly IConfiguration config;
    private readonly ObjectMachine objects;
    private readonly IEnumerable<ConfigBinderInfo> binders;

    public ConfigBinder(IConfiguration config, ObjectMachine objects, IEnumerable<ConfigBinderInfo> binders = null)
    {
        this.config = config;
        this.objects = objects;
        this.binders = binders ?? Enumerable.Empty<ConfigBinderInfo>();
    }

    public void Configure(ContainerBuilder builder)
    {
        foreach (var binder in binders)
        {
            builder
                .RegisterInstance(GetConfig(binder.ConfigSection, binder.ConfigType))
                .As(((IPleaseDontDoThis) binder.ConfigType).ClrType)
                .SingleInstance();
        }
    }

    public T GetConfig<T>(string section) =>
        (T) GetConfig(section, SpoType.Get<T>());

    public object GetConfig(string sectionName, ISpoType type)
    {
        var section = config.GetSection(sectionName).ToToke();
        var converted = objects.Convert(section, type);
        if (!objects.IsAllGood(converted, out var errors))
            throw Spoception.InvalidOperation($"Could not convert config section {sectionName} to type {type.FullName}: {errors.FormatNameEqualsValue()}");

        return objects.WriteObject(converted, type);
    }
}

But of course that leaves me with this issue - how can IContainerConfigurators configure the container after it's built? I can't instantiate ConfigBinder without an ObjectMachine, which won't work unless it has an IPolyJsonTypes instance, which I can't create unless I expose my ITypeInfo objects outside of the context of a container. Basically causing me to unwind a large part of how this project has been thus far architected.

Two Containers: I don't want to use two containers because of how the Configure_Serivce_ extension methods work in the .net core world. I can't call ContainerBuilder.Populate twice because I'm using AutofacServiceProviderFactory with my generic hosts, which handles the call to .Populate for me. Also, this needs to work with both the new generic host builder, as well as asp.net core v2's WebHost - preferably with the same container initialization.

Two containers could possibly work if there was a way to either A) create two containers from a single ContainerBuilder or B) copy a container builder's registrations into a fresh container builder.

ILifetimeScope: this was the most promising thread, and may work for me. The biggest issue that happens here is my configuration objects from ConfigBinder get registered on the ILifetimeScope, but not the main container - which doesn't seem like a problem until a service registered directly on the root IContainer requests a configuration object that it cannot reach.

I suppose I could have services that depend on dynamically registered services be registered in the lifetime scope, with everything else being registered on the root container... But this feels kinda gross, and requires that you know more details up front about your dependencies.

So, basically, that's where I am now. Tomorrow I may look into how complicated it would be to not have to use autofac to instantiate the objects that further configure my container builder. I don't prefer this solution, however, as everything else falls so neatly into place with the single cavate of Update being deprecated.

tillig commented 4 years ago

With the release of 5.0 containers are truly immutable after build and ContainerBuilder.Update is entirely removed from the package. ContainerBuilder.Update was marked obsolete in November 2016, so it's been about two years advance notice that this was coming. We plan on adding some documentation based on the discussion in this issue to help people have the safety of an immutable container but with the dynamic behavior they desire.

For folks who are unable or unwilling to change their application architecture to support a pattern that does not require ContainerBuilder.Update, you may stay on 4.9.4, the final release of Autofac. Note that we will not be issuing any further fixes or features for that version, so you remain there in an unsupported state.

If you feel there is a behavior that is missing, we'd love to see an enhancement issue filed with more specifics on what you would like to see and how you envision that feature working within the scope of immutable containers: pseudo-code sample usage, a description of the feature in detail, things you've tried that aren't working, that sort of thing.