如何将IQueryable转为Expression<Func>,为NHibernate QueryOver提供投影列表?
NHibernate QueryOver 结合 AutoMapper 实现自动DTO投影方案
直接将Query.ProjectTo得到的IQueryable传入QueryOver的Select方法不可行,因为LINQ Query和QueryOver基于不同的表达式模型,无法直接兼容复用。以下是两种简便的替代方案:
方案一:使用AutoMapper官方QueryOver扩展
AutoMapper提供了专门针对QueryOver的ProjectTo扩展方法,可直接实现和LINQ Query一致的自动投影效果,无需手动编写Select表达式。
示例代码
using(ISession session = SessionFactory.OpenSession()) { // 直接通过QueryOver调用AutoMapper的ProjectTo扩展 IList<MasterDto> listMaster = session.QueryOver<MasterEntity>() .ProjectTo<MasterDto>(autoMapperConfig) .List<MasterDto>(); }
优势
- 完全复用已有的AutoMapper配置,DTO修改时自动同步投影字段,无需手动维护Select列表
- 生成的SQL仅包含DTO所需字段,保证查询性能
- 支持嵌套DTO、自定义映射规则(只要能转换为NHibernate可解析的SQL表达式)
方案二:自定义通用投影工具类
如果无法使用AutoMapper官方扩展(如版本兼容问题),可以基于AutoMapper的映射配置封装工具类,自动生成QueryOver的ProjectionList。
工具类实现
using AutoMapper; using NHibernate.Criterion; using System.Linq.Expressions; public static class QueryOverProjectionHelper { public static IQueryOver<TEntity, TEntity> ProjectToDto<TEntity, TDto>(this IQueryOver<TEntity, TEntity> queryOver, IMapper mapper) { var typeMap = mapper.ConfigurationProvider.FindTypeMapFor<TEntity, TDto>(); var projectionList = Projections.ProjectionList(); foreach (var propertyMap in typeMap.PropertyMaps) { // 构建实体属性的表达式 var entityParam = Expression.Parameter(typeof(TEntity)); var entityProperty = Expression.Property(entityParam, propertyMap.SourceMember.Name); // 添加到投影列表,映射到DTO属性名 projectionList.Add(Projections.Property(entityProperty), propertyMap.DestinationMember.Name); } return queryOver.Select(projectionList); } }
使用示例
using(ISession session = SessionFactory.OpenSession()) { var mapper = autoMapperConfig.CreateMapper(); IList<MasterDto> listMaster = session.QueryOver<MasterEntity>() .ProjectToDto<MasterEntity, MasterDto>(mapper) .List<MasterDto>(); }
注意事项
- 仅支持基础的属性映射,复杂自定义转换(如内存中处理的转换)可能无法生成对应的SQL,需额外处理
- 需确保AutoMapper配置中实体与DTO的属性映射关系正确
内容的提问来源于stack exchange,提问作者PNG
相关产品推荐
相关产品推荐

