-
-
Notifications
You must be signed in to change notification settings - Fork 837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Discussion: ContainerBuilder.Update Marked Obsolete #811
Comments
I believe you could get around the builder.RegisterType<SomeService>().As<IService>();
// then later
builder.RegisterType<SomeOtherService>().As<IService>().PreserveExistingDefaults(); What that does is add the registration but make sure that previous registrations are used first, so if you have a singleton, you'd still get a previous registration first. The only time that may affect you is if you're resolving A new way you may be able to work around it is to use the But otherwise, what I'm hearing is that you may be able to get away from Note that part of what we're trying to do is not only provide a good container but push people towards better practices and help them avoid pitfalls. Bringing over practices from one container to another isn't something we're really interested in addressing since each container has its own quirks. On the other hand, it is interesting to provide a good feature set; get people to use good IoC practices that avoid container inconsistency; and be able to bring the container forward by adding new features. |
There is scenario in which I have to use
public IServiceProvider ConfigureServices(IServiceCollection services) {
IContainer container = Program.Container;
// configuring MVC services here
// I should create new container, populate MVC services into it
// and then update main container to be able to use it instead of
// built-in aspnetcore DI container
ContainerBuilder builder = new ContainerBuilder();
builder.Populate(services);
builder.Update(container);
return container.Resolve<IServiceProvider>();
} How can I rewrite this scenario? |
Hey @tillig , I am quite new to AutoFac and want to replace Unity in my application with AutoFac. When I tried this I ran into some issues.Most of them were quite easy to solve. My main problem on the other hand "seemed" to be easy to solved, by using the
The overload I use is
Yes I do resolve some services, before all registrations are registered. Why this is the case I will explain within the use case below.
The application I want to integrate AutoFac into is a ASP.NET WebAPI 2 based Web Service with OWIN integration. What I need to do at the startup of the application is registering services (which hold some configuration stuff so it is easily accessable with DI) and do registrations based on the config informations which are held by the mentioned services. To make this more clear here is some pseudo sample code:
I know this code is not correct in some ways, but I just wanted to display my situation. Also the resolving sometimes does happen in another class so it would not be possible for me to hold an instance locally and use this. This is not the only case I need this and not the only service I need that for. The application also uses extensions (MEF and Microsoft Addin Framework) to extend it's funcionality. So there is an use case where additional registrations have to be made when the application is already running. The application also supports updating and installing these extensions on runtime, and changing configurations on runtime. So in this case I may have to change some registrations, remove or update them. The next thing I evaluated were your suggestions using modules or lambdas to extend a container/scope with registrations.
In principal I would be ok with these solutions because I could solve most of my problems with it. But I wondered how these functions handle additional registrations for a container/scope. So I went through the sources and discovered, that these functions also use the So in conclusion: Is there any way to implement my use case with AutoFac in an "correct" way and if not, will modules or lambdas still be supported in future releases? Thank you! |
@twirpx Is your issue with If so an |
@alexmg It seems that it is not a solution. It is just another way to pass The problem is that the container building happens before WebHostBuilder initialization. |
Hi @Raphy1302. The first thing that comes to mind looking at the example code is what happens when something asks for the service that wasn't actually registered based on configuration? The same conditional logic would end up having to be used at the point of consuming the service, and most likely without that service taking a direct dependency, which implies the need for some kind of service location anti-pattern. The Null Object pattern with a lambda based registration that checks the configuration state could remove the need to update the container in a case such as this.
Other services could still take a dependency on Would something like that get you out of trouble if the |
Hey @alexmg Thanks for your quick response! What is about my last point I mentioned about the modules and lambda expressions. I still wonder how this is gonna work when the update function is not available anymore? Thanks! |
My use case is to inject the IContainer itself into the constructor, so that I could use it in See http://stackoverflow.com/a/22260100/263003 var builder = new ContainerBuilder();
// register stuff here
var container = builder.Build();
// register the container
var builder2 = new ContainerBuilder();
builder2.RegisterInstance<IContainer>(container);
builder2.Update(container); public class WindowService : IWindowService
{
private readonly IContainer container;
public WindowService(IContainer container)
{
this.container = container;
}
} |
hi @huan086, i've solved your problem using a different way which is also available in nuget package and tested. |
@huan086 If you need the container, it implies you're doing some service location. I'd recommend using the solution from @osoykan or another service locator like CommonServiceLocator rather than forcing the container to be able to resolve itself. |
@osoykan @tillig Don't think I'm using service locator. All I really need is to I realize that I don't need the See http://stackoverflow.com/questions/4609360/resolve-icontainer |
The overload I use is Update(IContainer).
Yes, consider the following example: builder.Register ...
...
var container = builder.Build();
var extenders = container.Resolve<IEnumerable<ISomeExtender>>()
extenders.ForEach(e => e.ConfigureODataStuffHereForEachModule(HttpConfiguration));
// has to happen here, while I'm still configuring WebApi and OData
var updater = new Autofac.ContainerBuilder();
updater.RegisterWebApiFilterProvider(HttpConfiguration);
updater.Update(container);
... The removal of the "Update" method will force me to manage two different containers: one for extensibility and other for general logic - one big mess. |
@arieradle What stops you from registering the filter provider when building the container the first time? No requests would be running through the system at the time so it shouldn't matter. |
@tillig RegisterWebApiFilterProvider requires HttpConfiguration object to be ready I presume. Will RegisterWebApiFilterProvider work if I still need to register some OData points and attribute routing for WebApi after it runs? |
@arieradle It needs the config object so it can add itself as the filter provider, but it won't resolve or really do anything until a request comes in. Give it a shot. I think you'll be fine, and it would mean no call to |
@tillig Could be, I'll try. Have another issue: var signalrResolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container);
GlobalHost.DependencyResolver = signalrResolver;
var updater = new ContainerBuilder();
updater.RegisterInstance(signalrResolver.Resolve<Microsoft.AspNet.SignalR.Infrastructure.IConnectionManager>());
updater.RegisterType<SignalRAssemblyLocator>().As<Microsoft.AspNet.SignalR.Hubs.IAssemblyLocator>().SingleInstance();
updater.Update(container); |
@arieradle Well, there are two things there. Let's tackle each in turn. The second line is the easiest:
There's no reason you couldn't register that earlier. It's not being resolved or activated, it's just a registration. The first line is a tiny bit trickier and I'm not sure why you need it. The way I'm reading it...
I'm not sure why that's required. The thing you're requesting is already in the container, so you're just adding a second, static copy of it. My initial thought is that you probably don't need that line at all. However, let's say you do need it or want it for some reason. You can do delayed resolution of an item by registering things as a lambda. I think that's something which may of the You could take this line...
...and change it to a lambda that uses the
The lambda doesn't execute until you actually try to resolve it, so by the time you need to get the object you'll have already set the static Again, though, verify that circular registration is necessary. If you can resolve it from the container already, you shouldn't need to re-add it. |
I think I might need to use I'm building a platform (user ASP.NET) that supports pluggable "apps." I'm using Autofac extensively currently for global and per-request scoped services. Now I need to allow my plugged in apps to register components/services but I'm not sure how to do that. Note that the app lifetimes pretty much match the entire global lifetime of the web app. Here's the challenge: One of the current services is the singleton I've seen that there is an How would you approach this in a way most consistent with best practices for Autofac? |
There are a few ways I've seen things in these situations handled. These aren't the only ways, and it's not One Size Fits All, but mix and match as appropriate. Keeping in mind the original workarounds and fix tips I already posted... Consider Two ContainersIf 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. I have this myself in several of my larger apps - a container that has services used to index plugins (the "assembly scanning" mechanism, logging, that sort of thing); and a second container into which runtime requirements are registered like the set of plugin assemblies, required common dependencies, and so on. Don't Over-DI Bootstrap ItemsIt'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 ContainersIf 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, LambdasTruly, almost every container update I see in the wild 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 (as shown in the workarounds at the top). Given there's not a One Size Fits All solution, I can't say "here's the step-by-step way you, in your very specific app, would handle all of these cases." But the above should help. The two-container approach is the first thing I thought of when you mentioned your issue. Note: I copied these ideas to the top so they're not buried in the middle of this long chain. |
I might actually lean towards the Don’t Over-DI recommendation. I could probably get my AppManager and a few other key objects (like logging and configuration) done manually – and then add those to the container before it’s built.
If I went with two containers, would I then end up needing to pass around both containers (as my apps, for example need services form both). Or is there some kind of composite resolver that could be used? Passing around two seems onerous – and the app then needs to know which one has which services…
|
Instead of passing around two containers, consider the "share instances across containers" idea... var configuration = CreateTheConfigurationInstance();
builderForContainer1.RegisterInstance(configuration);
builderForContainer2.RegisterInstance(configuration); You could also do something like... builderForContainer2.Register(ctx => container1.Resolve<Something>()); That is, use a lambda to resolve something from one container using another container. That latter snippet has a bad smell, in my opinion. I think sharing bootstrap object instances across containers is fine. Those instances may even be factories. var loggingFactory = new LoggingFactory(configuration);
builderForContainer1.Register(ctx => loggingFactory.CreateLogger()).As<ILogger>();
builderForContainer2.Register(ctx => loggingFactory.CreateLogger()).As<ILogger>(); If you have stuff in your app startup container that you also want to use during runtime, then you really need them to be singletons (instances). The problem is that if they aren't then the dependencies they are using from the first container aren't consistent with the dependencies registered in the second container. That's the Let me use an analogy: think about a circular build dependency. (I'm going off topic a bit here, but stick with me.) Say you have two libraries (Library 1 and Library 2) and some custom build tasks. The custom build tasks are used to build both libraries... but they also depend on Library 1. You could address that by building Library 1 without the tasks, saving a copy of that, building the build tasks, then re-building Library 1 and Library 2 using the built version of the tasks... but your build tasks are then using a stale/inconsistent version of Library 1. That's not cool. The right way to handle that is to either stop building Library 1 with the same set of build tasks that depend on it... or split the stuff out of Library 1 that the build tasks need into, say, Library 0, and build Library 0 without the tasks and let Library 1 and 2 build with the tasks. Bringing it back on topic: It may seem like there's no problem with that initial circular dependency... and that's the "I don't care if my app's dependencies are inconsistent, I need to use It also may seem like there's no way to unwind the circular dependency... and that's the "It's too hard to change my design so I'll do the least work possible and make it work despite the complexity!" argument. But there is a way to unwind it by changing the problem definition. In the circular dependency analogy, splitting Library 1 up allows you to change the way things work; in the app startup vs. plugins scenario, consider what really needs to be shared. Is it actual object instances or is it registrations? Can you use Autofac modules to help you? Can you use lambdas or How, exactly, to unwind that is going to be up to you. I can't really provide much beyond what I already have when faced with fairly sweeping generalities. I'd just say... if you see the solution getting really complicated ("this container resolves from that container which reads from some factory that...") then you may not have the right solution. An example of a widely-used two-container setup with some delegation around it is ASP.NET Web API 2. In there you'll see that the application configuration object has a static That comes with its own challenges since, at least in Web API 2, that doesn't support per-request services. In some cases things get incorrectly cached, too, so something that's registered "instance-per-dependency" will be held for the app lifetime and become a singleton. A lot of things changed in ASP.NET Core, where most things do actually come from the container... but when you look at how things like middleware I can't tell you if you need your own version of a |
Loving this discussion, apologies for jumping in a bit late. I find myself leaning heavily towards making the registrations-in-child-container pattern a first-class concept, but then found myself wondering, will supporting registrations in child scopes interfere with any of the simplification/optimisation opportunities we're trying to open up? (Also, FWIW, System.Composition is another immutable container implementation.) |
I'm starting with the idea of getting the minimal set of app-loading items up and running without DI. That seems simplest. I think that will probably work but if it doesn't I may try the multiple containers. I might end up trying to anyway because I'm trying to decide if one app's registrations should be visible for another app -- there are pros and cons -- but if they need to be per-app then one global container plus one container per app is probably the only choice. If I do end up with multiple containers, I would share instances for the singletons, but I'm really not sure how I'd deal with per-request scoped objects (yet). [And thanks for being so responsive. This sort of plugin situation seems like it would be at least semi-common...] |
Child lifetime scopes (another item mentioned in the top post) could also be a solution. Create the container with the application startup stuff. Each plugin then gets its own child lifetime scope and treat those lifetime scopes "like containers" for the plugins. Or if it's a central container with multiple "apps" or whatever your terminology is... instead of passing around When building the child app scopes, instead of passing around a var regActions = new List<Action<ContainerBuilder>>();
regActions.Add(b => b.RegisterInstance(something));
regActions.Add(b => b.RegisterType<SomeType>()); ...and later just run those. var appScope = container.BeginLifetimeScope(b => {
foreach(var a in regActions)
{
a(b);
}
}); This "treat a child lifetime scope as a container" thing is how the multitenant integration works. |
Update gets used by bots built on the Microsoft Bot Framework as the BotBuilder SDK uses Autofac internally. If users wish to register additional types in to the container already present in the BotBuilder for use in other areas of their bot, Said a different way, the BotBuilder SDK exposes an |
@bc3tech Interesting. We'll have to file an issue over there to see if we can get them to change their architecture. Thanks! |
@tillig I've started an internal thread on this |
I was in this same situation about OWIN and SignalR until I found a solutions |
I have done my example of container.update usage:
Build operation is expensive (276.4384ms), update operation is cheap (9.3741ms). |
Which overload do you use? 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. |
@vincentparrett |
@vincentparrett If you add an |
@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
This is because the IWcfUser is only registered in the child scope. |
@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. |
|
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? |
No, it's fine. I'm glad things are working. |
@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! |
@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. |
Will do... I'm back to the original error when removing the childscope registration so something is definitely amis. |
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. |
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)? |
@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. |
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. ContextThe 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 - Logically the process of an app initialization looks like:
"User code" means code of an application that uses the framework. 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). Then user code calls As you can see till now we didn't see any Autofac specifics despise the fact that a
where So let's back to ProblemIt's hard to build everything as a single resolution. Like when we register everything via 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 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. |
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: |
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. |
Let me discuss a more focused problem .
Does it make sense? |
@evil-shrike Absolutely - rolling your own registration source is a wonderful solution to providing dynamic registrations. That's how we handle things internally like 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: |
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? Have you already resolved something from the container before calling Update? What is the scenario? 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 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 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 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. |
With the release of 5.0 containers are truly immutable after build and For folks who are unable or unwilling to change their application architecture to support a pattern that does not require 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. |
In 8a89e94 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 whereUpdate
is literally the only way to handle it, we need to determine how best to fixUpdate
.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.Update(IContainer)
,Update(IContainer, ContainerBuildOptions)
, orUpdate(IComponentRegistry)
?ContainerBuilder
prior to container building?Update
, have you already resolved something from the container before callingUpdate
?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.
AutoActivate
orIStartable
gets resolved.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)...
Update
Isn't Consistent WithBuild
When you Build a container, a couple of things happen:
IEnumerable<T>
andFunc<T>
are resolved. These should only be added to the container one time.AutoActivate
andIStartable
) are automatically resolved.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 asBuild
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 ofIContainer
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 newContainerBuilder.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:
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:
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.
Use Conditional Registrations
Autofac 4.4.0 introduced
OnlyIf()
andIfNotRegistered
extensions. These allow you to execute a specific registration only if some other condition is true. Here's the documentation. Quick example: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.The text was updated successfully, but these errors were encountered: