在 Autofac 中注册回调并在回调中再次构建容器 [英] Register callback in Autofac and build container again in the callback
问题描述
我有一个 dotnet 核心应用程序.我的 Startup.cs
在 Autofac 中注册类型/实现.我的一个注册需要事先访问服务.
var containerBuilder = new ContainerBuilder();containerBuilder.RegisterSettingsReaders();//这使得 ISettingsReader 可用我可以用它来读取我的 appsettings.jsoncontainerBuilder.RegisterMyInfrastructureService(options =>{options.Username = "foo"//这应该来自appsettings});containerBuilder.Populate(服务);var applicationContainer = containerBuilder.Build();
困境是,当我必须 .RegisterMyInfrastructureService
时,我需要使用之前注册的 ISettingsReader
(Autofac 容器还没有尚未建成).
我正在阅读有关在构建 autofac 容器后使用回调注册以执行某些内容的信息.所以我可以做这样的事情:
builder.RegisterBuildCallback(c =>{var stringReader = c.Resolve>();var usernameValue = stringReader.GetValue("用户名");//现在我有了我的用户名foo",但我想继续注册东西!像下面这样:containerBuilder.RegisterMyInfrastructureService(options =>{options.Username = usernameValue});//怎么办?再建?});
但问题是,在我想使用该服务后,不是为了启动服务或类似的操作而是继续注册需要我现在能够提供的设置的东西.>
我可以在回调结束时再次调用 builder.Build()
以便简单地重建容器而没有任何问题吗?这看起来有点奇怪,因为构建器已经构建好了(这就是回调被执行的原因).
用 autofac 解决这个困境的最佳方法是什么?
更新 1: 我读到像 builder.Update() 这样的东西现在已经过时了,因为容器应该是不可变的.这证实了我的怀疑,即构建容器、添加更多注册并再次构建并不是一个好的做法.
换句话说,我可以理解使用注册构建回调不应该用于注册额外的东西.但是,问题仍然存在:如何处理这些问题?
这个讨论问题 解释了很多,包括解决必须更新容器的方法.我将在此处进行总结,但在该问题中有 很多 的信息,尝试从头复制是没有意义的.
- 熟悉注册组件和传递参数的所有方式一>.不要忘记解析参数、可以动态放置参数的模块等.
- Lambda 注册几乎解决了我们见过的所有问题.如果您需要注册一些提供配置的东西,然后在稍后使用该配置作为不同注册的一部分 - lambdas 将是巨大的.
- 考虑中间接口,例如创建一个由
ISettingsReader
支持的IUsernameProvider
.IUsernameProvider
可以是 lambda(解析某些设置、读取特定设置等),然后下游组件可以直接采用IUsernameProvider
.
这类问题很难回答,因为如果您利用 lambdas 和参数之类的东西,有很多方法可以解决必须构建/重建/重新构建容器的问题 -没有最佳实践",因为它始终取决于您的应用和您的需求.
就我个人而言,我通常会从 lambda 方法开始.
I have a dotnet core application.
My Startup.cs
registers types/implementations in Autofac.
One of my registrations needs previous access to a service.
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterSettingsReaders(); // this makes available a ISettingsReader<string> that I can use to read my appsettings.json
containerBuilder.RegisterMyInfrastructureService(options =>
{
options.Username = "foo" //this should come from appsettings
});
containerBuilder.Populate(services);
var applicationContainer = containerBuilder.Build();
The dilemma is, by the time I have to .RegisterMyInfrastructureService
I need to have available the ISettingsReader<string>
that was registered just before (Autofac container hasn't been built yet).
I was reading about registering with callback to execute something after the autofac container has been built. So I could do something like this:
builder.RegisterBuildCallback(c =>
{
var stringReader = c.Resolve<ISettingsReader<string>>();
var usernameValue = stringReader.GetValue("Username");
//now I have my username "foo", but I want to continue registering things! Like the following:
containerBuilder.RegisterMyInfrastructureService(options =>
{
options.Username = usernameValue
});
//now what? again build?
});
but the problem is that after I want to use the service not to do something like starting a service or similar but to continue registering things that required the settings I am now able to provide.
Can I simply call again builder.Build()
at the end of my callback so that the container is simply rebuilt without any issue? This seems a bit strange because the builder was already built (that's why the callback was executed).
What's the best way to deal with this dilemma with autofac?
UPDATE 1: I read that things like builder.Update() are now obsolete because containers should be immutable. Which confirms my suspicion that building a container, adding more registrations and building again is not a good practice.
In other words, I can understand that using a register build callback should not be used to register additional things. But then, the question remain: how to deal with these issues?
This discussion issue explains a lot including ways to work around having to update the container. I'll summarize here, but there is a lot of information in that issue that doesn't make sense to try and replicate all over.
- Be familiar with all the ways you can register components and pass parameters. Don't forget about things like resolved parameters, modules that can dynamically put parameters in place, and so on.
- Lambda registrations solve almost every one of these issues we've seen. If you need to register something that provides configuration and then, later, use that configuration as part of a different registration - lambdas will be huge.
- Consider intermediate interfaces like creating an
IUsernameProvider
that is backed byISettingsReader<string>
. TheIUsernameProvider
could be the lambda (resolve some settings, read a particular one, etc.) and then the downstream components could take anIUsernameProvider
directly.
These sorts of questions are hard to answer because there are a lot of ways to work around having to build/rebuild/re-rebuild the container if you take advantage of things like lambdas and parameters - there's no "best practice" because it always depends on your app and your needs.
Me, personally, I will usually start with the lambda approach.
这篇关于在 Autofac 中注册回调并在回调中再次构建容器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!