Automapper如何全局配置map条件过滤null值空值对所有映射起效

我们在使用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类

看似这里的代码都没有什么问题,但是我想在BirTimenull值的时候使用我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()方法取出来的值。

遇到的坑点

在实际的项目中使用AutoMapperDTOModel进行自动转化的时候遇到了一个坑点,其实也不算是坑点,只是一个我没有注意到的地方,这里做一个记录,加深自己的印象。
DTOModel的结构如下:

// 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中的beforeForMemberafter的区别

我这里准备了一个测试用例,我们正常实体转dto都是直接试用一些方法

CreateMap<TestEntity, TestDto>();

但是实际难免需要一些字段类型的改变,这里介绍beforeForMember还有after的区别

一、before的使用

before使用在实体转化dto之前,用于改变实体的字段值,然后再赋值给dto
在这里插入图片描述
这里的id本来是1,但是通过before转化赋值后,输出到都dto就变成了3

二、after的使用

after使用在实体转化dto之后,用于改变输出dto的值,在实体赋值之后,到这里类型如果没问题的话,是不会报错的,但是如果类型不对的话,就会报错,

下面为大家演示一下

类型正确
在这里插入图片描述
类型错误:Introduce字符串转成数组对象
在这里插入图片描述
这里的字符串转成数组对象,就会报类型错误,这也是我遇到的坑,这个时候beforeafter都不能用

三、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; }
        }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值