7

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

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

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

对于两个类

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 文件中,如

<ObjectMapping>
<mapping>
    <src>Title</src>
    <dest>Tile</dest>
</mapping>
<mapping>
    <src>EventDate.Date</src>
    <dest>EventDate</dest>
</mapping>
<mapping>
    <src>EventDate.Hour</src>
    <dest>EventHour</dest>
</mapping>
<mapping>
    <src>EventDate.Minute</src>
    <dest>EventMinute</dest>
</mapping>

并由此影响地图的创建(XML 不是必需的,也可以是其他一切)。为简单起见说类型没有问题,所以 src 和 dest 应该是相同的,否则失败是可以的。这背后的想法是在应该映射什么以及应该映射到哪里时非常灵活。我正在考虑反射以根据其名称获取属性值,但这似乎行不通。我也不确定这是否有意义,或者我是否遗漏了一些重要的东西,因此感谢您的帮助和想法。

4

3 回答 3

8

最后,我自己实现了最初的需求,尽管我并不需要它(需求改变了)。我将在此处提供代码以防有人需要它(或多或少作为概念证明,因为仍然可以进行很多改进)或者对 XML 容易出错感兴趣,请记住,这种方法中的关键组件,属性名称和类型必须完全匹配,否则它将无法工作,但是使用一点 GUI 来编辑文件应该是可以实现的(我的意思是不手动编辑文件)。

我使用了herehere的代码,并添加了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<PropertyMapping> myMappings = new List<PropertyMapping>();

    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<PropertyMapping> GenerateMappingObjectsFromXml(string xmlFile)
    {
        XElement definedMappings = XElement.Load(xmlFile);
        List<PropertyMapping> mappings = new List<PropertyMapping>();

        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 配置:

<?xml version="1.0" encoding="utf-8" ?>
    <ObjectMapping>
      <mapping>
        <src>Title</src>
        <dest>Title</dest>
       </mapping>
      <mapping>
        <src>EventDate.Date</src>
        <dest>EventDate</dest>
      </mapping>
      <mapping>
        <src>EventDate.Hour</src>
        <dest>EventHour</dest>
      </mapping>
      <mapping>
        <src>EventDate.Minute</src>
        <dest>EventMinute</dest>
      </mapping>
      <mapping>
        <src>EventDate</src>
        <dest>Foo.Bar.InternalDate</dest>
      </mapping>
     </ObjectMapping>
于 2013-10-08T07:50:16.947 回答
3

你不想做你所要求的。就像@Gruff Bunny 说的那样,automapper 已经有了一个Profile基本上可以完成您正在寻找的所有配置的类。

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

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

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

第三,这样做会减慢应用程序的启动时间。CreateMap直接从 CLR 代码进行调用比解析文本以进行映射要快得多。

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

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

public class CalendarEventProfile : AutoMapper.Profile
{
    public override void Configure()
    {
        CreateMap<CalendarEvent, CalendarEventForm>()
            //.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 配置不是。因此调试、测试和维护要容易得多。

于 2013-10-07T11:29:35.253 回答
1

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

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

于 2017-03-26T05:49:18.800 回答