zoukankan      html  css  js  c++  java
  • 使用AutoMapper

    一、AutoMapper初探

    [参考Using AutoMapper: Getting Started]

    1.新建空的ASP.NET MVC项目

    2.在Models文件夹添加类

        public class Book
        {
            public string Title { get; set; }
        }
        public class BookViewModel
        {
            public string Title { get; set; }
        }

    3.安装AtuoMapper

    Install-Package AutoMapper

    4.在App_Start文件夹添加配置类

        public static class AutoMapperConfig
        {
            public static void RegisterMappings()
            {
                AutoMapper.Mapper.CreateMap<Book, BookViewModel>();
            }
        }

    5.在Global.asax中注册

    AutoMapperConfig.RegisterMappings();

    6.新建HomeController

            public string Index()
            {
                var book = new Book{Title="水浒传" };
                var bookModel = AutoMapper.Mapper.Map<Book>(book);
                return bookModel.Title;
            }

    7.运行程序

      

    二、创建映射

    [参考Using AutoMapper: Creating Mappings]

    1.CreateMap方法

      AutoMapper所有映射都是使用CreateMap方法定义的:

    AutoMapper.Mapper.CreateMap<SourceClass, DestinationClass>();

      需要注意的是,上面定义的映射是单向映射。例如,我们定义了如下映射:

    AutoMapper.Mapper.CreateMap<Book, BookViewModel>();

      我们可以将一个Book实例映射到一个BookViewModel实例:

    var bookViewModel = AutoMapper.Mapper.Map<BookViewModel>(book);

      但是我们不能将一个BookViewModel实例映射到一个Book实例:

    var book = AutoMapper.Mapper.Map<Book>(bookViewModel);

      如果要实现双向映射,需要再次调用CreateMap方法定义:

    AutoMapper.Mapper.CreateMap<Book, BookViewModel>();
    AutoMapper.Mapper.CreateMap<BookViewModel, Book>();

      如果我们不需要为反向映射定义任何自定义映射逻辑,我们可以使用ReverseMap实现双向映射:

    AutoMapper.Mapper.CreateMap<Book, BookViewModel>().ReverseMap();

    2.映射规则

      AutoMapper有一些映射约定,其中一个约定就是同名映射。例如:

    public class Book
    {
        public string Title { get; set; }
    }
    
    public class NiceBookViewModel
    {
        public string Title { get; set; }
    }
    
    public class BadBookViewModel
    {
        public string BookTitle { get; set; }
    }

      如果我们将一个Book实例映射到一个NiceBookViewModel实例,Title属性的值会是我们所期望的值。然而如果我们将一个Book实例映射到一个BadBookViewModel实例,Title属性的值会为null。因为名字不同,AutoMapper无从知晓BookTitle需要从Title获取值。这种情况下就需要我们手动添加配置代码:

    AutoMapper.Mapper.CreateMap<Book, BadBookViewModel>()
        .ForMember(dest => dest.BookTitle,
                   opts => opts.MapFrom(src => src.Title));

      另一个约定涉及到嵌入对象。例如:

    public class Author
    {
        public string Name { get; set; }
    }
    
    public class Book
    {
        public string Title { get; set; }
        public Author Author { get; set; }
    }
    
    public class BookViewModel
    {
        public string Title { get; set; }
        public string Author { get; set; }
    }

      尽管Book和BookViewModel都有Author属性,但是它们的类型不匹配,因此AutoMapper不能将Book.Author映射到BookViewModel.Author。对于嵌入对象,AutoMapper的约定是目标类属性的命名为骆驼拼写法的“对象名+对象属性名”。示例如下:

    public class BookViewModel
    {
        public string Title { get; set; }
        public string AuthorName { get; set; }
    }

      如果不采用约定,我们依然可以使用配置代码:

    AutoMapper.Mapper.CreateMap<Book, BookViewModel>()
        .ForMember(dest => dest.Author,
                   opts => opts.MapFrom(src => src.Author.Name));

    3.投影

      在之前的示例中,如果我们把Author类修改为:

    public class Author
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }

      我们需要将这两个属性映射到一个属性,代码如下:

    AutoMapper.Mapper.CreateMap<Book, BookViewModel>()
        .ForMember(dest => dest.Author,
                   opts => opts.MapFrom(
                       src => string.Format("{0} {1}",
                           src.Author.FirstName,
                           src.Author.LastName)));

    4.更复杂的投影

    public class Address
    {
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string ZipCode { get; set; }
    }
    
    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Address Address { get; set; }
    }
    
    public class PersonDTO
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string ZipCode { get; set; }
    }

      如果我们要把PersonDTO映射到Person,代码如下:

    AutoMapper.Mapper.CreateMap<PersonDTO, Person>()
        .ForMember(dest => dest.Address,
                   opts => opts.MapFrom(
                       src => new Address
                       {
                           Street = src.Street,
                           City = src.City,
                           State = src.State,
                           ZipCode = src.ZipCode
                       }));

    5.嵌套映射

      上面的示例中,如果PersonDTO修改为:

    public class AddressDTO
    {
        public string Street { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string ZipCode { get; set; }
    }
    
    public class PersonDTO
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public AddressDTO Address { get; set; }
    } 

      映射应该修改为:

    AutoMapper.Mapper.CreateMap<PersonDTO, Person>();
    AutoMapper.Mapper.CreateMap<AddressDTO, Address>();

    三、映射到实例

    [参考Using AutoMapper: Mapping Instances]

    1.映射到新的实例

      之前的示例均产生一个新的实例,例如:

    var destinationObject = AutoMapper.Mapper.Map<DestinatationClass>(sourceObject);

    2.映射到已经存在的实例

    AutoMapper.Mapper.Map(sourceObject, destinationObject);

    3.映射到集合

    var destinationList = AutoMapper.Mapper.Map<List<DestinationClass>>(sourceList);

      我们可以映射到所有的集合类型和接口:List<T>、ICollection<T>、IEnumerable<T>等。

      但是如果我们尝试映射到现有的实例:

    AutoMapper.Mapper.Map(sourceList, destinationList);

      我们会发现destinationList已经被损坏。因为AutoMapper实际上是映射到集合而不是分别映射到集合中的对象。当我们考虑对象的层级结构时,这种情况就变得十分讨厌。例如:

    public class Pet
    {
        public string Name { get; set; }
        public string Breed { get; set; }
    }
    
    public class Person
    {
        public List<Pet> Pets { get; set; }
    }
    
    public class PetDTO
    {
        public string Name { get; set; }
        public string Breed { get; set; }
    }
    
    public class PersonDTO
    {
        public List<PetDTO> Pets { get; set; }
    }

      如果我们创建一个只更新Name属性的表单,我们提交的对象图会是这样:

    {
        Pets: [
            { Name : "Sparky", Breed : null },
            { Name : "Felix", Breed : null },
            { Name : "Cujo", Breed : null }
        ]
    }

      由于Breed属性没有被传递,它在每个PetDTO中的值为null。现在如果我们使用PersonDTO更新Person:

    AutoMapper.Mapper.Map(person, personDTO);

      这样Person中每个Pet的Breed属性均变为null。这种问题解决起来有点麻烦,首先我们需要使用Ignore方法:

    AutoMapper.Mapper.CreateMap<PersonDTO, Person>()
        .ForMember(dest => dest.Pets,
                   opts => opts.Ignore());

      上面的代码告诉AutoMapper不映射Pets集合,这就意味着我们现在必须手动处理:

    AutoMapper.Mapper.Map(person, personDTO);
    for (int i = 0; i < person.Pets.Count(); i++)
    {
        AutoMapper.Mapper.Map(person.Pets[i], personDTO.Pets[i]);
    }

      上面的代码是基于假设两个list是相同的即我们没有对它们重新排序,并且不允许在表单添加或者删除项。为了解决重新排序后不一致的问题,我们需要依赖某些标识属性。例如:

    var pet = person.Pets[i];
    var updatedPet = personDTO.Pets.Single(m => m.Id == pet.Id);
    AutoMapper.Mapper.Map(pet, updatedPet);

      添加或者删除项会更复杂一些:

    var updatedPets = new List<Pet>();
    foreach (var pet in personDTO.Pets)
    {
        var existingPet = person.Pets.SingleOrDefault(m => m.Id == pet.Id);
        // No existing pet with this id, so add a new one
        if (existingPet == null)
        {
            updatedPets.Add(AutoMapper.Mapper.Map<Pet>(pet));
        }
        // Existing pet found, so map to existing instance
        else
        {
            AutoMapper.Mapper.Map(existingPet, pet);
            updatedPets.Add(existingPet);
        }
    }
    // Set pets to updated list (any removed items drop out naturally)
    person.Pets = updatedPets;

      这可能是使用AutoMapper最大的难点,但是只要我们在执行更新操作的时候记得手动映射list项就行了。

  • 相关阅读:
    【bzoj1408】 Noi2002—Robot
    【bzoj3884】 上帝与集合的正确用法
    【bzoj2190】 SDOI2008—仪仗队
    【uoj264】 NOIP2016—蚯蚓
    【uoj262】 NOIP2016—换教室
    【uoj261】 NOIP2016—天天爱跑步
    python sort 和sorted排序
    mkdir: cannot create directory ‘/soft/hadoop-2.7.3/logs’: Permission denied问题
    RuntimeError: implement_array_function method already has a docstring
    flask 的orm
  • 原文地址:https://www.cnblogs.com/walden1024/p/4613198.html
Copyright © 2011-2022 走看看