在 Development 环境下依赖注入的行为可能有所不同

奇怪的问题

本周被一个奇怪的问题困扰了一天。事情的起因是这样的:在某个 PR 合并后,我拉了最新代码,但是在我本地F5调试始终报错。示例代码如下:

复制代码
    public interface Interface1
    {
        void Method1();
    }

    public class MockSerivce
    {
        public MockSerivce(Interface1 interface1)
        {

        }
    }

builder.Services.AddSingleton<MockSerivce>();

报的错误呢也显而易见:

复制代码
Unable to resolve service for type 'DevelopmentTest.Interface1' while attempting to activate 'DevelopmentTest.MockSerivce'

我们没有注册 Interface1 到 DI 容器里,那自然实例化 MockSerivce 的时候就找不到依赖了。

但是奇怪的是:我其他同事们都没有这个问题,他们在本地调试的时候都好好的,并不会报错。并且在这个分支编译后的代码在开发服务器上运行的都很完美。

这个就有点冲击到我了,难道是我电脑有问题,VS 有问题,还是我人品有问题?

寻找答案

当然了代码是不会骗人的,造成以上问题一定不是我人品问题而是代码的问题。

经过一番尝试,我发现这个问题跟系统运行在哪个环境有关系。只要我把 launchSettings.json 里的 ASPNETCORE_ENVIRONMENTDevelopment 改成别的什么值,那么一切都运行正常了。正巧在我们组其他同事都维护一个自己的 appestings.username.json 然后运行在这个环境之下,也就是说他们都不运行在 Development 下。这就是为啥只有我会报错的原因了。

事情到了这一步,那么我们很容易猜测: .NET DI 系统在 Development 下是有骚操作的。在 Development 下它会进行依赖分析,如果依赖关系有错误,那么直接会报错。但是在其他环境下就不会提交分析校验,只有在运行时真正尝试实例化对象的时候才会报错。

当然靠猜测总是不太靠谱,干脆翻翻代码吧。很快就找到了:

复制代码
    internal static ServiceProviderOptions CreateDefaultServiceProviderOptions(HostBuilderContext context)
        {
            bool isDevelopment = context.HostingEnvironment.IsDevelopment();
            return new ServiceProviderOptions
            {
                ValidateScopes = isDevelopment,
                ValidateOnBuild = isDevelopment,
            };
        }

HostingHostBuilderExtensions 这个扩展类里很清楚的看到,只有在 Development 下 DefaultServiceProviderOptions 的 ValidateScopesValidateOnBuild 会被设置为 True。这就直接证明了上面的猜想。只有在 Development 下才会在启动的时候去校验依赖关系。

强制校验

既然找到了答案,那么让我们来试一下:强制开启依赖关系的校验。

复制代码
var builder = WebApplication.CreateBuilder(args);

builder.Host.UseDefaultServiceProvider(op =>
{
    op.ValidateOnBuild = true;
    op.ValidateScopes = true;
});

代码如上在 Host 上调用 UseDefaultServiceProvider 扩展方法,指定 ValidateScopesValidateOnBuild 都为 True

再次运行我们的项目,这个时候不管是在 Development 还是 Production 还是别的任何环境下,都会报错了。

复制代码
Unable to resolve service for type 'DevelopmentTest.Interface1' while attempting to activate 'DevelopmentTest.MockSerivce'

总结

通过以上我们可以发现 .NET 的 DI 系统,在 Development 环境下跟其他环境的行为是不同的。在 Development 下会提交进行依赖关系的校验,如果有问题会提前报错。所以我们调试的时候请尽量选择在 Development 下进行或者手动强制开启校验。这个问题很容易被忽视,至少我没在其他博文里见有人提到过。其实在微软的官方文档上是提到了,但也确实就是提了一嘴而已。

关于这个话题其实还没完,还有一个更有意思的问题:Captive dependency 可以聊一下。但是今天太晚了,改天吧。

参考:https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#scope-validation

相关推荐
csdn_aspnet1 天前
在无状态 ASP.NET Core 8 Web API 中实现 CSRF 令牌,无需 Views/MVC!
前端·csrf·.net core
EdisonZhou5 天前
MAF快速入门(22)声明式Agent实战
llm·aigc·agent·.net core
EdisonZhou10 天前
MAF快速入门(21)RC5引入的Script运行能力
llm·agent·.net core
EdisonZhou13 天前
MAF快速入门(20)基于File-Based App开发MVP项目
llm·agent·.net core
时光追逐者18 天前
一款基于 .NET 开源、跨平台应用程序自动升级组件
c#·.net·.net core
.NET修仙日记22 天前
构建社区照护桥梁:.NET Core3.1+MVC社区呼叫系统设计与实现
c#·毕业设计·.net·.net core·社区照护平台
唯情于酒23 天前
net core web api 使用log4net
c#·.net core
从入门到放弃-咖啡豆1 个月前
Alibaba Cloud Linux 部署.NET 8 环境 项目运行
linux·服务器·.net·.net core
VAllen1 个月前
ConcurrentNativeQueue<T>:一个使用 .NET 实现的零 GC 压力的无锁 MPSC 原生队列
c#·.net·性能测试·.net core·dotnet·csharp
EdisonZhou1 个月前
MAF快速入门(19)给Agent Skill添加脚本执行能力
llm·agent·.net core