ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

.NET之AutoMapper使用

2022-06-22 23:31:40  阅读:138  来源: 互联网

标签:set get cfg var 使用 AutoMapper new NET public


开篇语

为了方便部分朋友的阅读,我将公众号的文章做了一个目录导航,可以通过点击公众号菜单【文章目录】获取地址。

介绍

是一个基于约定的对象-对象映射器。通俗地讲就是将一对象转换为另一个对象,前提是我们需要在这两个对象之间建立约定。支持框架:.Net 4.6.1+ 、.Net Standard 2.0+

官网:https://docs.automapper.org/

使用

本次示例环境:vs2019、.net5

生成代码

通过安装扩展或安装nuget包来更好地查看生成的代码,或者直接使用在线地址查看

详情:https://docs.automapper.org/en/latest/Understanding-your-mapping.html

    var config = new MapperConfiguration(cfg => cfg.CreateMap<AddUserDto, User>());
    var executionPlan = config.BuildExecutionPlan(typeof(AddUserDto), typeof(User));
    
    //如果是操作数据库
    var expression = context.Entities.ProjectTo<Dto>().Expression;

该代码不建议在发布后使用。

控制台操作

通过控制台对AutoMapper简单用法来一个了解,使用vs2019新建.Net5控制台应用

引用组件

  <ItemGroup>
    <PackageReference Include="AutoMapper" Version="10.1.1" />
    <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
  </ItemGroup>

Newtonsoft.Json是为了显示转换为效果,序列化使用

默认约定映射

目标:将接收到的用户请求类转换为我们的用户实体类添加到数据库中

数据准备

用户实体类

    public class User
    {
        public string Id { get; set; }

        /// <summary>
        /// 字符长度限制
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        ///图片地址
        /// </summary>
        public string Url { get; set; }

        /// <summary>
        /// 地址
        /// </summary>
        public string Address { get; set; }

        /// <summary>
        /// 生日
        /// </summary>
        public DateTime BirthDay { get; set; }

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime { get; set; }
    }

添加用户请求类

    public class AddUserDto
    {
        public string Id { get; set; }

        /// <summary>
        /// 字符长度限制
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        ///图片地址
        /// </summary>
        public string Url { get; set; }

        /// <summary>
        /// 省
        /// </summary>
        public string Province { get; set; }

        /// <summary>
        /// 市
        /// </summary>
        public string City { get; set; }

        /// <summary>
        /// 区
        /// </summary>
        public string Area { get; set; }

        /// <summary>
        /// 生日
        /// </summary>
        public DateTime BirthDay { get; set; }

        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime { get; set; }
    }
    var addUser = new AddUserDto
    {
        Id = "123456",
        Name = "azrng",
        Province = "河南省",
        City = "郑州市",
        Area = "金水区",
        BirthDay = DateTime.Parse("2021-05-20 13:14"),
        CreateTime = DateTime.Now,
        Url = "www.baidu.com/1.png"
    };

    //创建映射规则,第一个参数为源实体,第二个为目标实体
    var config = new MapperConfiguration(cfg => cfg.CreateMap<AddUserDto, User>());
    //var mapper = config.CreateMapper();
    //or
    var mapper = new Mapper(config);
    //进行转换,得到目标实体
    Console.WriteLine(JsonConvert.SerializeObject(mapper.Map<User>(addUser)));
    //{"Id":"123456","Name":"azrng","Url":"www.baidu.com/1.png","Address":null,"BirthDay":"2021-05-20T13:14:00","CreateTime":"2021-05-20T16:09:08.6982869+08:00"}

自动配置

主动扫描从配置文件继承的类,并将它们加入配置

    var assembly = Assembly.GetExecutingAssembly();
    var config = new MapperConfiguration(cfg =>
    {
        cfg.AddMaps(assembly);
    });
    var mapper = new Mapper(config);
    Console.WriteLine(JsonConvert.SerializeObject(mapper.Map<User>(addUser)));

WebAPI

安装组件

    <PackageReference Include="AutoMapper" Version="10.1.1" />
    <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" />

ConfigureServices中将Automapper注入到依赖注入容器中

    //只映射单个
    //services.AddAutoMapper(typeof(UserProfile));

    //映射程序集
    //services.AddAutoMapper(typeof(Startup).Assembly);

    //将继承于Profile的类批量映射
    var profiles = Assembly.GetExecutingAssembly().DefinedTypes
        .Where(t => typeof(Profile).GetTypeInfo().IsAssignableFrom(t.AsType()))
        .Select(t => t.AsType()).ToArray();
    services.AddAutoMapper(profiles);

创建包含用户实体类的WebAPI项目并且配置连接数据库。

    public class User : IdentityBaseEntity
    {
        /// <summary>
        /// 字符长度限制
        /// </summary>
        [Comment("名称")]
        [Required]//必填
        [StringLength(60, MinimumLength = 3)]//长度限制
        public string Name { get; set; }

        /// <summary>
        /// 生日
        /// </summary>
        [Comment("生日")]
        [Required]//必填
        public DateTime BirthDay { get; set; }

        /// <summary>
        ///头像
        /// </summary>
        [Comment("头像")]
        [Required]//必填
        [StringLength(200)]//长度限制
        public string HeadImg { get; set; }

        [Comment("创建时间")]
        [DataType(DataType.Date)]//格式限制
        public DateTime CreateTime { get; set; }
    }

默认约定映射

使用添加用户场景操作

    public class AddUserVm
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 生日
        /// </summary>
        public DateTime BirthDay { get; set; }

        /// <summary>
        ///头像
        /// </summary>
        public string HeadImg { get; set; }
    }

用户映射配置

    public class UserProfile : Profile
    {
        public UserProfile()
        {
            CreateMap<AddUserVm, User>();//默认映射
        }
    }

使用

        private readonly OpenDbContext _dbContext;
        private readonly IMapper _mapper;

        public UserController(OpenDbContext dbContext, IMapper mapper)
        {
            _dbContext = dbContext;
            _mapper = mapper;
        }

        [HttpPost]
        public async Task<ActionResult<string>> AddAsync(AddUserVm dto)
        {
            //自行参数校验
            var entity = _mapper.Map<User>(dto);
            await _dbContext.AddAsync(entity).ConfigureAwait(false);
            await _dbContext.SaveChangesAsync().ConfigureAwait(false);
            return entity.Id;
        }

添加成功

image.png

这里我们模拟存储的是头像的相对地址

自定义成员映射

这个时候我们需要查询某一个用户的信息,那么我们先来添加用户详情返回类

    public class GetUserResult
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        ///年
        /// </summary>
        public string Year { get; set; }

        /// <summary>
        /// 月
        /// </summary>
        public string Month { get; set; }

        /// <summary>
        /// 日
        /// </summary>
        public string Day { get; set; }

        /// <summary>
        ///头像
        /// </summary>
        public string HeadImg { get; set; }
    }

创建自定义成员映射

    CreateMap<User, GetUserResult>()
            //单独配置每一个成员
            .ForMember(ur => ur.Year, u => u.MapFrom(src => src.BirthDay.Year))
            .ForMember(ur => ur.Month, u => u.MapFrom(src => src.BirthDay.Month))
            .ForMember(ur => ur.Day, u => u.MapFrom(src => src.BirthDay.Day))
            .ForMember(ur => ur.HeadImg, u => u.MapFrom(src => "www.baidu.com/" + src.HeadImg));

查询操作

        [HttpGet("{userId}")]
        public async Task<ActionResult<GetUserResult>> GetDetails(string userId)
        {
            var result = await _dbContext.Users.Where(t => t.Id == userId).ProjectTo<GetUserResult>(_mapper.ConfigurationProvider).FirstOrDefaultAsync();
            return Ok(result);
        }

结果

{
    "name": "azrng",
    "year": "1990",
    "month": "5",
    "day": "29",
    "headImg": "www.baidu.com/123456.png"
}

其他操作

命名公约

相互映射以下属性:property_name -> PropertyName

    var config = new MapperConfiguration(cfg =>
    {
        //源成员的命名约定
        cfg.SourceMemberNamingConvention = new LowerUnderscoreNamingConvention();
        //目标成员的命名约定
        cfg.DestinationMemberNamingConvention = new PascalCaseNamingConvention();
    });

替换字符

替换属性名称字符  Source和Destination 的Avíator属性i不一样

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Source, Destination>();
        //可能替换单个字符或者整个单词
        cfg.ReplaceMemberName("í", "i");
        //cfg.ReplaceMemberName("Airlina", "Airline");
    });
    var mapper = new Mapper(config);
    Console.WriteLine(JsonConvert.SerializeObject(mapper.Map<Destination>(source)));
    //{"Value":1,"Aviator":2,"SubAirlinaFlight":3}

识别前/后缀

因为有时候源/目标属性具有前后缀,因为名称不同,所以不能直接映射

    var config = new MapperConfiguration(cfg =>
    {
        cfg.RecognizePrefixes("frm");//比如前缀都是frm区别
        //cfg.CreateMap<a,b>
    });

全局属性/字段过滤

映射时候选择一部分进行过滤

    var config = new MapperConfiguration(cfg =>
    {
        cfg.ShouldMapField = fi => false;

        cfg.ShouldMapProperty = pi => pi.GetMethod.IsPublic || pi.GetMethod.IsPrivate;
    });

嵌套映射

当映射的类里面包含嵌套的类,那么需要将这个嵌套的类也给配置

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<OuterSource, OuterDest>();
        cfg.CreateMap<InnerSource, InnerDest>();//需要将嵌套的内容也配置

    });
    var outerSource = new OuterSource
    {
        Value = 2,
        Inner = new InnerSource
        {
            OtherValue = 5
        }
    };
    var mapper = new Mapper(config);
    var outerDest = mapper.Map<OuterDest>(outerSource);

列表和数组

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Source, Destination>();
    });
    var listSourse = new List<Source>
                {
                    new Source{ Value=1 },
                    new Source{  Value=2}
                };
    var mapper = new Mapper(config);
    var listDest = mapper.Map<List<Destination>>(listSourse);

反向映射

    var configuration = new MapperConfiguration(cfg =>
    {
        //约定映射
        //cfg.CreateMap<Order, OrderDto>().ReverseMap();
        //自定义反向映射
        cfg.CreateMap<Order, OrderDto>()
        .ForMember(od => od.CustomerName, o => o.MapFrom(src => src.Customer.Name))
        .ReverseMap();
    });
    var mapper = new Mapper(configuration);

    var order = new Order
    {
        Customer = new Customer
        {
            Name = "Bob"
        },
        Total = 15.8m
    };
    var orderDto = mapper.Map<OrderDto>(order);

忽略成员

使用IgnoreAttribute
忽略映射和/或验证中的单个目标成员:

using AutoMapper.Configuration.Annotations;

[AutoMap(typeof(Order))]
public class OrderDto {
    [Ignore]
    public decimal Total { get; set; }

类型映射配置

这相当于一个配置。CreateMap<Order, OrderDto>()

[AutoMap(typeof(Order))]
public class OrderDto {
    // destination members

重定向到不同源成员

SourceMemberAttribute可以重定向到单独的命名成员

    [AutoMap(typeof(User))]
    public class GetUserResult
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 名称2
        /// </summary>
        [SourceMember(nameof(User.Name))]
        public string Name2 { get; set; }

        /// <summary>
        /// 名称3
        /// </summary>
        [SourceMember("Name")]
        public string Name3 { get; set; }
    }

注意:必须使用AutoMap,不能和CreateMap搭配使用

枚举映射

需要引用包

AutoMapper.Extensions.EnumMapping

配置操作

    CreateMap<Source, Destination>()
        .ConvertUsingEnumMapping(opt => opt
        .MapValue(Source.First, Destination.Default))
        .ReverseMap();//支持反过来映射

自定义类型转换器

特殊需求,将一种类型转换为另一种类型,例如字符串的源类型到 Int32 的目标类型可以直接使用Convert.ToInt32,复杂的需要自定义类型转换器

配置以及自定义转换器

    var configuration = new MapperConfiguration(cfg => {
      cfg.CreateMap<string, int>().ConvertUsing(s => Convert.ToInt32(s));
      cfg.CreateMap<string, DateTime>().ConvertUsing(new DateTimeTypeConverter());
      cfg.CreateMap<string, Type>().ConvertUsing<TypeTypeConverter>();
      cfg.CreateMap<Source, Destination>();
    });


public class DateTimeTypeConverter : ITypeConverter<string, DateTime>
{
    public DateTime Convert(string source, DateTime destination, ResolutionContext context)
    {
        return System.Convert.ToDateTime(source);
    }
}

public class TypeTypeConverter : ITypeConverter<string, Type>
{
    public Type Convert(string source, Type destination, ResolutionContext context)
    {
          return Assembly.GetExecutingAssembly().GetType(source);
    }
}

参考文档:https://docs.automapper.org/en/latest/Custom-type-converters.html

条件映射

仅当源对象中的属性baz大于或等于0时,才会映射该属性

var configuration = new MapperConfiguration(cfg => {
  cfg.CreateMap<Foo,Bar>()
    .ForMember(dest => dest.baz, opt => opt.Condition(src => (src.baz >= 0)));
});

通过PreCondition方法,解析值之前加条件,它在映射过程中运行更快。通过先调用先决条件,决定哪些是将要映射的源,然后调用条件,最后分配给目标值

var configuration = new MapperConfiguration(cfg => {
  cfg.CreateMap<Foo,Bar>()
    .ForMember(dest => dest.baz, opt => {
        opt.PreCondition(src => (src.baz >= 0));
        opt.MapFrom(src => {
            // Expensive resolution process that can be avoided with a PreCondition
        });
    });
});

空替换

如果源值在成员中的任何位置为空,则空替换允许您为目标成员提供替代值。这意味着它不是从 null 映射,而是从您提供的值映射。

var config = new MapperConfiguration(cfg => cfg.CreateMap<Source, Dest>()
    .ForMember(destination => destination.Value, opt => opt.NullSubstitute("Other Value")));

映射前或者映射后操作

一般不这么操作,因为经常不使用AutoMapper做该操作

var configuration = new MapperConfiguration(cfg => {
  cfg.CreateMap<Source, Dest>()
    .BeforeMap((src, dest) => src.Value = src.Value + 10)
    .AfterMap((src, dest) => dest.Name = "John");
});

参考文档

https://docs.automapper.org/en/latest/Getting-started.html

 

 

转 https://www.modb.pro/db/77562

标签:set,get,cfg,var,使用,AutoMapper,new,NET,public
来源: https://www.cnblogs.com/wl-blog/p/16403607.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有