【问题标题】:Determine type to map to based ob property value [duplicate]根据属性值确定要映射到的类型[重复]
【发布时间】:2018-01-15 22:17:07
【问题描述】:

通常,您将 Automapper 配置为从类型 A 映射到类型 B。我需要自动映射器映射到类型 B 或类型 C,具体取决于类型 A 中的属性值。我还需要从类型 B 或类型映射回来C 输入 A。

目前提出的解决方案是定义两种类型的映射(A => B,A => C)并根据鉴别器在自定义 if/switch 结构中调用正确的映射。映射嵌套类型,这个方案不起作用,因为这时会调用上级映射。

例如,Animal 类型根据动物的 AnimalType 值映射到猫或狗。

我还需要从猫或狗返回的方法,这应该很简单,因为在这里我可以定义 2 个固定关系(狗 => 动物,猫 => 动物)。

映射动物 => 可以定义狗或猫吗?如果有,怎么做?

    public enum EAnimalType
    {
        Cat = 1,
        Dog = 2
    }

    public class Animal
    {
        public EAnimalType AnimalType { get; set; }

        public int Age { get; set; }
    }

    public abstract class AnimalDto
    {
        public int Age { get; set; }
    }

    public class CatDto : AnimalDto {}

    public class DogDto : AnimalDto {}

【问题讨论】:

    标签: c# automapper


    【解决方案1】:

    定义基本类型的映射。此映射使用ConstructUsing 通过调用适用的子映射来实例化正确的子 DTO。使用 AutoMapper 3.0 测试。

    CreateMap<Animal, AnimalDto>()
          // create DTO by dispatching to child type mappings
          .ConstructUsing((animal, context) => {
               switch (animal.AnimalType) {
                   case EAnimalType.Dog:
                       return context.Mapper.Map<DogDto>(animal);
                   case EAnimalType.Cat:
                       return context.Mapper.Map<CatDto>(animal);
                   default:
                       throw new NotSupportedException(
                           $"Animal Type '{animal.AnimalType}' is not supported."
                       );
               }       
           })
          // map members of base type
          .ForMember(dto => dto.Age, o => o.MapFrom(ent => ent.Age));
    
          // mappings for child types, will handle dispatch from base type
          CreateMap<Animal, DogDto>()
               .ForMember(dto => dto.Age, o => o.Ignore()) // already mapped from base type 
               .ForMember(dto => dto.DogSpecific, o => o.MapFrom(dog => dog.DogSpecific));
    
          CreateMap<Animal, CatDto>()
               .ForMember(dto => dto.Age, o => o.Ignore()); // already mapped from base type
    

    为此,Animal, DogDtoAnimal, CatDto 映射必须可由 Animal, AnimalDto 配置中使用的 AutoMapper 访问(例如,在同一 AutoMapper 配置文件中定义)。

    【讨论】:

    • 谢谢。我将使用 ConstructUsing。我的单元测试也成功了。次要更改: 此: CreateMap() .ForMember(dto => dto.Age, o => o.Ignore()) 替换为: CreateMap() .IncludeBase();
    • 这真的应该记录在这里:docs.automapper.org/en/stable/…
    【解决方案2】:

    使用类型转换器,可以从 a 映射到 b 或 c。这是可行的,因为在类型转换器中我自己定义了映射指令。缺点:映射是手动定义的,必须更新,例如一个属性被添加到一个类中。

    也许存在更好的解决方案?

        [TestMethod]
        public void VerifyMappingsSecuritiesModule()
        {
            // Arrange
            Mapper.Initialize(cfg =>
                {
                    cfg.CreateMap<Animal, AnimalDto>().ConvertUsing<TypeTypeConverter>();
                });
    
            var expectedAge = 10;
            var animal = new Animal { Age = expectedAge, AnimalType = EAnimalType.Cat };
    
            // Act
            var catDto = Mapper.Map<Animal, AnimalDto>(animal);
    
            // Assert
            Assert.IsInstanceOfType(catDto, typeof(CatDto));
            Assert.AreEqual(expectedAge, catDto.Age);
        }
    
        public class TypeTypeConverter : ITypeConverter<Animal, AnimalDto>
        {
            public AnimalDto Convert(ResolutionContext context)
            {
                var animal = (Animal)context.SourceValue;
    
                if (animal.AnimalType == EAnimalType.Cat) { return new CatDto { Age = animal.Age }; }
                else { return new DogDto { Age = animal.Age };  }
            }
        }
    

    【讨论】:

    • 这里的缺点是您必须手动映射成员,而不是回退到已经定义的映射配置。我想你可以打电话给Mapper.Map&lt;DogDto&gt;(animal); 而不是new DogDto { Age = animal.Age };
    猜你喜欢
    • 2019-04-12
    • 2019-04-06
    • 1970-01-01
    • 2018-07-12
    • 2012-06-16
    • 2015-06-03
    • 2021-08-15
    • 2011-06-09
    • 2021-11-26
    相关资源
    最近更新 更多