DbContext 模型构建器中的用户详细信息
User details within the DbContext model builder
我是 razor pages/efcore/aspnet identity 的新手,一直在努力解决这个问题,但它打败了我。
基本上,我使用 AspNet Identity 进行用户身份验证和授权。我用一个额外的 OrganisationId 扩展了 AspNetUsers,它是组织实体的 FK;并将 ID 作为声明添加到身份声明存储中。这很好用。
现在我需要根据经过身份验证的用户的 organizationId 设置 efcore global filter,这样他们就只能查看分配给他们组织的数据。
但是,我无法在 ModelBuilder 中访问经过身份验证的用户详细信息。
public class SDMOxContext : IdentityDbContext<
ApplicationUser, ApplicationRole, string,
ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin,
ApplicationRoleClaim, ApplicationUserToken>
{
public SDMOxContext(DbContextOptions<SDMOxContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Set global filter so users can only see projects within their organisation.
builder.Entity<Project>().HasQueryFilter(project => project.OrganisationId == 1);
}
我需要输入用户 organizationid,而不是全局过滤器中的 1,它存储为用户声明。通常我是这样理解的:
User.FindFirstValue("OrganisationId")
但是,用户在当前上下文中不存在。
我建议将解决方案分解成两部分
在您的 dbcontext 中添加组织 ID,就像多租户环境中的租户 ID 一样。例如,参见此 link。
下一个挑战是将组织 ID 作为参数传递给 DbContext 构造函数。为此,您可以为 DbContext 创建一个工厂。由于您将 OrganizationId 存储在声明中。工厂可以访问相同的声明 HttpContext 并在实例化 dbContext 时将组织 ID 作为参数传递。
它并不完美,但可以给你一个起点。
So I would need to apply the query filter at a later stage, ie. after user authentication? Any pointers where to start with a mid-tier/logic-tier approach?
虽然这是对架构的看法,但我将其分解如下:
数据层——该层负责访问正在执行的应用程序之外的资源(通常)。这包括;数据库、文件 IO、Web Api 等
Business/Logic-Tier - 这一层的职责(可以进一步细分)应该验证、授权、验证和构建代表业务需求的 对象。为了构建这些对象,它可能会使用一个或多个数据访问对象(例如,它可能会使用 IO DA 从本地文件系统或 Azure 存储中检索图像,并使用数据库 DA 来检索有关该图像的元数据)。
Presentation/Exposure-Tier - 这一层的职责是将对象包装并转换为消费者需要的(winforms、wpf、html、json、xml、二进制序列化等)。
通过将逻辑排除在数据层之外(即使在多租户系统中),您可以获得跨所有系统访问数据的能力(相信我,这里可以赚很多钱)。
这可能比我在这么短的地方所能解释的要多得多,而且非常我的意见。我将 略去一些内容 但现在开始了。
数据层
namespace ProjectsData
{
public interface IProjectDA
{
IProjectDO GetProject(Guid projectId, Guid organizationId);
}
private class ProjectDA : DbContext, IProjectDA
{
public ProjectDA (...)
public IEnumerable<ProjectDO> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder builder) {... }
public IProjectDO GetProject(Guid projectId, Guid organizationId)
{
var result = Projects
.FirstOrDefault(p => p.Id == projectId && OrganizationId = organizationId);
return result;
}
}
public interface IProjectDO{ ... }
private class ProjectDO: IProjectDO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
逻辑
namespace ProjectBusiness
{
public interface IProjectBO { .. }
public interface IOrganization
{
Guid OrganizationId { get; }
}
private class ProjectBA : IProjectBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
private readonly IOrganization _organization;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity,
IOrganizationContext organizationContext)
{
_projectDA = projectDA;
_identity = identity;
}
public IProjectBO GetProject(Guid id)
{
var do = _projectDA
.GetProject(id, _organization);
var result = map.To<ProjectBO>(do);
return result;
}
}
public interface IProjectBO { .. }
private class ProjectBO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
因此在这些情况下,数据层知道请求的类型,但多租户不了解。它不限制基于任何东西的所有请求。这种 架构 在很多方面都有优势。
首先,在上面的示例中,您的产品起飞,您的主管想知道哪些类别最受欢迎。
namespace StatisticsBusiness
{
public interface IStatisticsBO
{
IEnumerable<ICategoryStatisticBO> CategoryStatistics { get; set; }
}
public interface ICategoryStaticBO
{
Guid CategoryId { get; }
int ProjectCount { get; }
}
private class StatisticsBA : IStatisticsBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity)
{
_projectDA = projectDA;
_identity = identity;
}
public IEnumerable<IProjectBO GetOrderedCategoryPopularity()
{
var dos = _projectDA
.GetProjectCategoryCounts()
var result = map.To<IEnumerable<IStatisticsBO>>(dos);
return result;
}
}
public interface IStatisticsBO{ .. }
private class StatisticsBO
{
public Guid CategoryId { get; }
public int ProjectCount { get; }
}
}
注意:有些人更喜欢将表达式作为谓词传递。两者都有其优点和缺点。如果您决定走谓词路线,那么您将必须决定是否所有数据访问类型都使用谓词。只需意识到对 IO 或 Web Api 使用谓词可能会付出更多的努力,这是值得的。
其次,某些要求导致您无法使用Entity Framework。您可以将其替换为 Dapper 或其他更好的新产品 technology/framework。您只需要创建新的 I<whataver>DA
类,因为使用逻辑不知道除这些接口 (programming against an interface, the L in SOLID programming principles and the I in SOLID programming principles) 之外的任何其他内容。
我不会一直使用这种模式,因为对于一些较小的网站来说,它的工作量太大了。
我是 razor pages/efcore/aspnet identity 的新手,一直在努力解决这个问题,但它打败了我。
基本上,我使用 AspNet Identity 进行用户身份验证和授权。我用一个额外的 OrganisationId 扩展了 AspNetUsers,它是组织实体的 FK;并将 ID 作为声明添加到身份声明存储中。这很好用。
现在我需要根据经过身份验证的用户的 organizationId 设置 efcore global filter,这样他们就只能查看分配给他们组织的数据。
但是,我无法在 ModelBuilder 中访问经过身份验证的用户详细信息。
public class SDMOxContext : IdentityDbContext<
ApplicationUser, ApplicationRole, string,
ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin,
ApplicationRoleClaim, ApplicationUserToken>
{
public SDMOxContext(DbContextOptions<SDMOxContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Set global filter so users can only see projects within their organisation.
builder.Entity<Project>().HasQueryFilter(project => project.OrganisationId == 1);
}
我需要输入用户 organizationid,而不是全局过滤器中的 1,它存储为用户声明。通常我是这样理解的:
User.FindFirstValue("OrganisationId")
但是,用户在当前上下文中不存在。
我建议将解决方案分解成两部分
在您的 dbcontext 中添加组织 ID,就像多租户环境中的租户 ID 一样。例如,参见此 link。
下一个挑战是将组织 ID 作为参数传递给 DbContext 构造函数。为此,您可以为 DbContext 创建一个工厂。由于您将 OrganizationId 存储在声明中。工厂可以访问相同的声明 HttpContext 并在实例化 dbContext 时将组织 ID 作为参数传递。
它并不完美,但可以给你一个起点。
So I would need to apply the query filter at a later stage, ie. after user authentication? Any pointers where to start with a mid-tier/logic-tier approach?
虽然这是对架构的看法,但我将其分解如下:
数据层——该层负责访问正在执行的应用程序之外的资源(通常)。这包括;数据库、文件 IO、Web Api 等
Business/Logic-Tier - 这一层的职责(可以进一步细分)应该验证、授权、验证和构建代表业务需求的 对象。为了构建这些对象,它可能会使用一个或多个数据访问对象(例如,它可能会使用 IO DA 从本地文件系统或 Azure 存储中检索图像,并使用数据库 DA 来检索有关该图像的元数据)。
Presentation/Exposure-Tier - 这一层的职责是将对象包装并转换为消费者需要的(winforms、wpf、html、json、xml、二进制序列化等)。
通过将逻辑排除在数据层之外(即使在多租户系统中),您可以获得跨所有系统访问数据的能力(相信我,这里可以赚很多钱)。
这可能比我在这么短的地方所能解释的要多得多,而且非常我的意见。我将 略去一些内容 但现在开始了。
数据层
namespace ProjectsData
{
public interface IProjectDA
{
IProjectDO GetProject(Guid projectId, Guid organizationId);
}
private class ProjectDA : DbContext, IProjectDA
{
public ProjectDA (...)
public IEnumerable<ProjectDO> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder builder) {... }
public IProjectDO GetProject(Guid projectId, Guid organizationId)
{
var result = Projects
.FirstOrDefault(p => p.Id == projectId && OrganizationId = organizationId);
return result;
}
}
public interface IProjectDO{ ... }
private class ProjectDO: IProjectDO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
逻辑
namespace ProjectBusiness
{
public interface IProjectBO { .. }
public interface IOrganization
{
Guid OrganizationId { get; }
}
private class ProjectBA : IProjectBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
private readonly IOrganization _organization;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity,
IOrganizationContext organizationContext)
{
_projectDA = projectDA;
_identity = identity;
}
public IProjectBO GetProject(Guid id)
{
var do = _projectDA
.GetProject(id, _organization);
var result = map.To<ProjectBO>(do);
return result;
}
}
public interface IProjectBO { .. }
private class ProjectBO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
因此在这些情况下,数据层知道请求的类型,但多租户不了解。它不限制基于任何东西的所有请求。这种 架构 在很多方面都有优势。
首先,在上面的示例中,您的产品起飞,您的主管想知道哪些类别最受欢迎。
namespace StatisticsBusiness
{
public interface IStatisticsBO
{
IEnumerable<ICategoryStatisticBO> CategoryStatistics { get; set; }
}
public interface ICategoryStaticBO
{
Guid CategoryId { get; }
int ProjectCount { get; }
}
private class StatisticsBA : IStatisticsBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity)
{
_projectDA = projectDA;
_identity = identity;
}
public IEnumerable<IProjectBO GetOrderedCategoryPopularity()
{
var dos = _projectDA
.GetProjectCategoryCounts()
var result = map.To<IEnumerable<IStatisticsBO>>(dos);
return result;
}
}
public interface IStatisticsBO{ .. }
private class StatisticsBO
{
public Guid CategoryId { get; }
public int ProjectCount { get; }
}
}
注意:有些人更喜欢将表达式作为谓词传递。两者都有其优点和缺点。如果您决定走谓词路线,那么您将必须决定是否所有数据访问类型都使用谓词。只需意识到对 IO 或 Web Api 使用谓词可能会付出更多的努力,这是值得的。
其次,某些要求导致您无法使用Entity Framework。您可以将其替换为 Dapper 或其他更好的新产品 technology/framework。您只需要创建新的 I<whataver>DA
类,因为使用逻辑不知道除这些接口 (programming against an interface, the L in SOLID programming principles and the I in SOLID programming principles) 之外的任何其他内容。
我不会一直使用这种模式,因为对于一些较小的网站来说,它的工作量太大了。