使用自定义[AutoMapper]特性时,如何为属性添加映射条件?
在AutoMapper中保留特性映射同时添加属性级映射条件
完全懂你的痛点——用特性做映射确实省心,但碰到要给特定属性加动态条件时,直接用ForMember好像没效果,尤其是源和目标类型还带点动态性的时候。下面给你几个落地可行的方案:
方案1:自定义条件特性+全局映射拦截
你可以扩展AutoMapper的特性映射逻辑,自己定义一个带条件的特性,然后在配置时批量处理所有标记了该特性的属性:
- 先写一个自定义条件特性:
[AttributeUsage(AttributeTargets.Property)] public class MapWithConditionAttribute : Attribute { public Func<ResolutionContext, bool> Condition { get; } // 通过反射加载静态条件方法,适配动态场景 public MapWithConditionAttribute(Type conditionHandlerType, string methodName) { var method = conditionHandlerType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static); Condition = (Func<ResolutionContext, bool>)Delegate.CreateDelegate(typeof(Func<ResolutionContext, bool>), method); } }
- 然后在AutoMapper配置中注册这个特性的处理逻辑:
var config = new MapperConfiguration(cfg => { // 先启用默认的特性映射 cfg.AddMaps(typeof(YourEntity).Assembly); // 遍历所有已注册的映射,给带自定义特性的属性加条件 foreach (var typeMap in cfg.GetAllTypeMaps()) { foreach (var propMap in typeMap.PropertyMaps) { var conditionAttr = propMap.DestinationProperty.GetCustomAttribute<MapWithConditionAttribute>(); if (conditionAttr != null) { propMap.Condition = conditionAttr.Condition; } } } });
- 最后在目标模型的属性上标记这个特性:
public class TargetModel { public string Name { get; set; } // 指定条件处理类和方法 [MapWithCondition(typeof(PropertyConditions), nameof(PropertyConditions.ShouldMapAge))] public int Age { get; set; } } // 单独的条件处理类,方便复用和维护 public static class PropertyConditions { public static bool ShouldMapAge(ResolutionContext context) { // 这里写你的动态条件逻辑,比如只有源Age大于18才映射 return (int)context.SourceValue > 18; } }
方案2:全局属性映射拦截(无需自定义特性)
如果不想额外加特性,也可以直接通过AutoMapper的全局拦截器,根据属性的元数据(名称、类型、已有特性)来动态添加条件:
var config = new MapperConfiguration(cfg => { cfg.AddMaps(typeof(YourEntity).Assembly); // 遍历所有属性映射,按需添加条件 cfg.ForAllPropertyMaps((propMap, context) => { // 示例1:针对名为"Age"的属性加条件 if (propMap.DestinationProperty.Name.Equals("Age", StringComparison.OrdinalIgnoreCase)) { propMap.Condition = rc => (int)rc.SourceValue > 18; } // 示例2:针对标记了特定内置特性的属性加条件 else if (propMap.SourceProperty.GetCustomAttribute<RequiredAttribute>() != null) { propMap.Condition = rc => rc.SourceValue != null; } }); });
这个方案特别适合源/目标类型动态的场景——你不需要提前知道具体类型,只要通过属性的通用特征来匹配即可。
方案3:在Profile中结合特性映射和条件补充
如果你的映射是按Profile组织的,可以先启用特性映射,再针对特定属性补充条件,注意顺序很重要:
public class YourMappingProfile : Profile { public YourMappingProfile() { // 先加载特性映射配置 ApplyConfigurationFromAttributes(typeof(YourEntity)); // 针对特定属性补充条件,动态类型可以用字符串指定属性名 CreateMap(typeof(SourceEntity), typeof(TargetModel)) .ForMember("Age", opt => opt.Condition(rc => (int)rc.SourceValue > 18)); } }
为什么直接用ForMember没生效?
大概率是配置顺序搞反了:如果你先调用CreateMap再启用特性映射,AutoMapper会用特性配置覆盖掉之前的ForMember设置。正确顺序是先加载特性映射,再补充自定义条件。
内容的提问来源于stack exchange,提问作者Mouna Ben Ayed
相关产品推荐
相关产品推荐

