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

ASP.NET Core中如何在Service类用Session实现EF Core全局过滤器?

解决ASP.NET Core 2.0中Service获取Session异常及EF Core全局过滤器实现问题

首先咱们拆解你遇到的两个核心问题:一是调用GetStructurePath()时抛出的Session未配置异常,二是如何在不让DbContext直接依赖HttpContext的前提下,实现动态全局过滤器。

一、修复Session未配置异常

你当前Configure方法的中间件顺序有问题——app.UseSession()必须放在app.UseMvc()之前,否则Mvc请求无法访问Session;同时要确保它在app.UseAuthentication()之后(因为Session通常依赖已认证的用户上下文)。

修改后的Configure方法代码:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, IDbInitializer dbInitializer)
{
    ApplicatonSettings.ConnectionString = Configuration.GetConnectionString("DefaultConnection");

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseDatabaseErrorPage();
    }
    else
    {
        //app.UseExceptionHandler("/Home/Error");
    }

    app.UseStaticFiles();
    app.UseRequestLocalization();
    app.UseAuthentication();
    app.UseSession(); // 移到UseMvc之前,确保Mvc请求能访问Session
    dbInitializer.Initialize(); // 启动时初始化数据库,此时无请求上下文,Session值为null是正常的
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}

另外你注册IHttpContextAccessor的方式是正确的(ASP.NET Core 2.0需要手动注册),这部分无需改动。

二、优化EF Core全局过滤器实现(避免DbContext依赖HttpContext)

你当前在DbContext构造函数中获取Session值的方式存在隐患:DbContext在请求初期就会被实例化,此时Session可能还未被设置(比如Login动作是在后续请求中写入Session的),而且会导致DbContext间接依赖HttpContext,不符合分层设计原则。

推荐方案:在Service层实现动态过滤

放弃DbContext的全局过滤器,转而在业务Service层中根据Session值动态添加过滤条件。这种方式更灵活,也能保持DbContext的独立性。

  1. 先封装一个Scoped服务专门获取StructurePath(功能单一,职责更清晰):
public interface IStructurePathProvider
{
    string GetCurrentStructurePath();
}

public class StructurePathProvider : IStructurePathProvider
{
    private readonly IHttpContextAccessor _contextAccessor;

    public StructurePathProvider(IHttpContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }

    public string GetCurrentStructurePath()
    {
        return _contextAccessor.HttpContext?.Session?.GetString("StructurePath");
    }
}
  1. 在ConfigureServices中注册这个服务:
services.AddScoped<IStructurePathProvider, StructurePathProvider>();
  1. 在你的业务Service中使用这个Provider添加过滤条件,比如PersonService:
public class PersonService
{
    private readonly ApplicationDbContext _dbContext;
    private readonly IStructurePathProvider _pathProvider;

    public PersonService(ApplicationDbContext dbContext, IStructurePathProvider pathProvider)
    {
        _dbContext = dbContext;
        _pathProvider = pathProvider;
    }

    public IEnumerable<PersonStruct> GetFilteredPersons()
    {
        var structurePath = _pathProvider.GetCurrentStructurePath();
        if (!string.IsNullOrEmpty(structurePath))
        {
            return _dbContext.PersonStruct.Where(p => p.Hierarchy.StartsWith(structurePath));
        }
        // 如果没有Session值,返回全部数据
        return _dbContext.PersonStruct;
    }
}

备选方案:动态全局过滤器(如果必须用DbContext全局过滤)

如果你坚持要使用EF Core的全局过滤器,可以通过Scoped服务动态更新过滤参数。EF Core 2.0的全局过滤器是静态的,但我们可以通过在DbContext中定义一个可变属性,每次请求时更新它:

修改你的ApplicationDbContext:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    private readonly IStructurePathProvider _pathProvider;
    // 用于动态存储当前请求的StructurePath
    public string CurrentStructurePath { get; private set; }

    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IStructurePathProvider pathProvider) 
        : base(options)
    {
        _pathProvider = pathProvider;
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        
        // 定义全局过滤器,使用CurrentStructurePath作为动态参数
        modelBuilder.Entity<PersonStruct>().HasQueryFilter(p => 
            string.IsNullOrEmpty(CurrentStructurePath) || p.Hierarchy.StartsWith(CurrentStructurePath));
    }

    // 在查询前更新CurrentStructurePath
    public override DbSet<TEntity> Set<TEntity>()
    {
        CurrentStructurePath = _pathProvider.GetCurrentStructurePath();
        return base.Set<TEntity>();
    }

    // 在保存数据前更新CurrentStructurePath
    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        CurrentStructurePath = _pathProvider.GetCurrentStructurePath();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }
}

这种方式下,每个请求的DbContext实例都会在查询或保存前更新CurrentStructurePath,全局过滤器就能正确使用当前Session的值了。

关键注意点

  • 绝对不要在DbContext构造函数中尝试获取Session:构造函数在请求初期执行,此时Session可能还未被初始化,必然会抛出异常。
  • 保持DbContext的纯洁性:DbContext只负责数据访问,不应该依赖HttpContext这类Web层的对象,这样你的数据访问层才能复用在非Web场景中。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 07:07:55