仓储模式下如何仅筛选所需数据值?
解决方案:在分层架构中实现按需列查询(避免IQueryable暴露)
针对你提到的痛点——视图模型位于应用层、基础设施层不能直接依赖应用层,同时要实现仅查询构建视图模型所需的列,以下是几种实用的落地方案:
1. 共享契约层定义查询DTO
在领域层或专门的查询契约层(独立于应用/基础设施层)定义仅包含所需属性的DTO,作为两层之间的数据传输契约。基础设施层基于该DTO编写查询(仅选择需要的列),再返回给应用层,由应用层将DTO映射为视图模型。
示例代码:
契约层DTO
// 位于Domain/QueryContracts目录下 public class UserBasicInfoDto { public Guid Id { get; set; } public string Username { get; set; } public string Email { get; set; } }
基础设施层查询实现
public async Task<UserBasicInfoDto> GetUserBasicInfoAsync(Guid userId) { return await _dbContext.Users .Where(u => u.Id == userId) .Select(u => new UserBasicInfoDto { Id = u.Id, Username = u.Username, Email = u.Email }) .FirstOrDefaultAsync(); }
应用层映射为视图模型
var userDto = await _userRepository.GetUserBasicInfoAsync(userId); var viewModel = new UserProfileViewModel { UserId = userDto.Id, DisplayName = userDto.Username, ContactEmail = userDto.Email };
2. 使用查询规范模式
应用层定义包含查询条件和属性选择逻辑的查询规范,基础设施层基于规范执行查询。这种方式通过表达式树传递选择逻辑,既隔离了层依赖,又能精准控制查询的列。
示例代码:
应用层查询规范
public class UserProfileSpecification : QuerySpecification<User, UserProfileViewModel> { public UserProfileSpecification(Guid userId) { // 查询条件 Criteria = u => u.Id == userId; // 投影到视图模型的逻辑 Selector = u => new UserProfileViewModel { UserId = u.Id, DisplayName = u.Username, ContactEmail = u.Email }; } } // 通用规范基类 public abstract class QuerySpecification<TEntity, TResult> where TEntity : class { public Expression<Func<TEntity, bool>> Criteria { get; protected set; } public Expression<Func<TEntity, TResult>> Selector { get; protected set; } }
基础设施层通用查询处理
public async Task<TResult> FindBySpecificationAsync<TEntity, TResult>(QuerySpecification<TEntity, TResult> spec) where TEntity : class { return await _dbContext.Set<TEntity>() .Where(spec.Criteria) .Select(spec.Selector) .FirstOrDefaultAsync(); }
3. 借助AutoMapper的ProjectTo实现投影
如果项目使用AutoMapper,可以在应用层配置领域实体到视图模型的映射规则,基础设施层通过ProjectTo方法直接执行投影查询。这种方式通过泛型+IMapper接口实现解耦,避免基础设施层直接依赖应用层视图模型。
示例代码:
应用层映射配置
public class MappingProfile : Profile { public MappingProfile() { CreateMap<User, UserProfileViewModel>() .ForMember(dest => dest.UserId, opt => opt.MapFrom(src => src.Id)) .ForMember(dest => dest.DisplayName, opt => opt.MapFrom(src => src.Username)) .ForMember(dest => dest.ContactEmail, opt => opt.MapFrom(src => src.Email)); } }
基础设施层通用投影查询
public async Task<TViewModel> GetProjectedAsync<TEntity, TViewModel>(Expression<Func<TEntity, bool>> criteria, IMapper mapper) where TEntity : class { return await _dbContext.Set<TEntity>() .Where(criteria) .ProjectTo<TViewModel>(mapper.ConfigurationProvider) .FirstOrDefaultAsync(); }
应用层调用
var viewModel = await _userRepository.GetProjectedAsync<User, UserProfileViewModel>( u => u.Id == userId, _mapper );
内容的提问来源于stack exchange,提问作者arakakivl
相关产品推荐
相关产品推荐

