ApplicationDbContext 中 OnModelCreating 中 ApplicationUser 的 Queryfilter 创建 StackOverflowException
Queryfilter on ApplicationUser in OnModelCreating in ApplicationDbContext creates StackOverflowException
我需要创建一个全局查询过滤器,只过滤那些属于特定租户的用户。
但是,我在将查询过滤器添加到 OnModelCreating 时遇到了计算器溢出问题。
我使用 IHttpContextAccessor 从当前登录的用户获取 TenantId。这与其他实体一起工作得很好,但 ApplicationUser 会产生错误。这可能是循环码的问题?
我的 ApplicationDbContext 如下(为清楚起见进行了缩写)
public class ApplicationDbContext
: IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserClaim<string>,
ApplicationUserRole, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>
{
private readonly IHttpContextAccessor _contextAccessor;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor contextAccessor)
: base(options)
{
_contextAccessor = contextAccessor;
}
public virtual Guid? CurrentTenantId
{
get
{
return Users.FirstOrDefault(u => u.UserName == _contextAccessor.HttpContext.User.Identity.Name)?.TenantId;
}
}
public virtual string CurrentUserName
{
get
{
return Users.FirstOrDefault(u => u.UserName == _contextAccessor.HttpContext.User.Identity.Name)?.UserName;
}
}
public DbSet<ApplicationUser> ApplicationUser { get; set; }
public DbSet<Tenant> Tenant { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Tenant>().HasQueryFilter(e => e.TenantId == CurrentTenantId);
builder.Entity<ApplicationUser>().HasQueryFilter(e => e.TenantId == CurrentTenantId);
}
}
}
我已将 services.AddHttpContextAccessor()
添加到启动时的 ConfifureServices 部分。
关于如何解决这个问题有什么建议吗?
有很多方法可以解决这个问题,但我只给你我喜欢的方法。
首先,您必须创建第二个 DbContext class,它可以访问您的用户数据库集,即not 不会对其应用查询过滤器。
public class UserDbContext : DbContext
{
public DbSet<ApplicationUser> Users { get; set; }
}
您在启动时以与当前 DbContext class 相同的方式注册,并且使用相同的连接字符串。
然后您可以继续创建服务,为您提供租户 ID。
public class TenantProvider : ITenantProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly UserDbContext _userDbContext;
private Guid _tenantId;
public TenantProvider(UserDbContext userDbContext,
IHttpContextAccessor httpContextAccessor)
{
_userDbContext = userDbContext;
_httpContextAccessor = httpContextAccessor;
}
private void SetTenantId()
{
if (_httpContextAccessor.HttpContext == null)
{
// Whatever you would like to return if there is no request (eg. on startup of app).
_tenantId = new Guid();
return;
}
_tenantId = _userDbContext.Users.FirstOrDefault(u => u.UserName == _httpContextAccessor.HttpContext.User.Identity.Name)?.TenantId;
return;
}
public Guid GetTenantId()
{
SetTenantId();
return _tenantId;
}
当然还有界面
public interface ITenantProvider
{
Guid GetTenantId();
}
你在启动时也注册这个服务。
services.AddScoped<ITenantProvider, TenantProvider>();
然后你修改你的ApplicationDbContext:
public class ApplicationDbContext
: IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserClaim<string>,
ApplicationUserRole, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>
{
private readonly IHttpContextAccessor _contextAccessor;
private Guid _tenantId;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor contextAccessor, ITenantProvider _tenantProvider)
: base(options)
{
_contextAccessor = contextAccessor;
_tenantId = _tenantProvider.GetTenantId();
}
public DbSet<ApplicationUser> ApplicationUser { get; set; }
public DbSet<Tenant> Tenant { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Tenant>().HasQueryFilter(e => e.TenantId == _tenantId);
builder.Entity<ApplicationUser>().HasQueryFilter(e => e.TenantId == _tenantId);
}
}
应该就是这样,不再有无限循环:)祝你好运
我需要创建一个全局查询过滤器,只过滤那些属于特定租户的用户。
但是,我在将查询过滤器添加到 OnModelCreating 时遇到了计算器溢出问题。
我使用 IHttpContextAccessor 从当前登录的用户获取 TenantId。这与其他实体一起工作得很好,但 ApplicationUser 会产生错误。这可能是循环码的问题?
我的 ApplicationDbContext 如下(为清楚起见进行了缩写)
public class ApplicationDbContext
: IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserClaim<string>,
ApplicationUserRole, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>
{
private readonly IHttpContextAccessor _contextAccessor;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor contextAccessor)
: base(options)
{
_contextAccessor = contextAccessor;
}
public virtual Guid? CurrentTenantId
{
get
{
return Users.FirstOrDefault(u => u.UserName == _contextAccessor.HttpContext.User.Identity.Name)?.TenantId;
}
}
public virtual string CurrentUserName
{
get
{
return Users.FirstOrDefault(u => u.UserName == _contextAccessor.HttpContext.User.Identity.Name)?.UserName;
}
}
public DbSet<ApplicationUser> ApplicationUser { get; set; }
public DbSet<Tenant> Tenant { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Tenant>().HasQueryFilter(e => e.TenantId == CurrentTenantId);
builder.Entity<ApplicationUser>().HasQueryFilter(e => e.TenantId == CurrentTenantId);
}
}
}
我已将 services.AddHttpContextAccessor()
添加到启动时的 ConfifureServices 部分。
关于如何解决这个问题有什么建议吗?
有很多方法可以解决这个问题,但我只给你我喜欢的方法。
首先,您必须创建第二个 DbContext class,它可以访问您的用户数据库集,即not 不会对其应用查询过滤器。
public class UserDbContext : DbContext
{
public DbSet<ApplicationUser> Users { get; set; }
}
您在启动时以与当前 DbContext class 相同的方式注册,并且使用相同的连接字符串。
然后您可以继续创建服务,为您提供租户 ID。
public class TenantProvider : ITenantProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly UserDbContext _userDbContext;
private Guid _tenantId;
public TenantProvider(UserDbContext userDbContext,
IHttpContextAccessor httpContextAccessor)
{
_userDbContext = userDbContext;
_httpContextAccessor = httpContextAccessor;
}
private void SetTenantId()
{
if (_httpContextAccessor.HttpContext == null)
{
// Whatever you would like to return if there is no request (eg. on startup of app).
_tenantId = new Guid();
return;
}
_tenantId = _userDbContext.Users.FirstOrDefault(u => u.UserName == _httpContextAccessor.HttpContext.User.Identity.Name)?.TenantId;
return;
}
public Guid GetTenantId()
{
SetTenantId();
return _tenantId;
}
当然还有界面
public interface ITenantProvider
{
Guid GetTenantId();
}
你在启动时也注册这个服务。
services.AddScoped<ITenantProvider, TenantProvider>();
然后你修改你的ApplicationDbContext:
public class ApplicationDbContext
: IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserClaim<string>,
ApplicationUserRole, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>
{
private readonly IHttpContextAccessor _contextAccessor;
private Guid _tenantId;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options, IHttpContextAccessor contextAccessor, ITenantProvider _tenantProvider)
: base(options)
{
_contextAccessor = contextAccessor;
_tenantId = _tenantProvider.GetTenantId();
}
public DbSet<ApplicationUser> ApplicationUser { get; set; }
public DbSet<Tenant> Tenant { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Tenant>().HasQueryFilter(e => e.TenantId == _tenantId);
builder.Entity<ApplicationUser>().HasQueryFilter(e => e.TenantId == _tenantId);
}
}
应该就是这样,不再有无限循环:)祝你好运