Automapper升级:DateTime转int映射与投影区分实现问题
问题描述
我正在将.NET Framework项目从AutoMapper 4.1.1升级至10.0.0,遇到了一个棘手的问题。AutoMapper 10里ConvertUsing和ProjectUsing已经合并,但我需要区分普通内存映射和IQueryable投影两种场景:
- 当映射是IQueryable投影时,DateTime转int需要用
SqlFunctions计算分钟数(因为要生成SQL语句) - 当是普通内存对象映射时,直接用
TimeOfDay.TotalMinutes做简单转换就行
但现在每个映射只能配置一个ConvertUsing,之前尝试用旧的ProjectUsing或者在ConvertUsing里判断是否是IQueryable场景都没用。
原4.1.1版本的配置代码如下:
CreateMap<DateTime?, int>().ConvertUsing(x => x.HasValue ? System.Data.Entity.SqlServer.SqlFunctions.DatePart("HOUR", x.Value).Value * 60 + System.Data.Entity.SqlServer.SqlFunctions.DatePart("MINUTE", x.Value).Value : 0); CreateMap<DateTime, int>().ConvertUsing(x => System.Data.Entity.SqlServer.SqlFunctions.DatePart("HOUR", x).Value * 60 + System.Data.Entity.SqlServer.SqlFunctions.DatePart("MINUTE", x).Value); CreateMap<DateTime?, int>().ConvertUsing(x => Convert.ToInt32(x.HasValue ? x.Value.TimeOfDay.TotalMinutes : 0)); CreateMap<DateTime, int>().ConvertUsing(x => Convert.ToInt32(x.TimeOfDay.TotalMinutes));
解决方案
在AutoMapper 10+中,可以通过以下两种方式实现区分场景的映射:
方法1:利用映射上下文判断场景
使用ConvertUsing的重载,接收ResolutionContext参数,通过context.IsProjection属性判断当前是否处于投影场景:
// 可空DateTime转int CreateMap<DateTime?, int>().ConvertUsing((src, dest, context) => { if (context.IsProjection) { return src.HasValue ? System.Data.Entity.SqlServer.SqlFunctions.DatePart("HOUR", src.Value).Value * 60 + System.Data.Entity.SqlServer.SqlFunctions.DatePart("MINUTE", src.Value).Value : 0; } else { return Convert.ToInt32(src.HasValue ? src.Value.TimeOfDay.TotalMinutes : 0); } }); // 非空DateTime转int CreateMap<DateTime, int>().ConvertUsing((src, dest, context) => { if (context.IsProjection) { return System.Data.Entity.SqlServer.SqlFunctions.DatePart("HOUR", src).Value * 60 + System.Data.Entity.SqlServer.SqlFunctions.DatePart("MINUTE", src).Value; } else { return Convert.ToInt32(src.TimeOfDay.TotalMinutes); } });
方法2:分离投影与内存映射配置
先配置基础的内存映射逻辑,再为投影场景单独覆盖映射规则:
// 基础内存映射配置 CreateMap<DateTime?, int>().ConvertUsing(x => Convert.ToInt32(x.HasValue ? x.Value.TimeOfDay.TotalMinutes : 0)); CreateMap<DateTime, int>().ConvertUsing(x => Convert.ToInt32(x.TimeOfDay.TotalMinutes)); // 为IQueryable投影场景覆盖逻辑 CreateMap<DateTime?, int>() .ForMember(dest => dest, opt => opt.MapFrom(src => src.HasValue ? System.Data.Entity.SqlServer.SqlFunctions.DatePart("HOUR", src.Value).Value * 60 + System.Data.Entity.SqlServer.SqlFunctions.DatePart("MINUTE", src.Value).Value : 0)); CreateMap<DateTime, int>() .ForMember(dest => dest, opt => opt.MapFrom(src => System.Data.Entity.SqlServer.SqlFunctions.DatePart("HOUR", src).Value * 60 + System.Data.Entity.SqlServer.SqlFunctions.DatePart("MINUTE", src).Value));
AutoMapper处理IQueryable投影时会优先解析MapFrom的表达式树生成SQL,内存映射则使用ConvertUsing的逻辑。
注意事项
- 确保AutoMapper版本为10.0.0及以上,
IsProjection属性在此版本已可用 - 使用
SqlFunctions时,需确保项目引用了EntityFramework相关包,且EF能正确解析函数生成SQL
内容的提问来源于stack exchange,提问作者Alberto Borja
相关产品推荐
相关产品推荐

