fix: 统一逐租户上下文执行

This commit is contained in:
root
2026-01-30 01:04:51 +00:00
parent 41cfd2e2e8
commit cf697b3889
29 changed files with 1037 additions and 490 deletions

View File

@@ -16,7 +16,6 @@ public sealed class DictionaryGroupRepository(DictionaryDbContext context) : IDi
EF.CompileAsyncQuery((DictionaryDbContext db, long tenantId, DictionaryCode code) =>
db.DictionaryGroups
.AsNoTracking()
.IgnoreQueryFilters()
.FirstOrDefault(group => group.TenantId == tenantId && group.DeletedAt == null && group.Code == code));
/// <summary>
@@ -25,7 +24,7 @@ public sealed class DictionaryGroupRepository(DictionaryDbContext context) : IDi
public Task<DictionaryGroup?> GetByIdAsync(long groupId, CancellationToken cancellationToken = default)
{
return context.DictionaryGroups
.IgnoreQueryFilters()
.AsNoTracking()
.FirstOrDefaultAsync(group => group.Id == groupId && group.DeletedAt == null, cancellationToken);
}
@@ -90,7 +89,6 @@ public sealed class DictionaryGroupRepository(DictionaryDbContext context) : IDi
return await context.DictionaryGroups
.AsNoTracking()
.IgnoreQueryFilters()
.Where(group => ids.Contains(group.Id) && group.DeletedAt == null)
.ToListAsync(cancellationToken);
}
@@ -144,7 +142,6 @@ public sealed class DictionaryGroupRepository(DictionaryDbContext context) : IDi
{
var query = context.DictionaryGroups
.AsNoTracking()
.IgnoreQueryFilters()
.Where(group => group.TenantId == tenantId && group.DeletedAt == null);
if (scope.HasValue)

View File

@@ -3,19 +3,19 @@ using Microsoft.EntityFrameworkCore;
using TakeoutSaaS.Domain.Dictionary.Entities;
using TakeoutSaaS.Domain.Dictionary.Repositories;
using TakeoutSaaS.Infrastructure.Dictionary.Persistence;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Infrastructure.Dictionary.Repositories;
/// <summary>
/// 字典项仓储实现。
/// </summary>
public sealed class DictionaryItemRepository(DictionaryDbContext context) : IDictionaryItemRepository
public sealed class DictionaryItemRepository(DictionaryDbContext context, ITenantContextAccessor tenantContextAccessor) : IDictionaryItemRepository
{
private static readonly Func<DictionaryDbContext, long, long, IEnumerable<DictionaryItem>> GetByGroupQuery =
EF.CompileQuery((DictionaryDbContext db, long tenantId, long groupId) =>
(IEnumerable<DictionaryItem>)db.DictionaryItems
.AsNoTracking()
.IgnoreQueryFilters()
.Where(item => item.GroupId == groupId && item.TenantId == tenantId && item.DeletedAt == null)
.OrderBy(item => item.SortOrder));
@@ -25,7 +25,7 @@ public sealed class DictionaryItemRepository(DictionaryDbContext context) : IDic
public Task<DictionaryItem?> GetByIdAsync(long itemId, CancellationToken cancellationToken = default)
{
return context.DictionaryItems
.IgnoreQueryFilters()
.AsNoTracking()
.FirstOrDefaultAsync(item => item.Id == itemId && item.DeletedAt == null, cancellationToken);
}
@@ -51,25 +51,26 @@ public sealed class DictionaryItemRepository(DictionaryDbContext context) : IDic
bool includeOverrides,
CancellationToken cancellationToken = default)
{
var systemGroup = await context.DictionaryGroups
.AsNoTracking()
.IgnoreQueryFilters()
.FirstOrDefaultAsync(group => group.Id == systemGroupId && group.DeletedAt == null, cancellationToken);
if (systemGroup == null)
DictionaryGroup? systemGroup;
List<DictionaryItem> systemItems;
using (tenantContextAccessor.EnterTenantScope(0, "dictionary"))
{
return Array.Empty<DictionaryItem>();
systemGroup = await context.DictionaryGroups
.AsNoTracking()
.FirstOrDefaultAsync(group => group.Id == systemGroupId && group.DeletedAt == null, cancellationToken);
if (systemGroup == null)
{
return Array.Empty<DictionaryItem>();
}
systemItems = await context.DictionaryItems
.AsNoTracking()
.Where(item => item.GroupId == systemGroupId && item.DeletedAt == null)
.OrderBy(item => item.SortOrder)
.ToListAsync(cancellationToken);
}
var result = new List<DictionaryItem>();
var systemItems = await context.DictionaryItems
.AsNoTracking()
.IgnoreQueryFilters()
.Where(item => item.GroupId == systemGroupId && item.TenantId == 0 && item.DeletedAt == null)
.OrderBy(item => item.SortOrder)
.ToListAsync(cancellationToken);
result.AddRange(systemItems);
var result = new List<DictionaryItem>(systemItems);
if (!includeOverrides || tenantId == 0)
{
@@ -78,7 +79,6 @@ public sealed class DictionaryItemRepository(DictionaryDbContext context) : IDic
var tenantGroup = await context.DictionaryGroups
.AsNoTracking()
.IgnoreQueryFilters()
.FirstOrDefaultAsync(group =>
group.TenantId == tenantId &&
group.DeletedAt == null &&
@@ -92,7 +92,6 @@ public sealed class DictionaryItemRepository(DictionaryDbContext context) : IDic
var tenantItems = await context.DictionaryItems
.AsNoTracking()
.IgnoreQueryFilters()
.Where(item => item.GroupId == tenantGroup.Id && item.TenantId == tenantId && item.DeletedAt == null)
.OrderBy(item => item.SortOrder)
.ToListAsync(cancellationToken);

View File

@@ -17,7 +17,6 @@ public sealed class DictionaryLabelOverrideRepository(DictionaryDbContext contex
public Task<DictionaryLabelOverride?> GetByIdAsync(long id, CancellationToken cancellationToken = default)
{
return context.DictionaryLabelOverrides
.IgnoreQueryFilters()
.Include(x => x.DictionaryItem)
.FirstOrDefaultAsync(x => x.Id == id && x.DeletedAt == null, cancellationToken);
}
@@ -28,7 +27,6 @@ public sealed class DictionaryLabelOverrideRepository(DictionaryDbContext contex
public Task<DictionaryLabelOverride?> GetByItemIdAsync(long tenantId, long dictionaryItemId, CancellationToken cancellationToken = default)
{
return context.DictionaryLabelOverrides
.IgnoreQueryFilters()
.FirstOrDefaultAsync(x =>
x.TenantId == tenantId &&
x.DictionaryItemId == dictionaryItemId &&
@@ -46,7 +44,6 @@ public sealed class DictionaryLabelOverrideRepository(DictionaryDbContext contex
{
var query = context.DictionaryLabelOverrides
.AsNoTracking()
.IgnoreQueryFilters()
.Include(x => x.DictionaryItem)
.Where(x => x.TenantId == tenantId && x.DeletedAt == null);
@@ -71,7 +68,6 @@ public sealed class DictionaryLabelOverrideRepository(DictionaryDbContext contex
return await context.DictionaryLabelOverrides
.AsNoTracking()
.IgnoreQueryFilters()
.Where(x =>
x.TenantId == tenantId &&
ids.Contains(x.DictionaryItemId) &&

View File

@@ -4,13 +4,14 @@ using TakeoutSaaS.Domain.Dictionary.Enums;
using TakeoutSaaS.Domain.Dictionary.ValueObjects;
using TakeoutSaaS.Domain.Dictionary.Repositories;
using TakeoutSaaS.Infrastructure.Dictionary.Persistence;
using TakeoutSaaS.Shared.Abstractions.Tenancy;
namespace TakeoutSaaS.Infrastructure.Dictionary.Repositories;
/// <summary>
/// EF Core 字典仓储实现。
/// </summary>
public sealed class EfDictionaryRepository(DictionaryDbContext context) : IDictionaryRepository
public sealed class EfDictionaryRepository(DictionaryDbContext context, ITenantContextAccessor tenantContextAccessor) : IDictionaryRepository
{
/// <summary>
/// 根据分组 ID 查询分组。
@@ -163,19 +164,37 @@ public sealed class EfDictionaryRepository(DictionaryDbContext context) : IDicti
return Array.Empty<DictionaryItem>();
}
// 2. 构建查询并忽略 QueryFilter
var query = context.DictionaryItems
// 2. 查询当前租户条目
var tenantItems = await context.DictionaryItems
.AsNoTracking()
.IgnoreQueryFilters()
.Include(item => item.Group)
.Where(item => normalizedCodes.Contains(item.Group!.Code) && item.DeletedAt == null);
// 3. 按租户或系统级过滤
query = query.Where(item => item.TenantId == tenantId || (includeSystem && item.TenantId == 0));
// 4. 排序返回
return await query
.Where(item => item.TenantId == tenantId && normalizedCodes.Contains(item.Group!.Code) && item.DeletedAt == null)
.OrderBy(item => item.SortOrder)
.ToListAsync(cancellationToken);
if (!includeSystem)
{
return tenantItems;
}
// 3. (空行后) 查询系统级条目TenantId=0
List<DictionaryItem> systemItems;
using (tenantContextAccessor.EnterTenantScope(0, "dictionary"))
{
systemItems = await context.DictionaryItems
.AsNoTracking()
.Include(item => item.Group)
.Where(item => item.TenantId == 0 && normalizedCodes.Contains(item.Group!.Code) && item.DeletedAt == null)
.OrderBy(item => item.SortOrder)
.ToListAsync(cancellationToken);
}
// 4. (空行后) 合并返回(系统优先)
if (systemItems.Count == 0)
{
return tenantItems;
}
return [.. systemItems, .. tenantItems];
}
}

View File

@@ -16,7 +16,6 @@ public sealed class TenantDictionaryOverrideRepository(DictionaryDbContext conte
public Task<TenantDictionaryOverride?> GetAsync(long tenantId, long systemGroupId, CancellationToken cancellationToken = default)
{
return context.TenantDictionaryOverrides
.IgnoreQueryFilters()
.FirstOrDefaultAsync(config =>
config.TenantId == tenantId &&
config.SystemDictionaryGroupId == systemGroupId &&
@@ -31,7 +30,6 @@ public sealed class TenantDictionaryOverrideRepository(DictionaryDbContext conte
{
return await context.TenantDictionaryOverrides
.AsNoTracking()
.IgnoreQueryFilters()
.Where(config => config.TenantId == tenantId && config.DeletedAt == null)
.ToListAsync(cancellationToken);
}