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

F#编写ASP.NET Core中间件如何共享初始化后的扫描状态?

解决方案

方案1:依托ASP.NET Core依赖注入(推荐)

该方案完全符合ASP.NET Core设计规范,生命周期由框架托管,和C#中使用单例类的思路完全对齐,不需要引入额外全局状态:

  • 第一步:定义扫描结果存储结构和扫描服务,扫描逻辑在服务构造时一次性执行
open System
open System.Collections.Generic
open System.Reflection
open Microsoft.AspNetCore.Http

// 根据业务调整扫描结果的存储结构
type AssemblyScanResult = {
    ScannedTypes: IReadOnlyDictionary<string, Type>
}

// 扫描服务,构造函数执行时完成一次性扫描
type AssemblyScannerService(targetAssemblies: Assembly seq) =
    let scanResult = 
        targetAssemblies
        |> Seq.collect (fun asm -> asm.GetTypes())
        // 替换为自定义过滤逻辑,比如筛选实现指定接口的类型
        |> Seq.filter (fun t -> t.IsClass && not t.IsAbstract)
        |> Seq.map (fun t -> t.FullName, t)
        |> dict
        |> fun d -> { ScannedTypes = d }

    // 对外暴露只读扫描结果
    member _.ScanResult = scanResult
  • 第二步:在Program.fs启动逻辑中注册服务为单例,整个应用生命周期只会初始化一次,扫描逻辑仅执行一次
let builder = WebApplication.CreateBuilder(args)

// 传入需要扫描的程序集
let targetAssemblies = [ Assembly.GetExecutingAssembly() ]
builder.Services.AddSingleton<AssemblyScannerService>(fun _ -> AssemblyScannerService(targetAssemblies))

// 其他服务注册逻辑
// ...

let app = builder.Build()
  • 第三步:中间件构造函数直接注入该单例服务,复用预扫描结果
type YourCustomMiddleware(next: RequestDelegate, scannerService: AssemblyScannerService) =
    // 中间件构造时直接拿到预扫描结果,本地存储复用
    let scanResult = scannerService.ScanResult

    member _.InvokeAsync(context: HttpContext) =
        // 直接使用扫描结果处理业务,不会触发重复扫描
        let targetType = scanResult.ScannedTypes["目标类型名"]
        // 其他业务逻辑
        // ...
        next.Invoke(context)

方案2:F#静态延迟初始化(轻量场景可选)

如果扫描逻辑无需依赖其他注入服务,可以用Lazy<_>实现全局一次性初始化:

open System
open System.Reflection
open System.Collections.Generic

module GlobalScanResult =
    // 延迟初始化,仅第一次访问时执行扫描,且只会执行一次
    let private scannedTypes = Lazy<_>.Create(fun () ->
        Assembly.GetExecutingAssembly().GetTypes()
        // 替换为自定义过滤逻辑
        |> Seq.filter (fun t -> t.IsClass && not t.IsAbstract)
        |> Seq.map (fun t -> t.FullName, t)
        |> dict
    )

    // 对外暴露只读扫描结果
    let get () = scannedTypes.Value

中间件直接调用即可:

type YourCustomMiddleware(next: RequestDelegate) =
    member _.InvokeAsync(context: HttpContext) =
        let scanResult = GlobalScanResult.get()
        // 业务逻辑处理
        // ...
        next.Invoke(context)

注意:如果扫描逻辑需要读取配置、依赖其他DI托管的服务,优先选择方案1。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 03:48:05