如何在代码外定义AutoMapper映射,即在XML文件中或使用不同的方法进行完全可配置的对象映射?

编辑:最初我打算使用AutoMapper来实现我的目标,但我必须知道AutoMapper并不打算以这种方式工作。 它为您提供了创建配置文件的可能性,但在我的情况下(完全可配置)我需要为每个参数组合一个配置文件,所以我想出了一个自己的方法,看看答案。

从AutoMapper wiki我学会了创建一个简单的映射

Mapper.CreateMap().ForMember(dest => dest.Title, opt => opt.MapFrom(src => src.Title)); Mapper.CreateMap().ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.EventDate.Date)); Mapper.CreateMap().ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.EventDate.Hour)); Mapper.CreateMap().ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.EventDate.Minute)); 

像两个class

 public class CalendarEvent { public DateTime EventDate; public string Title; } public class CalendarEventForm { public DateTime EventDate { get; set; } public int EventHour { get; set; } public int EventMinute { get; set; } public string Title { get; set; } } 

我现在想知道是否有可能在外部定义映射,例如在XML文件中

   Title Tile   EventDate.Date EventDate   EventDate.Hour EventHour   EventDate.Minute EventMinute  

并通过这种影响创建地图(XML不是一个要求,也可以是其他一切)。 为简单起见,说类型没有问题,所以src和dest应该是相同的,否则可以失败。 这背后的想法是在应该映射的内容和应该映射的位置上非常灵活。 我正在考虑根据其名称获取属性值的reflection,但这似乎不起作用。 我也不确定这是否有意义,或者我是否遗漏了重要的东西,所以我们非常感谢帮助和想法。

最后,我实现了我自己的原始要求,尽管我不需要它(要求已更改)。 我会在这里提供代码以防有人需要它(或多或少作为概念certificate,因为仍然可以进行大量改进)或者有兴趣,请记住,XML容易出错,在这种方法的关键组件,属性名称和类型必须完全匹配,否则它不会工作,但有一点GUI编辑文件这应该是可实现的(我的意思是不手动编辑文件)。

我使用了来自这里和这里的代码,并添加了类PropertyMapping来存储从XML读取的映射,还有类Foo和Bar来创建嵌套数据结构,以便复制到。

无论如何这里是代码,也许它可以帮助某人一些时间:

主要:

 public class Program { public static void Main(string[] args) { // Model var calendarEvent = new CalendarEvent { EventDate = new DateTime(2008, 12, 15, 20, 30, 0), Title = "Company Holiday Party" }; MyObjectMapper mTut = new MyObjectMapper(@"SampleMappings.xml"); Console.WriteLine(string.Format("Result MyMapper: {0}", Program.CompareObjects(calendarEvent, mTut.TestMyObjectMapperProjection(calendarEvent)))); Console.ReadLine(); } public static bool CompareObjects(CalendarEvent calendarEvent, CalendarEventForm form) { return calendarEvent.EventDate.Date.Equals(form.EventDate) && calendarEvent.EventDate.Hour.Equals(form.EventHour) && calendarEvent.EventDate.Minute.Equals(form.EventMinute) && calendarEvent.Title.Equals(form.Title); } } 

映射器实现:

 public class MyObjectMapper { private List myMappings = new List(); public MyObjectMapper(string xmlFile) { this.myMappings = GenerateMappingObjectsFromXml(xmlFile); } /* * Actual mapping; iterate over internal mappings and copy each source value to destination value (types have to be the same) */ public CalendarEventForm TestMyObjectMapperProjection(CalendarEvent calendarEvent) { CalendarEventForm calendarEventForm = new CalendarEventForm(); foreach (PropertyMapping propertyMapping in myMappings) { object originalValue = GetPropValue(calendarEvent,propertyMapping.FromPropertyName); SetPropValue(propertyMapping.ToPropertyName, calendarEventForm, originalValue); } return calendarEventForm; } /* * Get the property value from the source object */ private object GetPropValue(object obj, String compoundProperty) { foreach (String part in compoundProperty.Split('.')) { if (obj == null) { return null; } Type type = obj.GetType(); PropertyInfo info = type.GetProperty(part); if (info == null) { return null; } obj = info.GetValue(obj, null); } return obj; } /* * Set property in the destination object, create new empty objects if needed in case of nested structure */ public void SetPropValue(string compoundProperty, object target, object value) { string[] bits = compoundProperty.Split('.'); for (int i = 0; i < bits.Length - 1; i++) { PropertyInfo propertyToGet = target.GetType().GetProperty(bits[i]); propertyToGet.SetValue(target, Activator.CreateInstance(propertyToGet.PropertyType)); target = propertyToGet.GetValue(target, null); } PropertyInfo propertyToSet = target.GetType().GetProperty(bits.Last()); propertyToSet.SetValue(target, value, null); } /* * Read XML file from the provided file path an create internal mapping objects */ private List GenerateMappingObjectsFromXml(string xmlFile) { XElement definedMappings = XElement.Load(xmlFile); List mappings = new List(); foreach (XElement singleMappingElement in definedMappings.Elements("mapping")) { mappings.Add(new PropertyMapping(singleMappingElement.Element("src").Value, singleMappingElement.Element("dest").Value)); } return mappings; } } 

我的模型类:

 public class CalendarEvent { public DateTime EventDate { get; set; } public string Title { get; set; } } public class CalendarEventForm { public DateTime EventDate { get; set; } public int EventHour { get; set; } public int EventMinute { get; set; } public string Title { get; set; } public Foo Foo { get; set; } } public class Foo { public Bar Bar { get; set; } } public class Bar { public DateTime InternalDate { get; set; } } 

内部映射表示:

 public class PropertyMapping { public string FromPropertyName; public string ToPropertyName; public PropertyMapping(string fromPropertyName, string toPropertyName) { this.FromPropertyName = fromPropertyName; this.ToPropertyName = toPropertyName; } } 

示例XML配置:

    Title Title   EventDate.Date EventDate   EventDate.Hour EventHour   EventDate.Minute EventMinute   EventDate Foo.Bar.InternalDate   

你不想做你想问的事。 就像@Gruff Bunny所说,automapper已经拥有了Profile类,它基本上可以完成你正在寻找的所有配置。

为什么不想用XML(或其他配置)文件执行此操作?

首先,因为您将失去自动播放器配置的强类型性质。 您可以编写代码来解析XML或任何其他类型的文件以读取映射,然后根据文本映射调用CreateMap 。 但是如果你这样做,那么你真的需要对每个配置进行unit testing,以确保在运行时不会抛出任何exception。

其次,你说你想在运行时配置它。 但仅仅更换配置文件是不够的。 为了再次调用CreateMap方法,您需要一个入口点,通常是Web应用程序中的Global.asax 。 因此,在更换配置文件后,您仍然需要回收或重新启动应用程序才能进行新配置。 它不会像替换web.config时那样自动发生。

第三,当您执行此操作时,它会减慢应用程序的启动时间。 CreateMap调用直接从CLR代码发生比解析映射文本要快得多。

如何在没有XML或其他外部文本文件的情况下完成不同的映射配置?

使用AutoMapper.Profile 。 AutoMapper或.NET中没有任何内容表明您必须在与应用程序相同的程序集中声明映射。 您可以在另一个程序集中创建AutoMapper.Profile类,该程序集以强类型方式定义这些映射。 然后,您可以在引导自动化程序时加载这些Profile类。 在我的github帐户中查找AutoAutoMapper库以获取一些帮助您更轻松的帮助程序。

 public class CalendarEventProfile : AutoMapper.Profile { public override void Configure() { CreateMap() //.ForMember(d => d.Title, o => o.MapFrom(s => s.Title)) //redundant, not necessary .ForMember(d => d.EventDate, o => o.MapFrom(s => s.EventDate.Date)) .ForMember(d => d.EventHour, o => o.MapFrom(s => s.EventDate.Hour)) .ForMember(d => d.EventMinute, o => o.MapFrom(s => s.EventDate.Minute)) ; } } 

通过编写这个类,您基本上可以将映射配置外化为将其放入XML文件中的方式。 最大和最有利的区别是这是类型安全的,而XML配置则不是。 因此,调试,测试和维护更容易。

这是我使用Excel文件存储映射的实现(可以是任何源文件)。 如果您需要用户能够修改对象的映射方式,并且可以直观地了解应用中发生的情况,则效果很好。

https://github.com/JimmyOnGitHub/AutoMapper-LoadMappings/tree/master/LoadMappingExample