如何在AutoMapper中基于UserId实现带权限的文档收藏状态映射
解决AutoMapper动态映射IsFavourited字段的问题
核心需求
- 角色1用户:返回的
DocumentDto中IsFavourited字段为null - 角色2用户:返回的
DocumentDto中IsFavourited字段根据该用户是否收藏文档返回true/false - 不能过滤文档数据,仅动态调整字段值
可行方案
方案1:利用AutoMapper上下文参数传递用户信息
这种方式兼容内存映射和EF Core投影,且无需修改数据库设计。
1.1 配置映射规则
public class DocumentProfile : Profile { public DocumentProfile() { CreateMap<Document, DocumentDto>() .ForMember(dest => dest.IsFavourited, opt => opt.MapFrom((src, dest, _, context) => { // 从上下文获取用户信息 var userId = context.Items.TryGetValue("UserId", out var userIdObj) ? (long)userIdObj : (long?)null; var isRole2 = context.Items.TryGetValue("IsRole2", out var roleObj) ? (bool)roleObj : false; // 根据角色和用户ID判断返回值 if (!isRole2 || userId == null) return null; // EF Core会自动翻译为Exists查询,无需Include所有Favourites return src.Favourites.Any(f => f.UserId == userId); })); } }
1.2 在CQRS处理器中传递用户信息
// 假设已通过权限校验获取当前用户ID和角色 long currentUserId = ...; bool isRole2 = ...; // 内存映射场景(已从数据库加载Document数据) var documents = await _context.Documents.ToListAsync(); var dtos = _mapper.Map<List<DocumentDto>>(documents, opt => { opt.Items["UserId"] = currentUserId; opt.Items["IsRole2"] = isRole2; }); // EF Core投影场景(直接在数据库层面完成映射,性能更优) var dtos = await _context.Documents .ProjectTo<DocumentDto>(_mapper.ConfigurationProvider, new { UserId = currentUserId, IsRole2 = isRole2 }) .ToListAsync();
方案2:注入当前用户服务到AutoMapper Profile
如果项目中已有获取当前用户的服务(如ICurrentUserService),可直接注入到Profile中:
2.1 定义当前用户服务
public interface ICurrentUserService { long? UserId { get; } bool IsRole2 { get; } } // 实现类(可通过IHttpContextAccessor获取请求上下文信息) public class CurrentUserService : ICurrentUserService { private readonly IHttpContextAccessor _httpContextAccessor; public CurrentUserService(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public long? UserId => long.TryParse(_httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier), out var id) ? id : null; public bool IsRole2 => _httpContextAccessor.HttpContext?.User.IsInRole("Role2") ?? false; }
2.2 配置映射规则
public class DocumentProfile : Profile { public DocumentProfile(ICurrentUserService currentUserService) { CreateMap<Document, DocumentDto>() .ForMember(dest => dest.IsFavourited, opt => opt.MapFrom(src => { if (!currentUserService.IsRole2 || currentUserService.UserId == null) return null; return src.Favourites.Any(f => f.UserId == currentUserService.UserId); })); } }
2.3 在CQRS处理器中直接映射
// 内存映射 var documents = await _context.Documents.ToListAsync(); var dtos = _mapper.Map<List<DocumentDto>>(documents); // EF Core投影 var dtos = await _context.Documents .ProjectTo<DocumentDto>(_mapper.ConfigurationProvider) .ToListAsync();
关键说明
- 数据库设计无问题:一对多的收藏关系是合理的,无需调整
- 避免加载无关数据:使用
Any()代替Include(Favourites),EF会自动生成高效的Exists查询,不会加载其他用户的收藏记录 - 兼容现有业务:无需修改分页、过滤器等逻辑,仅在映射层动态处理字段值
内容的提问来源于stack exchange,提问作者Radek L
相关产品推荐
相关产品推荐

