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.htmlhtml

 

AutoMapper9.0快速上手

首先,咱們準備兩個須要用於相互轉換的類:git

1
2
3
4
5
6
7
8
9
10
11
12
     public  class  Person
     {
         public  string  Id {  get set ; }
         public  string  Name {  get set ; }
         public  Address Address {  get set ; }
     }
     public  class  Address
     {
         public  string  Province {  get set ; }
         public  string  City {  get set ; }
         public  string  Street {  get set ; }
     }
1
2
3
4
5
6
     public  class  PersonDto
     {
         public  string  Id {  get set ; }
         public  string  Name {  get set ; }
         public  string  Address {  get set ; }
     }

咱們想實現從Person到PersonDto的映射轉換。web

準備好實體模型後咱們將AutoMapper9.0經過nuget安裝到項目中:數據庫

懶得勤快的博客_互聯網分享精神

開始寫映射代碼吧,首先,咱們須要明確源和目標類型。因爲目標類型的設計通常會受其所在層的影響,好比一般狀況下咱們最終呈如今頁面上的數據結構和咱們數據庫底層設計會有所不一樣,但只要成員的名稱與源類型的成員匹配,AutoMapper就能發揮最佳效果。不知什麼時候開始的,AutoMapper能夠實現自動映射了,也就是若是須要映射的源和目標的屬性和類型長得同樣,均可以不用寫映射配置了,好比源對象裏面有一個名爲「FirstName」的成員,則會自動將其映射到目標對象的名爲「FirstName」成員上。c#

映射時,AutoMapper會忽略空引用異常。這是默認設計的。若是你以爲這樣作很差,你能夠根據須要將AutoMapper的方法與自定義值解析器結合使用。api

明確映射關係後,使用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)));

和早期版本同樣,左側的類型是源類型,右側的類型是目標類型。app

如今,映射關係作好了,咱們即可以建立映射器了:ide

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 {  get set ; }
         public  string  Name {  get set ; }
         public  Address Address {  get set ; }
     }
     public  class  Address
     {
         public  string  Id {  get set ; }
         public  string  Province {  get set ; }
         public  string  City {  get set ; }
         public  string  Street {  get set ; }
         public  string  PersonId {  get set ; }
         public  Person Person {  get set ; }
     }

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 {  get set ; }
         public  DbSet<Address> Address {  get set ; }
     }
 

遷移點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.DependencyInjecAutofac.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 {  get set ; }
         public  MapperConfiguration MapperConfig {  get set ; }
         
         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 {  get set ; }
         public  IPersonService PersonService {  get set ; }
         
         [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

相關文章
相關標籤/搜索