Files
TakeoutSaaS.AdminApi/src/Infrastructure/TakeoutSaaS.Infrastructure/Common/Persistence/TenantAwareDbContext.cs

81 lines
2.7 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Microsoft.EntityFrameworkCore;
using System.Reflection;
using TakeoutSaaS.Shared.Abstractions.Entities;
using TakeoutSaaS.Shared.Abstractions.Ids;
using TakeoutSaaS.Shared.Abstractions.Security;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Infrastructure.Common.Persistence;
/// <summary>
/// 多租户感知 DbContext自动应用租户过滤并填充租户字段。
/// </summary>
public abstract class TenantAwareDbContext(
DbContextOptions options,
ITenantProvider tenantProvider,
ICurrentUserAccessor? currentUserAccessor = null,
IIdGenerator? idGenerator = null) : AppDbContext(options, currentUserAccessor, idGenerator)
{
/// <summary>
/// 当前请求租户 ID。
/// </summary>
protected long CurrentTenantId => tenantProvider.GetCurrentTenantId();
/// <summary>
/// 保存前填充租户元数据并执行基础处理。
/// </summary>
protected override void OnBeforeSaving()
{
ApplyTenantMetadata();
base.OnBeforeSaving();
}
/// <summary>
/// 应用租户过滤器到所有实现 <see cref="IMultiTenantEntity"/> 的实体。
/// </summary>
/// <param name="modelBuilder">模型构建器。</param>
protected void ApplyTenantQueryFilters(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
if (!typeof(IMultiTenantEntity).IsAssignableFrom(entityType.ClrType))
{
continue;
}
var methodInfo = typeof(TenantAwareDbContext)
.GetMethod(nameof(SetTenantFilter), BindingFlags.Instance | BindingFlags.NonPublic)!
.MakeGenericMethod(entityType.ClrType);
methodInfo.Invoke(this, new object[] { modelBuilder });
}
}
/// <summary>
/// 为具体实体设置租户过滤器。
/// </summary>
/// <typeparam name="TEntity">实体类型。</typeparam>
/// <param name="modelBuilder">模型构建器。</param>
private void SetTenantFilter<TEntity>(ModelBuilder modelBuilder)
where TEntity : class, IMultiTenantEntity
{
modelBuilder.Entity<TEntity>().HasQueryFilter(entity => entity.TenantId == CurrentTenantId);
}
/// <summary>
/// 为新增实体填充租户 ID。
/// </summary>
private void ApplyTenantMetadata()
{
var tenantId = CurrentTenantId;
foreach (var entry in ChangeTracker.Entries<IMultiTenantEntity>())
{
if (entry.State == EntityState.Added && entry.Entity.TenantId == 0 && tenantId != 0)
{
entry.Entity.TenantId = tenantId;
}
}
}
}