配置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, Destination>();
进行一下测试:
Source src = new Source() { SomeValue = 1, AnotherValue = "2" };
Destination dest = Mapper.Map<Destination>(src); ObjectDumper.Write(dest);
我们可以在控制台看到dest对象的属性值:
这样我们就完成了一个简单的AutoMapper映射。
Profile的用法
Profile提供了一个命名的映射类,所有继承自Profile类的子类都是一个映射集合。
我们来看一下Profile的用法,这个例子中仍然使用上面的Source类和Destination类。
public class SourceProfile : Profile
{
protected override void Configure()
{
CreateMap<Source, Destination>();
}
}
我们可以再Profile中重写Configure方法,从而完成映射规则的配置。从Profile初始化Mapper规则:
Mapper.Initialize(x => x.AddProfile<SourceProfile>());
在一个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, Destination>(); //Source->Destination2
CreateMap<Source, Destination2>().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<Profiles.SourceProfile>();
cfg.AddProfile<Profiles.OrderProfile>();
cfg.AddProfile<Profiles.CalendarEventProfile>();
});
}
}
在程序运行的时候,只需要调用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.Order, Dto.OrderDto>();
}
}
测试代码:
Entity.Customer customer = new Entity.Customer() { Name = "Tom" };
Entity.Order order = new Entity.Order() { Customer = customer };
Dto.OrderDto orderDto = Mapper.Map<Dto.OrderDto>(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<Entity.CalendarEvent, Entity.CalendarEventForm>()
.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<Entity.CalendarEventForm>(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<Entity.Source, Entity.Destination>();
Mapper.AssertConfigurationIsValid();
运行程序将会出现AutoMapperConfigurationException异常:
这是因为SomeValuefff在Source类中没有对应的字段造成的。
解决这种异常的方法有:
指定映射字段,例如:
Mapper.CreateMap<Entity.Source, Entity.Destination>()
.ForMember(dest => dest.SomeValuefff, opt =>
{
opt.MapFrom(src => src.SomeValue);
});
或者使用Ignore方法:
Mapper.CreateMap<Entity.Source, Entity.Destination>()
.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<Source, int>
{
protected override int ResolveCore(Source source)
{
return source.Value1 + source.Value2;
}
}
然后在映射规则中使用这个解析器:
public class SourceProfile : Profile
{
protected override void Configure()
{
//Source->Destination
CreateMap<Source, Destination>()
.ForMember(dest => dest.Total, opt =>
{
opt.ResolveUsing<CustomResolver>();
});
}
}
测试代码:
Source src = new Source()
{
Value1 = 1,
Value2 = 2
};
Destination dest = Mapper.Map<Destination>(src);
ObjectDumper.Write(dest);
测试结果:
在使用自定义Resolver中,我们还可以指定Resolver的构造函数,例如:
//Source->Destination
CreateMap<Source, Destination>()
.ForMember(dest => dest.Total, opt =>
{
opt.ResolveUsing<CustomResolver>()
.ConstructedBy(() =>
new CustomResolver
());
});
自定义类型转换器(Custom type converters)
AutoMapper通过ConvertUsing来使用自定义类型转换器。ConvertUsing有三种用法:
void ConvertUsing(Func<TSource, TDestination> mappingFunction);
void ConvertUsing(ITypeConverter<TSource, TDestination> converter);
void ConvertUsing<TTypeConverter>() where TTypeConverter : ITypeConverter<TSource, TDestination>;
当我们有如下的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<string, int>()
.ConvertUsing(Convert.ToInt32);
//Source->Destination
CreateMap<Source, Destination>();
}
}
在上面的配置中,我们首先创建了从string到int的类型转换,这里使用了系统自带的Convert.ToInt32转换方法。
除了这种方法之外,我们还可以自定义类型转换器:
public class CustomConverter : ITypeConverter<Source, Destination>
{
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<Source, Destination>()
.ConvertUsing<CustomConverter>();
}
}
或者,我们也可以使用下面的配置:
public class SourceProfile : Profile
{
protected override void Configure()
{
//Source->Destination
CustomConverter converter = new CustomConverter();
CreateMap<Source, Destination>()
.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<Source, Destination>()
.ForMember(dest => dest.Value, opt =>
{
opt.NullSubstitute("原始值为NULL");
});
}
}
测试代码:
Source src = new Source();
Destination dest = Mapper.Map<Destination>(src);
ObjectDumper.Write(dest);
测试结果:
条件映射(Conditional mapping)
条件映射只当Source类中的属性值满足一定条件的时候才进行映射。例如:
public class Foo
{
public int baz;
} public class Bar
{
public uint baz;
}
对应的配置代码如下:
Mapper.CreateMap<Foo, Bar>()
.ForMember(dest => dest.baz, opt =>
{
opt.Condition(src => (src.baz >= 0));
});
转自:http://www.cnblogs.com/youring2/p/automapper.html
配置AutoMapper映射规则《转》的更多相关文章
- SpringCloud Zuul 路由映射规则配置
阅读目录 前言 快速入门 路由详解 Cookie与头信息 本地跳转 Hystrix和Ribbon支持 过滤器解释 动态加载 后记 回到目录 前言 本文起笔于2018-06-26周二,接了一个这周要完成 ...
- EF Code First数据库映射规则及配置
EF Code First数据库映射规则主要包括以下方面: 1.表名及所有者映射 Data Annotation: 指定表名 1 using System.ComponentModel.DataAnn ...
- 使用 AutoMapper 映射 IDataReader、DataSet、DataTable 到实体类
AutoMapper是一个.NET的对象映射工具. 项目地址:https://github.com/AutoMapper/AutoMapper. 帮助文档:https://github.com/Aut ...
- SpringMVC注解汇总(二)-请求映射规则
接上一节SpringMVC注解汇总-定义 讲到Httpy请求信息 URL路径映射 1)普通URL路径映射 @RequestMapping(value={"/test1", &quo ...
- SpringMVC学习系列(3) 之 URL请求到Action的映射规则
在系列(2)中我们展示了一个简单的get请求,并返回了一个简单的helloworld页面.本篇我们来学习如何来配置一个action的url映射规则. 在系列(2)中我们在HelloWorldContr ...
- SpringMVCURL请求到Action的映射规则
SpringMVC学习系列(3) 之 URL请求到Action的映射规则 在系列(2)中我们展示了一个简单的get请求,并返回了一个简单的helloworld页面.本篇我们来学习如何来配置一个acti ...
- Springboot学习02-webjars和静态资源映射规则
Springboot学习01-webjars和静态资源映射规则 前言 1-以前我们在IDEA中创建一个项目,添加web依赖包,我们现在是一个web应用,应该在man目录下面有一个webapp文件夹,将 ...
- 使用Logstash创建ES映射模版并进行数据默认的动态映射规则
本文配置为 ELK 即(Elasticsearch.Logstash.Kibana)5.5.1. Elasticsearch 能够自动检测字段的类型并进行映射,例如引号内的字段映射为 String,不 ...
- go培训课程都学什么?xorm框架学习系列(二):xorm结构体映射规则和表操作
上节内容我们学习了基本的xorm框架的知识和基础配置的相关信息.本节课内容我们继续学习相关的知识和相关操作. 名称映射规则 名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射. ...
随机推荐
- C#SerialPort实现串口控制继电器
最近做了一个小系统,麻雀虽小五脏俱全呀,用到各种线程控制,串口控制等技术.其中串口控制最麻烦,因为继电器的响应很快,根据不同的转接口,返回的数据质量是不一样的,所以不能直接wirte,然后马上read ...
- 杂谈PID控制算法——最终篇:C语言实现51单片机中的PID算法
真遗憾,第二篇章没能够发表到首页上去.趁热打铁.把最终篇——代码篇给发上来. 代码的设计思想请移步前两篇文章 //pid.h #ifndef __PID__ #define __PID__ /*PID ...
- 移植Linux2.6.38到Tiny6410_1GNandflash
首先说一下为什么要下决心移植Linux内核,本来移植完Uboot后我想先把韦东山第二期的驱动教程看完后在试图移植内核,可是当我跟着教程写好LCD的驱动后,去设置编译友善的内核来测试程序时,我稍微改一点 ...
- WCF服务全局统一异常处理机制
转载:http://www.csframework.com/archive/1/arc-1-20150109-2193.htm 服务端增加WCF服务全局异常处理机制,任一WCF服务或接口方式出现异常, ...
- Chrome DevTools 代码覆盖率功能详解
共 1812 字,读完需 3 分钟.工欲善其事必先利其器,前端周刊本周起每周会加餐 1 篇工具技巧,里面辅以动图,让大家看完就能学会,并上手使用.本文会介绍 Chrome Canary 新增的代码覆盖 ...
- mongodb_命令行
一.打开命令行 cmd --> cd C:\Program Files\MongoDB\Server\3.0\bin\ --> mongo.exe 二.连接远程机器命令行工具 1.连接 ...
- Protostuff序列化工具类
源代码 package org.wit.ff.util; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStre ...
- javascript(JQuery)元素操作
html代码如下: <div id="picK"> <ul> <li style="float:left;width:90px;" ...
- DELPHI MAKEWORD的用法
WORD MAKEWORD( BYTE bLow, // low-order byte of short value BYTE bHigh // high-order byte of ...
- Admin Finder
#Created for coded32 and his teamopenfire Eliminated Some bugs from my last code shared here as Gues ...