AutoMapper是对象到对象的映射工具。在完成映射规则之后,AutoMapper可以将源对象转换为目标对象。

配置AutoMapper映射规则

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

public class Source

{

public int SomeValue { get; set; }

public string AnotherValue { get; set; }

}

public class Destination

{

public int SomeValue { get; set; }

}

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

Mapper.CreateMap();

进行一下测试:

Source src = new Source() { SomeValue = 1, AnotherValue = "2" };

Destination dest = Mapper.Map(src);

ObjectDumper.Write(dest);

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

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

Profile的用法

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

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

public class SourceProfile : Profile

{

protected override void Configure()

{

CreateMap();

}

}

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

Mapper.Initialize(x => x.AddProfile());

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

public class Destination2

{

public int SomeValue { get; set; }

public string AnotherValue2 { get; set; }

}

public class SourceProfile : Profile

{

protected override void Configure()

{

//Source->Destination

CreateMap();

//Source->Destination2

CreateMap().ForMember(d => d.AnotherValue2, opt =>

{

opt.MapFrom(s => s.AnotherValue);

});

}

}

AutoMapper最佳实践

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

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

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

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

public class Configuration

{

public static void Configure()

{

Mapper.Initialize(cfg =>

{

cfg.AddProfile();

cfg.AddProfile();

cfg.AddProfile();

});

}

}

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

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

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

AutoMapper.Configuration.Configure();

扁平化映射(Flattening)

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

Order类:

public class Order

{

public Customer Customer { get; set; }

public decimal GetTotal()

{

return 100M;

}

}

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

Customer类的定义如下:

public class Customer

{

public string Name { get; set; }

}

OrderDto类的定义如下:

public class OrderDto

{

public string CustomerName { get; set; }

public string Total { get; set; }

}

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

public class OrderProfile : Profile

{

protected override void Configure()

{

CreateMap();

}

}

测试代码:

Entity.Customer customer = new Entity.Customer() { Name = "Tom" };

Entity.Order order = new Entity.Order() { Customer = customer };

Dto.OrderDto orderDto = Mapper.Map(order);

ObjectDumper.Write(order, 2);

ObjectDumper.Write(orderDto);

测试结果:

指定映射字段(Projection)

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

public class CalendarEvent

{

public DateTime Date { 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 DisplayTitle { get; set; }

}

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

public class CalendarEventProfile : Profile

{

protected override void Configure()

{

CreateMap()

.ForMember(dest => dest.EventDate, opt => opt.MapFrom(src => src.Date.Date))

.ForMember(dest => dest.EventHour, opt => opt.MapFrom(src => src.Date.Hour))

.ForMember(dest => dest.EventMinute, opt => opt.MapFrom(src => src.Date.Minute))

.ForMember(dest => dest.DisplayTitle, opt => opt.MapFrom(src => src.Title));

}

}

测试代码:

Entity.CalendarEvent calendarEvent = new Entity.CalendarEvent()

{

Date = DateTime.Now,

Title = "Demo Event"

};

Entity.CalendarEventForm calendarEventForm = Mapper.Map(calendarEvent);

ObjectDumper.Write(calendarEventForm);

测试结果:

验证配置项(Configuration Validation)

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

验证的用法:

Mapper.AssertConfigurationIsValid();

例如:

public class Source

{

public int SomeValue { get; set; }

public string AnotherValue { get; set; }

}

Destination代码:

public class Destination

{

public int SomeValuefff { get; set; }

}

测试:

Mapper.CreateMap();

Mapper.AssertConfigurationIsValid();

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

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

解决这种异常的方法有:

指定映射字段,例如:

Mapper.CreateMap()

.ForMember(dest => dest.SomeValuefff, opt =>

{

opt.MapFrom(src => src.SomeValue);

});

或者使用Ignore方法:

Mapper.CreateMap()

.ForMember(dest => dest.SomeValuefff, opt =>

{

opt.Ignore();

});

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

自定义解析器(Custom value resolvers)

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

public class Source

{

public int Value1 { get; set; }

public int Value2 { get; set; }

}

public class Destination

{

public int Total { get; set; }

}

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

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

public interface IValueResolver

{

ResolutionResult Resolve(ResolutionResult source);

}

我们来自定义一个Resolver:

public class CustomResolver : ValueResolver

{

protected override int ResolveCore(Source source)

{

return source.Value1 + source.Value2;

}

}

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

public class SourceProfile : Profile

{

protected override void Configure()

{

//Source->Destination

CreateMap()

.ForMember(dest => dest.Total, opt =>

{

opt.ResolveUsing();

});

}

}

测试代码:

Source src = new Source()

{

Value1 = 1,

Value2 = 2

};

Destination dest = Mapper.Map(src);

ObjectDumper.Write(dest);

测试结果:

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

//Source->Destination

CreateMap()

.ForMember(dest => dest.Total, opt =>

{

opt.ResolveUsing()

.ConstructedBy(() =>

new CustomResolver

());

});

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

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

void ConvertUsing(Func mappingFunction);

void ConvertUsing(ITypeConverter converter);

void ConvertUsing() where TTypeConverter : ITypeConverter;

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

public class Source

{

public string Value1 { get; set; }

}

public class Destination

{

public int Value1 { get; set; }

}

我们可以使用如下配置:

public class SourceProfile : Profile

{

protected override void Configure()

{

//string->int

CreateMap()

.ConvertUsing(Convert.ToInt32);

//Source->Destination

CreateMap();

}

}

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

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

public class CustomConverter : ITypeConverter

{

public Destination Convert(ResolutionContext context)

{

Source src = context.SourceValue as Source;

Destination dest = new Destination();

dest.Value1 = System.Convert.ToInt32(src.Value1);

return dest;

}

}

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

对应的配置如下:

public class SourceProfile : Profile

{

protected override void Configure()

{

//Source->Destination

CreateMap()

.ConvertUsing();

}

}

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

public class SourceProfile : Profile

{

protected override void Configure()

{

//Source->Destination

CustomConverter converter = new CustomConverter();

CreateMap()

.ConvertUsing(converter);

}

}

空值替换(Null substitution)

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

public class Source

{

public string Value { get; set; }

}

public class Destination

{

public string Value { get; set; }

}

配置代码:

public class SourceProfile : Profile

{

protected override void Configure()

{

//Source->Destination

CreateMap()

.ForMember(dest => dest.Value, opt =>

{

opt.NullSubstitute("原始值为NULL");

});

}

}

测试代码:

Source src = new Source();

Destination dest = Mapper.Map(src);

ObjectDumper.Write(dest);

测试结果:

条件映射(Conditional mapping)

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

public class Foo

{

public int baz;

}

public class Bar

{

public uint baz;

}

对应的配置代码如下:

Mapper.CreateMap()

.ForMember(dest => dest.baz, opt =>

{

opt.Condition(src => (src.baz >= 0));

});

java automapper 使用_AutoMapper实际项目运用相关推荐

  1. java automapper 使用_AutoMapper使用

    前言:前篇搭建了下WCF的代码,就提到了DTO的概念,对于为什么要有这么一个DTO的对象,上章可能对于这点不太详尽,在此不厌其烦再来提提它的作用: 从安全上面考虑,领域Model都带有领域业务,让Cl ...

  2. java automapper 使用_AutoMapper 使用总结

    初识AutoMapper 在开始本篇文章之前,先来思考一个问题:一个项目分多层架构,如显示层.业务逻辑层.服务层.数据访问层.层与层访问需要数据载体,也就是类.如果多层通用一个类,一则会暴露出每层的字 ...

  3. java automapper 使用_AutoMapper入门使用

    AutoMapper入门使用 在应用开发的过程中,首先要了解整个系统中各个系统的组件的作用,然后了解系统的工作流(workflow),最后需要梳理一遍数据流(dataflow),而在整理数据流的过程中 ...

  4. 【Java】eclipse如何导入项目

    [Java]eclipse如何导入项目 1.第一步,打开eclipse,点击file->import 2.第二步,选择general->existing projects into wor ...

  5. 20155322 《Java程序设计》课堂实践项目 数据库-3-4

    20155322 <Java程序设计>课堂实践项目 数据库-3-4 数据库-3 实践要求 参考教材代码完成下面的要求,提交能连接到world的截图(有学号水印),并提交代码的码云链接.查询 ...

  6. 《好好学Java 从零基础到项目实战》姗姗而来

    梦里寻她千百度,千呼万唤始出来.从决定开始写这本书,到最终出版上市,数数已经是第三个夏天了.今年疫情有点紧,各行各业都受到影响,多亏出版社各位老师东奔西走,总算排除了万般艰难险阻,这才有了<好好 ...

  7. 【赠书活动】赠送清华社的《好好学Java:从零基础到项目实战》

    零基础学习Java的朋友有福利啦,本博客现联合公众号"老欧说安卓"开展送书活动,下面就是本次活动要赠送的书籍封面 书籍信息: 作者:欧阳燊 出版时间:2020年7月 定价:128元 ...

  8. 视频教程-用Java从零开始开发一个物联网项目-物联网技术

    用Java从零开始开发一个物联网项目 多年的产品设计和开发经验,带领团队完成多个知名产品.历任多家大型公司的Java架构师,对知名框架的源码均有深入研究.拥有IT一线开发.教学10多年的实战经验,能充 ...

  9. 基于JAVA springboot + MYSQL +VUE的项目管理系统(含数据库),包括工时统计、原型预览、效果图管理等

    平台介绍 无鱼工时管理系统,是一款轻量级工时记录和管理工具,包括项目管理,工时上报,工时日报,工时统计等功能. 无鱼工时管理系统可通过员工工时上报的方式,来记录项目所花费的工时,帮助企业进行项目工时统 ...

最新文章

  1. API接口自动化之2 处理http请求的返回体,对返回体做校验
  2. Mac系统下Homebrew的安装和使用Homebrew安装python
  3. JPA与Hibernate的关系
  4. c语言使用位运算符包含头文件,C语言的运算符
  5. 一个优秀数据分析师的准则
  6. 判断回文数 java_java如何判断回文数
  7. NOIP2016换教室 BZOJ 4720
  8. 51单片机按键控制数码管0~9_0.39寸2位数码管厂家
  9. 【MySQL基础 安装】CentOS 7 Yum网络部署 最新官方MySQL5 2020_2_1
  10. 计算机中的根源证书是什么,安全证书过期怎么办
  11. Lightbox JS 用法
  12. java 实现小米商城系统
  13. 标准误和标准差及CV值
  14. caffe ssd 测试demo,检测单张图片
  15. http-invoker插件原理解析
  16. Python: 向量、矩阵和多维数组(基于NumPy库)
  17. SSIS - 1.简介
  18. c语言中整型常量ox,C语言基础教程:数字常量
  19. php获取上级绝对目录路径
  20. Abandoned connection cleanup thread failed to stop

热门文章

  1. OkHttp的简单使用
  2. mysql drop后回收站怎么恢复_回收站清空了怎么恢复
  3. 第一课时(下):破解基础之常见加壳程序特征
  4. 深度学习NLP领域文本生成总结
  5. 数据名称:中国家庭追踪调查(CFPS)数据区县码数据描述:162个区县代码,适用于10-20年份,可匹配约85-90%的样本。可依次匹配coutyid-区县行政码code-地级市行政码city-省份
  6. Epson机械手程序开发(1)新建一个项目
  7. react 全选_使用React的Hooks+ts实现全选和全不选?
  8. 【通信技术】信噪比及单位
  9. 移动端 -- 点击输入框默认弹出数字键盘
  10. TreeView安装使用集锦