zoukankan      html  css  js  c++  java
  • AutoMapper用法(转载)

    申明

    本文转载自http://www.qeefee.com/article/automapper

    作者:齐飞

    配置AutoMapper映射规则

    AutoMapper是基于约定的,因此在实用映射之前,我们需要先进行映射规则的配置。

     1 public class Source
     2 {
     3     public int SomeValue { get; set; }
     4     public string AnotherValue { get; set; }
     5 }
     6 
     7 public class Destination
     8 {
     9     public int SomeValue { get; set; }
    10 }

     在上面的代码中,我们定义了两个类,我们需要将Source类的对象映射到Destination类的对象上面。要完成这个操作,我们需要对AutoMapper进行如下配置:

    1 Mapper.CreateMap<Source, Destination>();

    进行一下测试:

    1 Source src = new Source() { SomeValue = 1, AnotherValue = "2" };
    2 Destination dest = Mapper.Map<Destination>(src);
    3 ObjectDumper.Write(dest);

    我们可以在控制台看到dest对象的属性值:

    这样我们就完成了一个简单的AutoMapper映射。

    Profile的用法

    Profile提供了一个命名的映射类,所有继承自Profile类的子类都是一个映射集合。

    我们来看一下Profile的用法,这个例子中仍然使用上面的Source类和Destination类。

    1 public class SourceProfile : Profile
    2 {
    3     protected override void Configure()
    4     {
    5         CreateMap<Source, Destination>();
    6     }
    7 }

    我们可以再Profile中重写Configure方法,从而完成映射规则的配置。从Profile初始化Mapper规则:

    1 Mapper.Initialize(x => x.AddProfile<SourceProfile>());

    在一个Profile中,我们可以完成多个、更复杂的规则的约定:

     1 public class Destination2
     2 {
     3     public int SomeValue { get; set; }
     4     public string AnotherValue2 { get; set; }
     5 }
     6 
     7 public class SourceProfile : Profile
     8 {
     9     protected override void Configure()
    10     {
    11         //Source->Destination
    12         CreateMap<Source, Destination>();
    13 
    14         //Source->Destination2
    15         CreateMap<Source, Destination2>().ForMember(d => d.AnotherValue2, opt =>
    16         {
    17             opt.MapFrom(s => s.AnotherValue);
    18         });
    19     }
    20 }

    AutoMapper最佳实践

    这段内容将讨论AutoMapper的规则写在什么地方的问题。

    在上一段中,我们已经知道了如何使用AutoMapper进行简单的对象映射,但是,在实际的项目中,我们会有很多类进行映射(从Entity转换为Dto,或者从Entity转换为ViewModel等),这么多的映射如何组织将成为一个问题。

    首先我们需要定义一个Configuration.cs的类,该类提供AutoMapper规则配置的入口,它只提供一个静态的方法,在程序第一次运行的时候调用该方法完成配置。

    当有多个Profile的时候,我们可以这样添加:

     1 public class Configuration
     2 {
     3     public static void Configure()
     4     {
     5         Mapper.Initialize(cfg =>
     6         {
     7             cfg.AddProfile<Profiles.SourceProfile>();
     8             cfg.AddProfile<Profiles.OrderProfile>();
     9             cfg.AddProfile<Profiles.CalendarEventProfile>();
    10         });
    11     }
    12 }

    在程序运行的时候,只需要调用Configure方法即可。

    了解了这些实现以后,我们可以再项目中添加AutoMapper文件夹,文件夹结构如下:

    Configuration为我们的静态配置入口类;Profiles文件夹为我们所有Profile类的文件夹。如果是MVC,我们需要在Global中调用:

    1 AutoMapper.Configuration.Configure();

    扁平化映射(Flattening)

    默认情况下,我们的Source类和Destination类是根据属性名称进行匹配映射的。除此之外,默认的映射规则还有下面两种情况,我们称之为扁平化映射,即当Source类中不包含Destination类中的属性的时候,AutoMapper会将Destination类中的属性进行分割,或匹配“Get”开头的方法,例如:

    Order类:

    1 public class Order
    2 {
    3     public Customer Customer { get; set; }
    4 
    5     public decimal GetTotal()
    6     {
    7         return 100M;
    8     }
    9 }

    Order类中包含了一个customer对象和一个GetTotal方法,为了方便演示,我直接将GetTotal方法返回100;

    Customer类的定义如下:

    1 public class Customer
    2 {
    3     public string Name { get; set; }
    4 }

    OrderDto类的定义如下:

    1 public class OrderDto
    2 {
    3     public string CustomerName { get; set; }
    4     public string Total { get; set; }
    5 }

    我们在进行映射的时候,不需要进行特殊的配置,既可以完成从Order到OrderDto的映射。

    1 public class OrderProfile : Profile
    2 {
    3     protected override void Configure()
    4     {
    5         CreateMap<Entity.Order, Dto.OrderDto>();
    6     }
    7 }

    测试代码:

    1 Entity.Customer customer = new Entity.Customer() { Name = "Tom" };
    2 Entity.Order order = new Entity.Order() { Customer = customer };
    3 Dto.OrderDto orderDto = Mapper.Map<Dto.OrderDto>(order);
    4 ObjectDumper.Write(order, 2);
    5 ObjectDumper.Write(orderDto);

    测试结果:

    指定映射字段(Projection)

    在实际的业务环境中,我们的Source类和Destination类的字段不可能一对一的匹配,这个时候我们就需要来指定他们的实际映射关系,例如:

     1 public class CalendarEvent
     2 {
     3     public DateTime Date { get; set; }
     4     public string Title { get; set; }
     5 }
     6 
     7 public class CalendarEventForm
     8 {
     9     public DateTime EventDate { get; set; }
    10     public int EventHour { get; set; }
    11     public int EventMinute { get; set; }
    12     public string DisplayTitle { get; set; }
    13 }

    在这两个类中,CalendarEvent的Date将被拆分为CalendarEventForm的日期、时、分三个字段,Title也将对应DisplayTitle字段,那么相应的Profile定义如下:

     1 public class CalendarEventProfile : Profile
     2 {
     3     protected override void Configure()
     4     {
     5         CreateMap<Entity.CalendarEvent, Entity.CalendarEventForm>()
     6             .ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date))
     7             .ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.Date.Hour))
     8             .ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.Date.Minute))
     9             .ForMember(dest => dest.DisplayTitle, opt => opt.MapFrom(src => src.Title));
    10     }
    11 }

    测试代码:

    1 Entity.CalendarEvent calendarEvent = new Entity.CalendarEvent()
    2 {
    3     Date = DateTime.Now,
    4     Title = "Demo Event"
    5 };
    6 Entity.CalendarEventForm calendarEventForm = Mapper.Map<Entity.CalendarEventForm>(calendarEvent);
    7 ObjectDumper.Write(calendarEventForm);

    测试结果:

    验证配置项(Configuration Validation)

    AutoMapper提供了一种验证机制,用来判断Destination类中的所有属性是否都被映射,如果存在未被映射的属性,则抛出异常。

    验证的用法:

    1 Mapper.AssertConfigurationIsValid();

    例如:

    1 public class Source
    2 {
    3     public int SomeValue { get; set; }
    4     public string AnotherValue { get; set; }
    5 }

    Destination代码:

    1 public class Destination
    2 {
    3     public int SomeValuefff { get; set; }
    4 }

    测试:

    1 Mapper.CreateMap<Entity.Source, Entity.Destination>();
    2 Mapper.AssertConfigurationIsValid();

    运行程序将会出现AutoMapperConfigurationException异常:

    这是因为SomeValuefff在Source类中没有对应的字段造成的。

    解决这种异常的方法有:

    指定映射字段,例如:

    1 Mapper.CreateMap<Entity.Source, Entity.Destination>()
    2     .ForMember(dest => dest.SomeValuefff, opt =>
    3     {
    4         opt.MapFrom(src => src.SomeValue);
    5     });

    或者使用Ignore方法:

    1 Mapper.CreateMap<Entity.Source, Entity.Destination>()
    2     .ForMember(dest => dest.SomeValuefff, opt =>
    3     {
    4         opt.Ignore();
    5     });

    或者使用自定义解析器,自定义解析器在下面讲到。

    自定义解析器(Custom value resolvers)

    AutoMapper允许我们自定义解析器来完成Source到Destination的值的转换。例如:

     1 public class Source
     2 {
     3     public int Value1 { get; set; }
     4     public int Value2 { get; set; }
     5 }
     6 
     7 public class Destination
     8 {
     9     public int Total { get; set; }
    10 }

    Total属性在Source中不存在,如果现在创建映射规则,在映射的时候必然会抛出异常。这个时候我们就需要使用自定义解析器来完成映射。

    自定义解析器需要实现 IValueResolver 接口,接口的定义如下:

    1 public interface IValueResolver
    2 {
    3     ResolutionResult Resolve(ResolutionResult source);
    4 }

    我们来自定义一个Resolver:

    1 public class CustomResolver : ValueResolver<Source, int>
    2 {
    3     protected override int ResolveCore(Source source)
    4     {
    5         return source.Value1 + source.Value2;
    6     }
    7 }

    然后在映射规则中使用这个解析器:

     1 public class SourceProfile : Profile
     2 {
     3     protected override void Configure()
     4     {
     5         //Source->Destination
     6         CreateMap<Source, Destination>()
     7             .ForMember(dest => dest.Total, opt =>
     8             {
     9                 opt.ResolveUsing<CustomResolver>();
    10             });
    11     }
    12 }

    测试代码:

    1 Source src = new Source()
    2 {
    3     Value1 = 1,
    4     Value2 = 2
    5 };
    6 Destination dest = Mapper.Map<Destination>(src);
    7 ObjectDumper.Write(dest);

    测试结果:

    在使用自定义Resolver中,我们还可以指定Resolver的构造函数,例如:

    1 //Source->Destination
    2 CreateMap<Source, Destination>()
    3     .ForMember(dest => dest.Total, opt =>
    4     {
    5         opt.ResolveUsing<CustomResolver>()
    6             .ConstructedBy(() => new CustomResolver());
    7     });

    自定义类型转换器(Custom type converters)

     AutoMapper通过ConvertUsing来使用自定义类型转换器。ConvertUsing有三种用法:

    1 void ConvertUsing(Func<TSource, TDestination> mappingFunction);
    2 void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
    3 void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;

    当我们有如下的Source类和Destination类:

    1 public class Source
    2 {
    3     public string Value1 { get; set; }
    4 }
    5 
    6 public class Destination
    7 {
    8     public int Value1 { get; set; }
    9 }

    我们可以使用如下配置:

     1 public class SourceProfile : Profile
     2 {
     3     protected override void Configure()
     4     {
     5         //string->int
     6         CreateMap<string, int>()
     7             .ConvertUsing(Convert.ToInt32);
     8         //Source->Destination
     9         CreateMap<Source, Destination>();
    10     }
    11 }

    在上面的配置中,我们首先创建了从string到int的类型转换,这里使用了系统自带的Convert.ToInt32转换方法。

    除了这种方法之外,我们还可以自定义类型转换器:

     1 public class CustomConverter : ITypeConverter<Source, Destination>
     2 {
     3     public Destination Convert(ResolutionContext context)
     4     {
     5         Source src = context.SourceValue as Source;
     6         Destination dest = new Destination();
     7         dest.Value1 = System.Convert.ToInt32(src.Value1);
     8 
     9         return dest;
    10     }
    11 }

    通过这个转换器,我们可以绕过string到int的转换,直接将Source类的对象转换为Destination类的对象。

    对应的配置如下:

    1 public class SourceProfile : Profile
    2 {
    3     protected override void Configure()
    4     {
    5         //Source->Destination
    6         CreateMap<Source, Destination>()
    7             .ConvertUsing<CustomConverter>();
    8     }
    9 }

    或者,我们也可以使用下面的配置:

     1 public class SourceProfile : Profile
     2 {
     3     protected override void Configure()
     4     {
     5         //Source->Destination
     6         CustomConverter converter = new CustomConverter();
     7         CreateMap<Source, Destination>()
     8             .ConvertUsing(converter);
     9     }
    10 }

    空值替换(Null substitution)

    空值替换允许我们将Source对象中的空值在转换为Destination的值的时候,使用指定的值来替换空值。

    1 public class Source
    2 {
    3     public string Value { get; set; }
    4 }
    5 
    6 public class Destination
    7 {
    8     public string Value { get; set; }
    9 }

    配置代码:

     1 public class SourceProfile : Profile
     2 {
     3     protected override void Configure()
     4     {
     5         //Source->Destination
     6         CreateMap<Source, Destination>()
     7             .ForMember(dest => dest.Value, opt =>
     8             {
     9                 opt.NullSubstitute("原始值为NULL");
    10             });
    11     }
    12 }

    测试代码:

    1 Source src = new Source();
    2 Destination dest = Mapper.Map<Destination>(src);
    3 ObjectDumper.Write(dest);

    测试结果:

    条件映射(Conditional mapping)

    条件映射只当Source类中的属性值满足一定条件的时候才进行映射。例如:

    1 public class Foo
    2 {
    3     public int baz;
    4 }
    5 
    6 public class Bar
    7 {
    8     public uint baz;
    9 }

    对应的配置代码如下:

    1 Mapper.CreateMap<Foo, Bar>()
    2     .ForMember(dest => dest.baz, opt =>
    3     {
    4         opt.Condition(src => (src.baz >= 0));
    5     }); 

    转载

    作者:齐飞

    原文:http://www.qeefee.com/article/automapper

  • 相关阅读:
    C语言相关题目6
    C语言相关题目7
    C语言相关题目8
    C语言相关题目9
    哈夫曼编码
    查看linux系统信息命令
    C++基类和派生类的构造函数/析构函数
    C++的const类成员函数
    c++中的static关键字的作用
    为什么二叉树的叶子结点数等于度为2的结点数+1
  • 原文地址:https://www.cnblogs.com/sggx/p/4668234.html
Copyright © 2011-2022 走看看