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
相关产品推荐
相关产品推荐

