使用AutoMapper自定义映射

我有两个非常简单的对象:

public class CategoryDto { public string Id { get; set; } public string MyValueProperty { get; set; } } public class Category { public string Id { get; set; } [MapTo("MyValueProperty")] public string Key { get; set; } } 

使用AutoMapper将CategoryDto映射到CategoryDto时,我想要以下行为:

除了具有MapTo属性的属性之外,应该像往常一样映射属性。 在这种情况下,我必须读取Attribute的值来查找target属性。 source属性的值用于在destination属性中查找要注入的值(借助字典)。 一个例子总是好于1000字……

例:

 Dictionary keys = new Dictionary { { "MyKey", "MyValue" } }; Category category = new Category(); category.Id = "3"; category.Key = "MyKey"; CategoryDto result = Map(category); result.Id // Expected : "3" result.MyValueProperty // Expected : "MyValue" 

Key属性映射到MyValueProperty (通过MapTo属性),分配的值是“MyValue”,因为source属性值是“MyKey”,它被映射(通过字典)到“MyValue”。

这可以使用AutoMapper吗? 我当然需要一个适用于每个对象的解决方案,而不仅仅是Category / CategoryDto。

我终于(经过这么多小时!!!!)找到了解决方案。 我和社区分享这个; 希望它能帮助别人……

编辑:注意它现在更简单(AutoMapper 5.0+),你可以像我在这篇文章中回答的那样: 如何使AutoMapper根据MaxLength属性截断字符串?

 public static class Extensions { public static IMappingExpression MapTo(this IMappingExpression expression) { Type sourceType = typeof(TSource); Type destinationType = typeof(TDestination); TypeMap existingMaps = Mapper.GetAllTypeMaps().First(b => b.SourceType == sourceType && b.DestinationType == destinationType); string[] missingMappings = existingMaps.GetUnmappedPropertyNames(); if (missingMappings.Any()) { PropertyInfo[] sourceProperties = sourceType.GetProperties(); foreach (string property in missingMappings) { foreach (PropertyInfo propertyInfo in sourceProperties) { MapToAttribute attr = propertyInfo.GetCustomAttribute(); if (attr != null && attr.Name == property) { expression.ForMember(property, opt => opt.ResolveUsing(new MyValueResolve(propertyInfo))); } } } } return expression; } } public class MyValueResolve : IValueResolver { private readonly PropertyInfo pInfo = null; public MyValueResolve(PropertyInfo pInfo) { this.pInfo = pInfo; } public ResolutionResult Resolve(ResolutionResult source) { string key = pInfo.GetValue(source.Value) as string; string value = dictonary[key]; return source.New(value); } } 

使用IValueResolver和ResolveUsing()方法的实现应该相当简单。 你基本上只需要一个获取属性信息的解析器的构造函数(或者如果你想要接受一个lambda表达式并解析类似于如何获取特定属性的PropertyInfo的属性信息? 。虽然我没有自己测试我想象以下会工作:

 public class PropertyBasedResolver : IValueResolver { public PropertyInfo Property { get; set; } public PropertyBasedResolver(PropertyInfo property) { this.Property = property; } public ResolutionResult Resolve(ResolutionResult source) { var result = GetValueFromKey(property, source.Value); // gets from some static cache or dictionary elsewhere in your code by reading the prop info and then using that to look up the value based on the key as appropriate return source.New(result) } } 

然后设置您需要执行的映射:

 AutoMapper.Mapper.CreateMap() .ForMember( dest => dest.Value, opt => opt.ResolveUsing( src => new PropertyBasedResolver(typeof(Category.Key) as PropertyInfo).Resolve(src))); 

当然这是一个非常糟糕的lamda,我建议你通过让属性解析器根据属性/属性信息确定它应该查看的源对象的属性来清理它,这样你就可以传递一个干净的新PropertyBasedResolver(进入ResolveUsing(),但希望这足以说明你能够走上正轨。

让我们假设我有以下课程

 public class foo { public string Value; } public class bar { public string Value1; public string Value2; } 

您可以将lambda传递给ResolveUsing:

 .ForMember(f => f.Value, o => o.ResolveUsing(b => { if (b.Value1.StartsWith("A"));) { return b.Value1; } return b.Value2; } ));