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

.NET 6 Startup中创建Microsoft Graph订阅的实现问题

.NET 启动阶段自动创建Microsoft Graph订阅的实现

问题根因

  • .NET 原生不支持Startup阶段的异步配置操作,且官方无后续支持计划。
  • 若在Startup.ConfigureServices()阶段阻塞线程执行Graph订阅创建,即使代码放在services.AddControllers()调用之后,该阶段控制器、路由、中间件管道均未完成构建,Graph API发送订阅校验请求时,应用无法返回要求的200响应,必然导致订阅注册失败。
  • 最初尝试的阻塞式实现代码如下,该方案无法正常工作:
Task<Subscription> subscriptionTask = gsc.Subscriptions.Request().AddAsync(subscription);
subscription = subscriptionTask.GetAwaiter().GetResult();

services.AddSingleton<GraphServiceClient>(provider => gsc);
services.AddSingleton<Subscription>(provider => subscription);

已验证的变通方案

方案1:启动后手动调用接口创建

  1. 服务注册阶段仅注册空对象单例:
    services.AddSingleton<GraphServiceClient>(provider => gsc);
    services.AddSingleton<Subscription>(provider => subscription);
    
  2. 控制器暴露订阅创建接口,应用启动后手动调用触发:
    public async Task<IActionResult> CreateMSGraphSubscription()
    {
        try
        {
            Subscription newSubscription = await _gsc.Subscriptions.Request().AddAsync(_sub);
                    
            // 逐字段更新单例属性
            _sub.Id = newSubscription.Id;
            _sub.AdditionalData = newSubscription.AdditionalData;
            _sub.ApplicationId = newSubscription.ApplicationId;
            _sub.CreatorId = newSubscription.CreatorId;
                
            _graphRenewalSettings.IsActive = true;
        }
        catch (Exception e)
        {
            return new BadRequestObjectResult(e);
        }
    
        return new OkObjectResult(_sub);
    }
    
  • 缺陷:需要人工介入调用接口,无法实现应用启动后订阅自动就绪。

方案2:利用应用生命周期回调自动注册

在Startup.Configure()中注入IHostApplicationLifetime,在方法末尾注册应用启动完成后的回调,自动执行订阅创建:

lifetime.ApplicationStarted.Register(async () =>
{
    Subscription newSubscription = await gsc.Subscriptions.Request().AddAsync(sub);

    // 逐字段更新单例属性
    sub.Id = newSubscription.Id;
    sub.AdditionalData = newSubscription.AdditionalData;
    sub.ApplicationId = newSubscription.ApplicationId;
    sub.CreatorId = newSubscription.CreatorId;

    graphRenewalSettings.IsActive = true;
});
  • 优势:无需人工调用接口,应用启动完成、管道就绪后会自动执行订阅注册,此时Graph的校验请求可以被正常响应。
  • 缺陷:必须预先注册空的Subscription单例,创建完真实订阅后逐字段拷贝属性,实现冗余,无法直接注入已完成初始化的订阅实例。

更优雅的实现方式

不需要逐字段更新单例,用IHostedService配合单例包装类即可实现直接注入可用订阅实例的需求:

  • 定义一个单例级别的订阅状态包装类,内部持有Subscription实例属性,用于存储初始化完成的订阅对象。
  • 实现一个IHostedService托管服务,在其StartAsync方法(框架原生支持异步执行,执行时机在应用启动完成、请求管道完全就绪之后)中完成Graph订阅创建,将创建好的实例赋值给包装类的Subscription属性。
  • DI注册时同时注册包装类单例和托管服务,所有需要使用订阅实例的地方直接注入包装类即可,不需要做任何字段拷贝操作。

核心原则:所有需要依赖应用自身HTTP接口响应的初始化逻辑,都不能放在ConfigureServices或Configure执行阶段运行,必须放到应用已经完成启动、开始监听请求之后执行,否则外部请求无法命中应用接口,必然导致校验失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 15:45:39