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

ASP.NET Core 2中在中间件及跨项目类访问DbContext的最佳方式

嘿,针对你在ASP.NET Core 2项目里遇到的「中间件和跨项目类如何访问BloggingContext(结合仓储+工作单元模式)」的问题,我整理了几个最佳实践方案,帮你理清思路:

一、中间件中访问的正确姿势

ASP.NET Core的中间件默认是单例生命周期,而BloggingContext和你的IUnitOfWork都是Scoped(和请求绑定)的服务,所以不能直接在中间件的构造函数里注入它们,否则会导致生命周期不匹配的问题。这里有两种靠谱的方式:

方式1:在Invoke方法中直接注入Scoped服务

ASP.NET Core 2及以上版本支持直接在InvokeAsync方法参数里注入Scoped服务,框架会自动为每个请求创建对应的作用域,非常简洁:

public class CustomBlogMiddleware
{
    private readonly RequestDelegate _next;

    public CustomBlogMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    // 直接在方法参数里注入IUnitOfWork,框架会处理作用域
    public async Task InvokeAsync(HttpContext context, IUnitOfWork unitOfWork)
    {
        // 在这里使用工作单元操作BloggingContext
        var recentBlogs = unitOfWork.BlogRepository.GetRecent(5);
        // 比如把数据存入HttpContext.Items,供后续中间件/控制器使用
        context.Items["RecentBlogs"] = recentBlogs;

        await _next(context);
    }
}

方式2:通过IServiceProvider创建作用域(兼容更复杂场景)

如果你的中间件逻辑需要手动控制作用域(比如在请求前后都要操作数据库),可以注入IServiceProvider,手动创建作用域来获取服务:

public async Task InvokeAsync(HttpContext context, IServiceProvider serviceProvider)
{
    // 创建独立作用域,确保Scoped服务使用后被正确释放
    using (var scope = serviceProvider.CreateScope())
    {
        var unitOfWork = scope.ServiceProvider.GetRequiredService<IUnitOfWork>();
        
        // 请求前的操作:比如记录访问日志到数据库
        unitOfWork.LogRepository.Add(new AccessLog { Path = context.Request.Path });
        await unitOfWork.CommitAsync();

        await _next(context);

        // 请求后的操作:比如更新请求统计
        unitOfWork.StatisticsRepository.IncrementRequestCount();
        await unitOfWork.CommitAsync();
    }
}
二、跨项目类中的访问方案

对于其他类库项目中的类,核心原则是依赖注入优先,不要直接实例化BloggingContext或UnitOfWork,而是通过构造函数注入依赖:

步骤1:在类库中定义依赖接口

假设你的类库有一个统计服务,需要访问博客数据:

// 其他项目中的类
public class BlogStatisticsService
{
    private readonly IUnitOfWork _unitOfWork;

    // 构造函数注入IUnitOfWork,依赖抽象而非具体实现
    public BlogStatisticsService(IUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }

    public int GetTotalPublishedBlogs()
    {
        return _unitOfWork.BlogRepository.Count(b => b.IsPublished);
    }
}

步骤2:在ASP.NET Core项目中注册服务

回到Web项目的Startup.cs,把类库中的服务注册到DI容器:

public void ConfigureServices(IServiceCollection services)
{
    // 注册DbContext
    services.AddDbContext<BloggingContext>(options =>
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    
    // 注册工作单元(假设你的实现类是UnitOfWork)
    services.AddScoped<IUnitOfWork, UnitOfWork>();
    
    // 注册类库中的服务,生命周期和IUnitOfWork保持一致(Scoped)
    services.AddScoped<BlogStatisticsService>();
}

步骤3:在需要的地方使用

不管是控制器、中间件还是其他服务,都可以通过构造函数注入BlogStatisticsService来间接访问BloggingContext:

public class HomeController : Controller
{
    private readonly BlogStatisticsService _statsService;

    public HomeController(BlogStatisticsService statsService)
    {
        _statsService = statsService;
    }

    public IActionResult Index()
    {
        var totalBlogs = _statsService.GetTotalPublishedBlogs();
        ViewData["TotalBlogs"] = totalBlogs;
        return View();
    }
}
三、关键注意事项
  • 生命周期匹配:BloggingContext和IUnitOfWork必须注册为Scoped(AddDbContext默认就是Scoped),每个请求对应一个实例,避免并发问题和资源泄漏。
  • 禁止单例服务直接注入Scoped服务:如果有单例服务需要访问数据库,必须通过IServiceProvider创建作用域来获取Scoped服务,否则会导致Scoped服务被单例持有,引发线程安全问题。
  • 依赖抽象而非具体:始终注入IUnitOfWork而非UnitOfWork,注入IBlogRepository而非BlogRepository,这样你的代码更易测试和维护。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:18:35