AutoMapper 9.0快速上手,从老版本迁移到9.0+AutoMapper9.0和Autofac的完美结合

.NET模型映射器AutoMapper 9.0发布了,官方宣称不再支持静态方法调用了,老版本的部分API将在升级到9.0后,直接升级包到9.0会编译报错,所以写篇文章记录下AutoMapper新版本的学习过程吧,如果还不知道AutoMapper是什么的,建议先看这篇文章:https://masuit.com/156,或者参考官方文档:https://automapper.readthedocs.io/en/latest/Getting-started.html

AutoMapper9.0快速上手

首先,我们准备两个需要用于相互转换的类:


1

2

3

4

5

6

7

8

9

10

11

12

    public class Person

    {

        public string Id { getset; }

        public string Name { getset; }

        public Address Address { getset; }

    }

    public class Address

    {

        public string Province { getset; }

        public string City { getset; }

        public string Street { getset; }

    }


1

2

3

4

5

6

    public class PersonDto

    {

        public string Id { getset; }

        public string Name { getset; }

        public string Address { getset; }

    }

我们想实现从Person到PersonDto的映射转换。

准备好实体模型后我们将AutoMapper9.0通过nuget安装到项目中:

开始写映射代码吧,首先,我们需要明确源和目标类型。由于目标类型的设计一般会受其所在层的影响,比如通常情况下我们最终呈现在页面上的数据结构和我们数据库底层设计会有所不同,但只要成员的名称与源类型的成员匹配,AutoMapper就能发挥最佳效果。不知何时开始的,AutoMapper可以实现自动映射了,也就是如果需要映射的源和目标的属性和类型长得一样,都可以不用写映射配置了,比如源对象里面有一个名为“FirstName”的成员,则会自动将其映射到目标对象的名为“FirstName”成员上。

映射时,AutoMapper会忽略空引用异常。这是默认设计的。如果你觉得这样做不好,你可以根据需要将AutoMapper的方法与自定义值解析器结合使用。

明确映射关系后,使用MapperConfiguration和CreateMap 为两种类型创建映射关系。MapperConfiguration每个AppDomain通常只需要一个实例,并且应该在应用程序启动期间进行实例化。


1

var config = new MapperConfiguration(cfg => cfg.CreateMap<Person, PersonDto>().ForMember(p => p.Address, e => e.MapFrom(p => p.Address.Province + p.Address.City + p.Address.Street)));

和早期版本一样,左侧的类型是源类型,右侧的类型是目标类型。

现在,映射关系做好了,我们便可以创建映射器了:


1

var mapper = config.CreateMapper();

这样,便可以像往常一样,使用Map方法进行对象的映射了,而如今大多数应用程序都在用依赖注入,所以AutoMapper现在也推荐我们通过依赖注入来注入创建的IMapper实例。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

        static void Main(string[] args)

        {

            var config = new MapperConfiguration(cfg => cfg.CreateMap<Person, PersonDto>().ForMember(p => p.Address, e => e.MapFrom(p => p.Address.Province + p.Address.City + p.Address.Street)));

            var mapper = config.CreateMapper();

            var person = new Person()

            {

                Id = "1",

                Name = "李扯火",

                Address = new Address()

                {

                    Province = "新日暮里",

                    City = "地牢",

                    Street = "van先生的家"

                }

            };

            var personDto = mapper.Map<PersonDto>(person);

        }

通常情况下,我们为了代码规范,会将映射配置单独写在一个类中,而AutoMapper也为我们提供了这样的一个“接口”,我们只需要创建一个class,继承自Profile,在构造函数中写映射配置:


1

2

3

4

5

6

7

    public class MappingProfile : Profile

    {

        public MappingProfile()

        {

            CreateMap<Person, PersonDto>().ForMember(p => p.Address, e => e.MapFrom(p => p.Address.Province + p.Address.City + p.Address.Street));

        }

    }


1

var config = new MapperConfiguration(cfg => cfg.AddProfile(new MappingProfile()));

从早期版本迁移至AutoMapper9.0

由于早期版本的AutoMapper映射时我们都直接调静态方法Mapper.Map就可以了,很爽,但是,9.0版本开始,取消了静态方法的调用,这就意味着升级后的代码可能需要随处创建Mapper的实例或者使用依赖注入容器对AutoMapper的实例进行托管。

我们先创建一个.NET Core的web项目,并创建一个基于内存的DbContext:

Models:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

    public class Person

    {

        public string Id { getset; }

        public string Name { getset; }

        public Address Address { getset; }

    }

    public class Address

    {

        public string Id { getset; }

        public string Province { getset; }

        public string City { getset; }

        public string Street { getset; }

        public string PersonId { getset; }

        public Person Person { getset; }

    }

DataContext:


1

2

3

4

5

6

7

8

9

10

11

12

13

    public class DataContext : DbContext

    {

        public DataContext(DbContextOptions<DataContext> options) : base(options)

        {

        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)

        {

            base.OnModelCreating(modelBuilder);

            modelBuilder.Entity<Person>().HasOne(e => e.Address).WithOne(a => a.Person).IsRequired(false).OnDelete(DeleteBehavior.Cascade);

        }

        public DbSet<Person> Persons { getset; }

        public DbSet<Address> Address { getset; }

    }

迁移点1:Mapper实例的创建

由于早期版本都是通过Mapper.Map静态方法实现对象映射,现在需要一个Mapper实例了,所以,我们就使用无处不在的依赖注入来管理它吧,我们在Startup.cs里面:


1

2

3

var config = new MapperConfiguration(e => e.AddProfile(new MappingProfile()));

var mapper = config.CreateMapper();

services.AddSingleton(mapper);

这样便可以在需要用到Mapper的地方通过构造函数注入Mapper实例对象:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

    [Route("api/[controller]")]

    [ApiController]

    public class ValuesController : ControllerBase

    {

        private readonly IMapper _mapper;

        private readonly DataContext _dataContext;

        public ValuesController(IMapper mapper, DataContext dataContext)

        {

            _mapper = mapper;

            _dataContext = dataContext;

        }

        

        [HttpGet]

        public ActionResult Get()

        {

            var person = _dataContext.Persons.Include(p => p.Address).FirstOrDefault();

            return Ok(_mapper.Map<PersonDto>(person));

        }

    }

迁移点2:ProjectTo的新实现

AutoMapper9.0的ProjectTo方法需要传入一个MapperConfiguration对象,所以,要调用ProjectTo方法,还需要注入MapperConfiguration对象:


1

2

var config = new MapperConfiguration(e => e.AddProfile(new MappingProfile()));

services.AddSingleton(config);


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

    [Route("api/[controller]")]

    [ApiController]

    public class ValuesController : ControllerBase

    {

        private readonly IMapper _mapper;

        private readonly MapperConfiguration _mapperConfig;

        private readonly DataContext _dataContext;

        public ValuesController(IMapper mapper, DataContext dataContext, MapperConfiguration mapperConfig)

        {

            _mapper = mapper;

            _dataContext = dataContext;

            _mapperConfig = mapperConfig;

        }

        

        [HttpGet]

        public ActionResult Get()

        {

            var person = _dataContext.Persons.Include(p => p.Address).FirstOrDefault();

            return Ok(_mapper.Map<PersonDto>(person));

        }

        

        [HttpGet("list")]

        public ActionResult Gets()

        {

            var list = _dataContext.Persons.Include(p => p.Address).ProjectTo<PersonDto>(_mapperConfig).ToList();

            return Ok(list);

        }

    }

AutoMapper的依赖注入扩展

AutoMapper官方还提供了一个nuget包,用于AutoMapper的依赖注入实现:AutoMapper.Extensions.Microsoft.Dependency

安装扩展后,Startup.cs里面只需要一句话:


1

services.AddAutoMapper(Assembly.GetExecutingAssembly());

即可实现Mapper实例的托管。

同样,AutoMapper实例也可以被Autofac托管,实现属性注入!

AutoMapper与Autofac的完美结合

Autofac的好处就在于它能批量注入和属性注入,当然在这里体现的就是autofac属性注入的优势,省去了构造函数注入的麻烦,如果没装Resharper的同学有时还会忘记注入,而autofac则解决了这样的问题。

我们新建一个.NET Core的web项目,并安装好AutoMapper.Extensions.Microsoft.DependencyInjec和Autofac.Extensions.DependencyInjection这两个nuget包,因为这两个包已经包含了AutoMapper和autofac,所以不需要单独安装这两个包。

准备一个Service来模拟我们的项目分层:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

    public interface IPersonService

    {

        List<PersonDto> GetAll();

        Person Get(string id);

    }

    

    public class PersonService : IPersonService

    {

        public DataContext DataContext { getset; }

        public MapperConfiguration MapperConfig { getset; }

        

        public List<PersonDto> GetAll()

        {

            return DataContext.Persons.ProjectTo<PersonDto>(MapperConfig).ToList();

        }

        

        public Person Get(string id)

        {

            return DataContext.Persons.FirstOrDefault(p => p.Id == id);

        }

    }

注意上面的代码,没有写构造函数。

然后我们改造Startup.cs,让依赖注入容器使用Autofac托管:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

        public IServiceProvider ConfigureServices(IServiceCollection services)

        {

            services.AddDbContext<DataContext>(opt => opt.UseInMemoryDatabase());

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2).AddControllersAsServices().AddViewComponentsAsServices().AddTagHelpersAsServices();

            

            var config = new MapperConfiguration(e => e.AddProfile(new MappingProfile()));

            services.AddSingleton(config);

            services.AddAutoMapper(Assembly.GetExecutingAssembly());

            services.AddAutofac();

            ContainerBuilder builder = new ContainerBuilder();

            builder.Populate(services);

            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly()).AsImplementedInterfaces().Where(t => t.Name.EndsWith("Service") || t.Name.EndsWith("Controller")).PropertiesAutowired().AsSelf().InstancePerDependency(); //注册控制器为属性注入

            var autofacContainer = new AutofacServiceProvider(builder.Build());

            return autofacContainer;

        }

在控制器中,也能属性注入,是不是不上面迁移时的代码简单了许多!


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

    [Route("api/[controller]")]

    [ApiController]

    public class ValuesController : ControllerBase

    {

        public Mapper Mapper { getset; }

        public IPersonService PersonService { getset; }

        

        [HttpGet]

        public ActionResult Get()

        {

            return Ok(Mapper.Map<PersonDto>(PersonService.Get("1")));

        }

        

        [HttpGet("list")]

        public ActionResult Gets()

        {

            var list = PersonService.GetAll();

            return Ok(list);

        }

    }

没想到如此简单,就将AutoMapper和Autofac融合为一体!??????

上面都是.NET Core的代码,.NET Framework怎么办?

由于AutoMapper和autofac都是基于.NET Standard的项目,所以用法上都是大同小异,我相信你如果在项目中用了这两个库,要升级AutoMapper9.0也不难了,升级后哪些地方报错的,就按上面的步骤弄吧。

完整Demo代码下载

https://www.lanzous.com/i5qhrvg

出处:https://masuit.com/1625/ocrel

原文地址:https://www.cnblogs.com/mq0036/p/12409047.html

时间: 2024-09-30 18:36:02

AutoMapper 9.0快速上手,从老版本迁移到9.0+AutoMapper9.0和Autofac的完美结合的相关文章

ESFramework 4.0 快速上手(06) -- Rapid引擎(续)

<ESFramework 4.0 快速上手>系列介绍的都是如何使用Rapid引擎(快速引擎) -- RapidServerEngine 和 RapidPassiveEngine.其实,大家可以将这两个引擎看作是两个壳,内部包装的才是真正的ESFramework的网络引擎, ESFramework支持很多种网络引擎(客户端/服务端.二进制协议/文本协议.TCP/UDP),而RapidServerEngine和RapidPassiveEngine采用的是基于TCP和二进制协议的服务端引擎和客户端引

离线消息如何实现?-- ESFramework 4.0 快速上手(02)

在ESFramework 4.0 快速上手一文中,主要介绍了如何使用ESPlus.Rapid命名空间中的引擎来快速地构建基于TCP的网络通信系统,即使是使用ESPlus.Rapid来进行ESFramework快速开发,也还有很多可以介绍的内容,于是,我想再多写几篇文章来说明现实通信系统中的一些常见需求如何使用ESFramework快速实现.本文是为第一篇,介绍离线消息的原理和实现. 一.如何截获离线消息 阅读了ESFramework 4.0 快速上手朋友都知道,一个在线用户给另一个用户发送文本信

ESFramework 4.0 快速上手(01) -- Rapid引擎

(在阅读该文之前,请先阅读 ESFramework 4.0 概述 ,会对本文的理解更有帮助.) ESFramework/ESPlatform 4.0 的终极目标是为百万级的用户同时在线提供支持,因为强大,所以使用也较为复杂,配置也较多.但是如果我们的应用只是一个中小型的通信应用(同时在线5000人以下),直接使用ESPlatform就有点显得杀鸡用牛刀了.ESPlus.Rapid提供了一种快速的方式,来解决类似中小型的通信应用,以最简洁的方式来使用ESFramework. 使用ESPlus.Ra

如何使用自定义消息?--ESFramework 4.0 快速上手(04)

在ESFramework 4.0 快速上手一文中,我们讲述了如何使用Rapid引擎可以快速地上手ESFramework开发,文中介绍了使用ESPlus.Application.CustomizeInfo命名空间下的类可以发送和处理自定义消息,本文我们就通过一个简单的例子来深入讲解如何使用自定义消息. 例子的场景很简单:假设客户端登陆到服务器之后,要求请求加入某个组,服务端收到该请求后,处理该请求,并给客户端相应的回复 -- 是否加入成功,客户端收到回复后,即可作出相应的处理. 一.定义消息类型和

重登陆模式 --ESFramework 4.0 快速上手(07)

在ESFramework框架中基于TCP的服务端引擎(当然也包括Rapid引擎)都采用了这样一条规则:默认情况下,客户端与服务器成功建立TCP连接以后,服务端会从客户端发过来的第一条消息中取出消息头的UserID属性的值,并将其与对应的TCP连接绑定起来.这样,服务端就知道每一个TCP连接所对应的用户UserID,而当我们要求服务端向某个客户端发送消息时,服务端就知道通过哪个TCP连接进行发送了.TCP连接与UserID是一一对应的,一个TCP连接只能对应一个UserID,同样的,一个UserI

eureka server中设置认证登录,spring cloud新老版本不同的配置(spring cloud2.0+ 和springcloud- )

注:spring cloud学习关于设置eureka server的登录认证出现问题记录 spring cloud2.0以下版本配置: application.yml配置: 1 security: 2 basic: 3 enabled: true 4 user: 5 name: user 6 password: password123 spring cloud2.0以上版本配置: application.yml配置: 1 spring: 2 application: 3 name: eureka

判定生死的心跳机制 --ESFramework 4.0 快速上手(07)

在Internet上采用TCP进行通信的系统,都会遇到一个令人头疼的问题,就是"掉线".而"TCP掉线"这个问题远比我们通常所能想象的要复杂的多 -- 网络拓扑纷繁复杂.而从始节点A到终节点B之间可能要经过N多的交换机.路由器.防火墙等等硬件设备,每个硬件设备的相关设定也不统一,再加上网络中可能出现的拥塞.延迟等,使得我们在编程时,处理掉线也非常棘手. 一.从程序的角度看待TCP掉线 TCP掉线的原因可能多种多样.不一而足,比如,客人的电脑突然断电.OS崩溃.路由器

Kali Linux 初上手记录 初始版本1.0.9

一直学的都是理论方面的东西,最近想多搞一搞具体的技术.于是从Kali Linux开始,做一些渗透练习,慢慢敲定,以后做些什么. 这里主要是记录一些过程中犯的错误和一些经验总结. 也许是因为4.1愚人节开始学习吧,我认为kali一直在和我开玩笑,以至于我随时都不知道自己今天能进步多少. 开始使用2016.1版本,毕竟此时是最新的嘛,然后发现好多不一样,又回到1.0.9版本,然后又用2016.1版本.反正各种交错使用.以后更多用2016.1吧我估计,不过我也不敢确定. 第一天  4.1: 这里视频是

快速入门系列--WebAPI--04在老版本MVC4下的调整

WebAPI是建立在MVC和WCF的基础上的,原来微软老是喜欢封装的很多,这次终于愿意将http编程模型的相关细节暴露给我们了.在之前的介绍中,基本上都基于.NET 4.5之后版本,其System.Net.Http程序集非常的丰富,而老版本的则相对较弱.在WebAPI v1.0(和ASP.NET MVC4在一起的版本)很多的类和接口并不存在,同时对Task异步编程(ApiController默认提供异步执行方法)的支持还有一些欠缺(缺少不少方便的扩展方法),在使用时会有一些需要注意的地方,由于一