我们在使用automapper
的时候经常会遇到这样的问题:假设展示给用户的数据我们用UserDto
类,User
类就是我们的实体类。在给用户编辑的时候,我们可能某些字段在数据库中为Null
,这时候需要一些默认值 比如这里UserDto
中的BirTime
,然后我们有一些人的习惯是在构造函数里面进行赋值
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime? BirTime{ get; set; }
}
public class UserDto
{
public UserDto()
{
this.BirTime=DateTime.Now;//把当前时间赋值给BirTime
}
public string Name { get; set; }
public DateTime? BirTime{ get; set; }
}
//假设我们从数据库中取出来的值是这样的,然后我们要和UserDto之间进行转换
User user = new User()
{
Id = 1,
Name = "caoyc",
BirTime=null
};
UserDto userDto=user.MapTo<UserDto>();//这里把实体类转换成前台给用户展示的UserDto类
看似这里的代码都没有什么问题,但是我想在
BirTime
为null
值的时候使用我UserDto
类中的默认值也就是当前时间,但在实际操作之后就会发现转换过后的UserDto
的值已经变掉了。那么问题来了, 如何才能使用默认值呢。
方法有很多,比如
//第一种方法
UserDto userDto=user.MapTo<UserDto>();//这里把实体类转换成前台给用户展示的UserDto类
userDto.BirTime=userDto.BirTime==null?DateTime.Now:userDto.BirTime;
第一种方法就是在自己转换完成之后判断一下,然后重新赋值,用这种方法的话那写构造函数自然就没什么必要了
//第二种方法
Mapper.CreateMap<User, UserDto>().ForAllMembers(opt => opt.Condition(srs => !srs.IsSourceValueNull));
第二种方法不怎么好,当你需要转换的类很多的时候,每个都要这样写,会很多也会很烦
//第三种方法,在全局配置的时候加上这句话
private static void CreateMappingsInternal(IMapperConfigurationExpression cfg)
{
//没错就是这句话
cfg.ForAllMaps((a, b) => b.ForAllMembers(opt => opt.Condition((src, dest, sourceMember) => sourceMember != null)));
}
个人比较推荐第三种方法,不过这就相当于一棒子打死了,有利也有弊。
【C#】AutoMapper
使用学习笔记
什么是AutoMapper
?
AutoMapper
是一个能自动完成对象与对象之间转化的开源库;通常DTO(Data Transfer Object
数据传输对象)与Model
之间的常规转化做法会使代码相当的冗长,AutoMapper
作者为了减少这种工作量,写了这个开源库,使DTO与Model之间能够自动的完成转化。
在学习过程中查阅了很多资料,帮助比较大的一片博文为:http://www.codeproject.com/Articles/61629/AutoMapper 全英文对我来说压力还是挺大的,但是还是一点点啃下来了。
基础使用方法:
public class DTO
{
public string userName {set; get;}
public string age {set; get;}
public string job {set; get;}
}
public class Model
{
public string userName {set; get;}
public string age {set; get;}
public string job {set; get;}
}
上述的DTO与Model
,一般情况下,DTO
为直接映射数据库中的数据,Model
为交互数据,那么如何把两着对象里的数据进行映射呢?
- 通常的做法为:
//DTO与Model之间可以这样转化,因为的对象的属性都为string类型
DTO.userName = Model.userName;
DTO.age = Model.age ;
DTO.job = Model.job ;
- 使用
AutoMapper
转化:
//首先需要先为DTO与Model之间定义一个映射关系
Mapper.CreateMap<DTO, Model>();
DTO dtoData = GetdtoDataFromDB();
Model modelData = Mapper.Map<DTO, Model>(dtoData );
这里的DTO对象就被AutoMapper
自动转化成了Model
对象,所以modelData
中的userName、age、job
的值即为GetdtoDataFromDB()
方法取出来的值。
遇到的坑点
在实际的项目中使用AutoMapper
对DTO
与Model
进行自动转化的时候遇到了一个坑点,其实也不算是坑点,只是一个我没有注意到的地方,这里做一个记录,加深自己的印象。
DTO
与Model
的结构如下:
// DTO
public class DTO
{
public string userName {set; get;}
public string age {set; get;}
public string job {set; get;}
public AddressDTO address {set ; get;}
}
public class AddressDTO{
public string country {set; get;}
public string province {set; get;}
}
// Model
public class Model
{
public string userName {set; get;}
public string age {set; get;}
public string job {set; get;}
public AddressModel address {set ; get;}
}
public class AddressModel{
public string country {set; get;}
public string province {set; get;}
}
写完了这个两个对象,于是便开始写与上面的一样开始用AutoMapper
进行自动转化
Mapper.CreateMap<DTO, Model>();
DTO dtoData = GetdtoDataFromDB();
Model modelData = Mapper.Map<DTO, Model>(dtoData );
写完了,心里很高兴,心里感叹着AutoMapper
的强大。结果项目一跑起来报错,报错结果为:
从报错结果分析,原因是我没有配置正确,如何解决这个问题呢?
很简单,像这样包含子对象之间的转换,只要把子对象之间也使用AutoMapper
来定义映射关系不就行了吗?
Action
Mapper.CreateMap<DTO, Model>();
Mapper.CreateMap<AddressDTO , AddressModel >();//子对象映射关系
DTO dtoData = GetdtoDataFromDB();
Model modelData = Mapper.Map<DTO, Model>(dtoData );
这样,项目就成功的跑起来了,AutoMapper
的一个好处之一:如果我们需要一个新的字段,比如在DTO
中新增一个
public string sex {set; get;}
这个时候如果是常规的写法,我们需要在转化的地方手动加上
DTO.sex = Model.sex;
当项目越来越大的时候,我们做这种更改需要极其小心。
而使用AutoMapper
来实现自动转化,只需要在DTO
中与Model
中添加需要新增的字段即可,避免了手写代码的风险。
最后
AutoMapper
还有许多强大的功能,如自定义映射关系等;
今后学习过程中如果使用到,也会以笔记的形式记录下来。
C# AutoMapper
踩坑
AutoMapper
中的before
和ForMember
和after
的区别
我这里准备了一个测试用例,我们正常实体转dto
都是直接试用一些方法
CreateMap<TestEntity, TestDto>();
但是实际难免需要一些字段类型的改变,这里介绍before
和ForMember
还有after
的区别
一、before
的使用
before
使用在实体转化dto
之前,用于改变实体的字段值,然后再赋值给dto
;
这里的id
本来是1
,但是通过before
转化赋值后,输出到都dto
就变成了3
;
二、after
的使用
after
使用在实体转化dto
之后,用于改变输出dto
的值,在实体赋值之后,到这里类型如果没问题的话,是不会报错的,但是如果类型不对的话,就会报错,
下面为大家演示一下
类型正确
类型错误:Introduce
字符串转成数组对象
这里的字符串转成数组对象,就会报类型错误,这也是我遇到的坑,这个时候before
和after
都不能用
三、ForMember
的使用
这里使用ForMember
进行属性条件转换
属性转换成功了
public void Test1()
{
List<Detail> details = new List<Detail>
{
new Detail
{
Id=1,
Name="唱"
},
new Detail
{
Id=1,
Name="跳"
},
new Detail
{
Id=1,
Name="rap"
},
new Detail
{
Id=1,
Name="篮球"
},
};
TestEntity test = new TestEntity
{
Id = 1,
Name = "小明",
Introduce = JsonConvert.SerializeObject(details)
};
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<TestEntity, TestDto>();
// 表示源类型命名规则
cfg.SourceMemberNamingConvention = new PascalCaseNamingConvention();
//表示目标类型命名规则
cfg.DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();
//LowerUnderscoreNamingConvention 和 PascalCaseNamingConvention 是 AutoMapper 提供的两个命名规则。前者命名是小写并包含下划线,后者就是帕斯卡命名规则(每个单词的首字母大写)。
});
var result = config.CreateMapper().Map<TestDto>(test);
}
public class TestEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string Introduce { get; set; }
}
public class TestDto
{
public int Id { get; set; }
public string Name { get; set; }
public List<Detail> Introduce { get; set; }
}
public class Detail
{
public int Id { get; set; }
public string Name { get; set; }
}