You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何.NET依赖注入控制台程序无报错以退出码0终止?

问题描述

我需要开发一个名为Port的.NET控制台应用程序,它引用了包含数据模型等内容的名为Data的类库项目。

Data项目中用于扩展IServiceCollection的静态类及方法代码如下:

public static class ServiceCollectionsFish
{
    public static IServiceCollection RegisterFishServices(this IServiceCollection services, IConfiguration configuration)
    {
        services.AddDbContext<FishContext>(o =>
        {
            o.UseSqlServer(configuration.GetConnectionString("Legacy"));
            o.EnableDetailedErrors();
            o.EnableSensitiveDataLogging();
        });

        services.AddTransient<IBrandRepository, BrandRepository>();
        services.AddTransient<IBrandGroupRepository, BrandGroupRepository>();

        return services;
    }
}

该Data项目同时被一个WEB API项目引用,运行正常。

控制台应用包含Program.cs和appsettings.json,Program.cs中构建配置的静态方法:

static void BuildConfig(IConfigurationBuilder builder)
{
    builder.SetBasePath(Directory.GetCurrentDirectory())
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
        .AddEnvironmentVariables();
}

主方法代码:

static void Main(string[] args)
{
    var builder = new ConfigurationBuilder();
    BuildConfig(builder);

    var host = Host.CreateDefaultBuilder()
        .ConfigureServices((context, services) => {
            services.RegisterFishServices(builder.Build());
            services.AddTransient<IStart, Start>();
        }).Build();

    var start = ActivatorUtilities.CreateInstance<Start>(host.Services);
    start.Test();
}

Start类代码:

public class Start : IStart
{
    private readonly IBrandRepository _brandRepository;

    public Start(IBrandRepository brandRepository)
    {
        _brandRepository = brandRepository;
        // 此处代码正常运行
        Brand_ brand = _brandRepository.GetById(new Guid("8D09F3E2-322B-4AFF-A4F2-57BDE98A78B5")).Result;
        Console.WriteLine("OK");
    }

    public async Task Test()
    {
        try
        {
            // 程序执行到此处直接退出,后续代码不执行
            Brand_? brand = await _brandRepository.GetById(new Guid("8D09F3E2-322B-4AFF-A4F2-57BDE98A78B5"));
            Console.WriteLine(brand);
            Console.WriteLine("OK");
        }
        catch(Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
    }
}

问题:调用Test()方法时,程序从未执行到Console.WriteLine(brand)就直接以退出码0终止,无异常抛出;但在构造函数中执行相同逻辑却能正常运行,请问原因是什么?


原因分析

核心问题是同步方法调用异步方法未等待:

  • Main方法是返回void的同步方法,调用start.Test()这个异步方法时,没有等待其执行完成。
  • 异步方法执行到await _brandRepository.GetById(...)时,会释放当前线程回到Main方法,此时Main已经执行完所有代码,直接终止程序,导致异步后续的数据库查询、打印逻辑根本没机会执行。
  • 构造函数中用.Result是同步阻塞等待异步方法完成,会一直等到数据库操作结束再继续,所以程序不会提前退出,逻辑能正常执行。

解决方案

有两种常见的修复方式:

方式1:将Main改为异步入口方法

.NET支持异步的Main方法,修改后可以正常等待异步操作完成:

static async Task Main(string[] args)
{
    var builder = new ConfigurationBuilder();
    BuildConfig(builder);

    var host = Host.CreateDefaultBuilder()
        .ConfigureServices((context, services) => {
            services.RegisterFishServices(builder.Build());
            services.AddTransient<IStart, Start>();
        }).Build();

    var start = ActivatorUtilities.CreateInstance<Start>(host.Services);
    await start.Test(); // 等待异步方法执行完成
}

方式2:同步阻塞等待异步方法完成

如果不想修改Main的返回类型,可以调用.Wait()或.GetAwaiter().GetResult()来阻塞等待:

static void Main(string[] args)
{
    var builder = new ConfigurationBuilder();
    BuildConfig(builder);

    var host = Host.CreateDefaultBuilder()
        .ConfigureServices((context, services) => {
            services.RegisterFishServices(builder.Build());
            services.AddTransient<IStart, Start>();
        }).Build();

    var start = ActivatorUtilities.CreateInstance<Start>(host.Services);
    start.Test().Wait(); // 阻塞等待异步方法执行完毕
}

内容的提问来源于stack exchange,提问作者Vladimir99

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 05:16:24