首页
/ MediatR在Blazor Server中的服务作用域管理实践

MediatR在Blazor Server中的服务作用域管理实践

2025-05-20 19:26:01作者:宣聪麟

在Blazor Server应用中使用MediatR时,服务作用域管理是一个需要特别注意的技术点。本文将深入探讨这一问题的本质,分析现有解决方案的优缺点,并提出更优雅的实践方案。

问题背景

Blazor Server应用采用电路(Circuit)模型,每个用户连接会建立一个持久化的电路。在这个模型中,Scoped服务的生命周期与电路绑定,而非传统ASP.NET Core中的请求级别。这与MediatR通常的使用模式产生了根本性的冲突。

当我们在MediatR处理器(Handler)中注入Scoped服务时,这些服务实例会在整个电路生命周期内保持存活。这可能导致:

  1. 数据库上下文等资源长时间不释放
  2. 状态意外共享
  3. 并发问题
  4. 资源泄漏

传统解决方案分析

开发者通常采用以下两种方式解决这个问题:

1. 处理器内创建作用域

public class SomeHandler : IRequestHandler<SomeQuery>
{
    private readonly IServiceScopeFactory _scopeFactory;

    public SomeHandler(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public async Task Handle(SomeQuery request, CancellationToken ct)
    {
        using var scope = _scopeFactory.CreateScope();
        var service = scope.ServiceProvider.GetRequiredService<ISomeService>();
        // 使用服务...
    }
}

优点

  • 明确控制服务生命周期
  • 每个请求获得全新服务实例

缺点

  • 代码重复
  • 手动解析服务不够优雅
  • 容易忘记创建作用域

2. 管道行为(Pipeline Behavior)

public class ScopedBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly IServiceScopeFactory _scopeFactory;

    public ScopedBehavior(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public async Task<TResponse> Handle(TRequest request, CancellationToken ct, RequestHandlerDelegate<TResponse> next)
    {
        using var scope = _scopeFactory.CreateScope();
        // 如何将作用域传递给处理器?
        return await next();
    }
}

优点

  • 集中管理作用域
  • 减少重复代码

缺点

  • 难以将作用域传递给处理器
  • 通知处理器(Notification Handler)难以共享同一作用域

进阶解决方案

自定义作用域标记属性

我们可以设计一个自定义属性来标记需要从请求作用域解析的服务:

[AttributeUsage(AttributeTargets.Parameter)]
public class FromRequestScopeAttribute : Attribute { }

public class SomeHandler : IRequestHandler<SomeQuery>
{
    private readonly ISomeService _service;

    public SomeHandler([FromRequestScope] ISomeService service)
    {
        _service = service;
    }
    // ...
}

实现方案

  1. 自定义服务提供者工厂
public class RequestScopedServiceProvider : IServiceProvider
{
    private readonly IServiceProvider _circuitProvider;
    private readonly IServiceScope _requestScope;

    public RequestScopedServiceProvider(IServiceProvider circuitProvider)
    {
        _circuitProvider = circuitProvider;
        _requestScope = circuitProvider.CreateScope();
    }

    public object GetService(Type serviceType)
    {
        // 优先从请求作用域解析
        return _requestScope.ServiceProvider.GetService(serviceType) 
            ?? _circuitProvider.GetService(serviceType);
    }

    public void Dispose() => _requestScope.Dispose();
}
  1. 集成到MediatR管道
public class ScopedBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
    private readonly IServiceScopeFactory _scopeFactory;

    public ScopedBehavior(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    public async Task<TResponse> Handle(TRequest request, CancellationToken ct, RequestHandlerDelegate<TResponse> next)
    {
        using var scope = _scopeFactory.CreateScope();
        var requestServices = new RequestScopedServiceProvider(scope.ServiceProvider);
        
        // 将requestServices传递给处理器...
        return await next();
    }
}

通知处理器的特殊考虑

通知处理器(Notification Handler)需要特别注意,因为它们可能由请求处理器触发,但运行在不同的上下文中。为确保一致性:

  1. 在发布通知时传递当前作用域
  2. 使用自定义的发布策略确保通知处理器使用正确的作用域
public class ScopedPublisher : INotificationPublisher
{
    public async Task Publish(IEnumerable<NotificationHandlerExecutor> handlers, INotification notification, CancellationToken ct)
    {
        // 从当前上下文获取作用域
        var scope = GetCurrentScope();
        
        foreach (var handler in handlers)
        {
            using var handlerScope = scope.CreateScope();
            var handlerInstance = handlerScope.ServiceProvider.GetService(handler.HandlerType);
            await handler.HandlerCallback(handlerInstance, notification, ct);
        }
    }
}

最佳实践建议

  1. 明确区分服务作用域

    • 电路级服务:用户会话状态等
    • 请求级服务:数据库上下文等
  2. 采用分层架构

    • 业务逻辑层使用请求级作用域
    • 表示层可以使用电路级作用域
  3. 文档化服务生命周期

    • 为每个服务明确标注其预期生命周期
    • 在DI注册时添加注释
  4. 自动化测试验证

    • 编写测试验证服务作用域行为
    • 特别是并发场景下的行为

结论

在Blazor Server中使用MediatR时,正确处理服务作用域是保证应用稳定性的关键。通过自定义作用域管理策略,我们可以在保持代码整洁的同时,确保资源得到正确管理。本文提出的解决方案既考虑了请求处理器的需求,也兼顾了通知处理器的特殊情况,为开发者提供了一套完整的实践方案。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
22
6
docsdocs
OpenHarmony documentation | OpenHarmony开发者文档
Dockerfile
217
2.23 K
flutter_flutterflutter_flutter
暂无简介
Dart
523
116
ohos_react_nativeohos_react_native
React Native鸿蒙化仓库
JavaScript
210
285
nop-entropynop-entropy
Nop Platform 2.0是基于可逆计算理论实现的采用面向语言编程范式的新一代低代码开发平台,包含基于全新原理从零开始研发的GraphQL引擎、ORM引擎、工作流引擎、报表引擎、规则引擎、批处理引引擎等完整设计。nop-entropy是它的后端部分,采用java语言实现,可选择集成Spring框架或者Quarkus框架。中小企业可以免费商用
Java
9
1
RuoYi-Vue3RuoYi-Vue3
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
982
580
pytorchpytorch
Ascend Extension for PyTorch
Python
67
97
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
564
87
openHiTLSopenHiTLS
旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
C
1.02 K
399
GLM-4.6GLM-4.6
GLM-4.6在GLM-4.5基础上全面升级:200K超长上下文窗口支持复杂任务,代码性能大幅提升,前端页面生成更优。推理能力增强且支持工具调用,智能体表现更出色,写作风格更贴合人类偏好。八项公开基准测试显示其全面超越GLM-4.5,比肩DeepSeek-V3.1-Terminus等国内外领先模型。【此简介由AI生成】
Jinja
33
0