7

Update 1-13-10 I've been able to find some success using the code below for mapping. I am essentially ignoring any of the properties that do not have a mapping and mapping them afterwards. I would appreciate feedback as to whether or not I am going about this in the best way possible. In addition, I am not sure how to go about unit testing this mapping. It was my impression that using the AutoMapper should help alleviate tediousness of checking each property.

Here is my new code:

Mapper.CreateMap<MoveEntity, MoveEntityDto>()
           .ForMember(dest => dest.PrimaryOriginTransferee, opt => opt.Ignore())
           .ForMember(dest => dest.PrimaryDestinationTransferee, opt => opt.Ignore())
           .ForMember(dest => dest.Customer, opt => opt.Ignore())
           .ForMember(dest => dest.DestinationAddress, opt => opt.Ignore())
           .ForMember(dest => dest.OriginAddress, opt => opt.Ignore())
           .ForMember(dest => dest.Order, opt => opt.Ignore())
           .ForMember(dest => dest.Shipment, opt => opt.Ignore())
           .ForMember(dest => dest.SourceSystemName, opt => opt.Ignore());

        Mapper.CreateMap<ContactEntity, TransfereeEntityDto>();
        Mapper.CreateMap<CustomerEntity, CustomerEntityDto>();
        Mapper.CreateMap<AddressEntity, AddressEntityDto>();
        Mapper.CreateMap<OrderEntity, OrderEntityDto>()
            .ForMember(dest => dest.OrderForwarding, opt => opt.Ignore())
            .ForMember(dest => dest.Forwarder, opt => opt.Ignore());
        Mapper.CreateMap<ShipmentEntity, ShipmentEntityDto>()
            .ForMember(dest => dest.Services, opt => opt.Ignore());
        Mapper.CreateMap<ServiceEntity, ServiceEntityDto>()
            .ForMember(dest => dest.ServiceTypeCode, opt => opt.Ignore()) //TODO: ServiceTypeCode not being mapped, should it?
            .ForMember(dest => dest.SourceSystemName, opt => opt.MapFrom(src => Enum.GetName(typeof(SourceSystemName), src.SourceSystemName)));
        Mapper.CreateMap<OrderForwardingEntity, OrderForwardingEntityDto>();


        Mapper.AssertConfigurationIsValid();


        MoveEntityDto moveEntityDto = Mapper.Map<MoveEntity, MoveEntityDto>(moveEntity);
        moveEntityDto.PrimaryDestinationTransferee = Mapper.Map<ContactEntity, TransfereeEntityDto>(moveEntity.PrimaryDestinationTransferee);
        moveEntityDto.PrimaryOriginTransferee = Mapper.Map<ContactEntity, TransfereeEntityDto>(moveEntity.PrimaryOriginTransferee);
        moveEntityDto.Customer = Mapper.Map<CustomerEntity, CustomerEntityDto>(moveEntity.Customer);
        moveEntityDto.DestinationAddress = Mapper.Map<AddressEntity, AddressEntityDto>(moveEntity.DestinationAddress);
        moveEntityDto.OriginAddress = Mapper.Map<AddressEntity, AddressEntityDto>(moveEntity.OriginAddress);
        moveEntityDto.Order = Mapper.Map<OrderEntity, OrderEntityDto>(moveEntity.Order);
        moveEntityDto.Order.OrderForwarding = Mapper.Map<OrderForwardingEntity, OrderForwardingEntityDto>(moveEntity.Order.OrderForwarding);
        //moveEntityDto.Order.Forwarder = Mapper.Map<ForwarderEntity, ForwarderEntityDto>(moveEntity.Order.Forwarder);  //Apparently there is no forwarder entity for an Order
        moveEntityDto.Shipment = Mapper.Map<ShipmentEntity, ShipmentEntityDto>(moveEntity.Shipment);
        moveEntityDto.Shipment.Services = Mapper.Map<ServiceEntity[], ServiceEntityDto[]>(moveEntity.Shipment.ServiceEntities);

Original Post:

I'm attempting to use AutoMapper for the first time in order to map from a Bussiness Object to a DTO. I am running into issues that I do not know how to troubleshoot, including the following exception:

AutoMapper.AutoMapperMappingException: Trying to map Graebel.SP.BO.MoveEntity to Graebel.SOA.Contracts.DataContracts.SP.MoveEntity. Exception of type 'AutoMapper.AutoMapperMappingException' was thrown

Here is the AutoMapper Code that I am running:

public MoveEntityDto MapMoveEntityToMoveEntityDto(MoveEntity moveEntity)
    {
        Mapper.CreateMap<MoveEntity, MoveEntityDto>()
            .ForMember(dest => dest.PrimaryOriginTransferee, opt => opt.MapFrom(src => src.PrimaryOriginTransferee))
            .ForMember(dest => dest.PrimaryDestinationTransferee,opt => opt.MapFrom(src => src.PrimaryDestinationTransferee))
            .ForMember(dest => dest.Customer, opt => opt.MapFrom(src => src.Customer))
            .ForMember(dest => dest.DestinationAddress, opt => opt.MapFrom(src => src.DestinationAddress))
            .ForMember(dest => dest.Order, opt => opt.MapFrom(src => src.Order))
            .ForMember(dest => dest.OriginAddress, opt => opt.MapFrom(src => src.OriginAddress))
            .ForMember(dest => dest.Shipment, opt => opt.MapFrom(src => src.Shipment))
            .ForMember(dest => dest.SourceSystemName, opt => opt.Ignore());

        Mapper.AssertConfigurationIsValid();
        MoveEntityDto moveEntityDto = Mapper.Map<MoveEntity, MoveEntityDto>(moveEntity);

        return moveEntityDto;
    }

Here is the DTO (MoveEntityDto) that I am attempting to map:

public class MoveEntityDto
{       
    public bool IsOrderDetailPageModified { get; set; }  
    public bool IsRoutingPageModified { get; set; }  
    public bool IsServicePageModified { get; set; }  
    public bool IsContentAndContainerPageModified { get; set; }   
    public string FamilyRange { get; set; }  
    public string Office { get; set; }
    public string ActivityType { get; set; }
    public string ActivitySubject { get; set; }
    public string ActivityNote { get; set; }
    public TransfereeEntity PrimaryOriginTransferee { get; set; }
    public TransfereeEntity PrimaryDestinationTransferee { get; set; }
    public CustomerEntity Customer { get; set; }
    public AddressEntity OriginAddress { get; set; }
    public AddressEntity DestinationAddress { get; set; }
    public OrderEntity Order { get; set; }
    public ShipmentEntity Shipment { get; set; }
    public string PortalId { get; set; }
    public string SourceSystemId { get; set; }
    public EnterpriseEnums.SourceSystemName SourceSystemName { get; set; }

    public MoveEntity()
    {
        PrimaryOriginTransferee = new TransfereeEntity();
        PrimaryDestinationTransferee = new TransfereeEntity();
        Customer = new CustomerEntity();
        OriginAddress = new AddressEntity();
        DestinationAddress = new AddressEntity();
        Order = new OrderEntity();
        Shipment = new ShipmentEntity();
    }

    public bool HasShipment()
    {
        if (Shipment.ExternalShipmentId > 0)
        {
            return true;
        }
        return false;
    }
 }

Here is the Business Object (MoveEntity) that I am trying to map from

 public class MoveEntity
{
    public int SourceId { get; set; }
    public int MoveId { get; set; }
    public bool IsOrderDetailPageModified { get; set; }  // TODO: Internal -  Remove from data contract
    public bool IsRoutingPageModified { get; set; }  // TODO: Internal -  Remove from data contract
    public bool IsServicePageModified { get; set; }  // TODO: Internal -  Remove from data contract
    public bool IsContentAndContainerPageModified { get; set; }  // Rmove from data contract
    public string FamilyRange { get; set; } // TODO: Is this being used?
    public string Office { get; set; }
    public string ActivityType { get; set; }
    public string ActivitySubject { get; set; }
    public string ActivityNote { get; set; }
    public ContactEntity PrimaryOriginTransferee { get; set; }
    public ContactEntity PrimaryDestinationTransferee { get; set; }
    public CustomerEntity Customer { get; set; }
    public AddressEntity OriginAddress { get; set; }
    public AddressEntity DestinationAddress { get; set; }
    public OrderEntity Order { get; set; }
    public ShipmentEntity Shipment { get; set; }
    public string CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public string ModifiedBy { get; set; }
    public DateTime ModifiedDate { get; set; }
    public string SourceSystemId { get; set; }
    public string SourceSystemName { get; set; }
    public string Version { get; set; }
    public string PortalId { get; set; }

    public MoveEntity()
    {
        PrimaryOriginTransferee = new ContactEntity
        {
            ContactTypeId = ContactEntity.ContactType.PrimaryOriginationTransferee
        };

        PrimaryDestinationTransferee = new ContactEntity
        {
            ContactTypeId = ContactEntity.ContactType.PrimaryDestinationTransferee
        };

        OriginAddress = new AddressEntity
        {
            AddressTypeId = AddressEntity.AddressType.Origination
        };

        DestinationAddress = new AddressEntity
        {
            AddressTypeId = AddressEntity.AddressType.Destination
        };

        Order = new OrderEntity();
        Customer = new CustomerEntity();
        Shipment = new ShipmentEntity();
    }

    public bool HasShipment()
    {
        if (Shipment.ShipmentId > 0)
        {
            return true;
        }
        return false;
    }
}

The properties within each class almost match up perfectly by name, but their types are different. Therefore I have attempted to perform a custom mapping using the "MapFrom" expression. However, AutoMapper doesn't seem to be able to allow me to point from one object type to another without complain.

I've also tried mapping property-to-property, with no luck. It looked something like this:

.ForMember(dest => dest.PrimaryOriginTransferee.Email, opt => opt.MapFrom(src => src.PrimaryOriginTransferee.Email))

However, when attempting this, I receive the following exeception:

must resolve to top-level member. Parameter name: lambdaExpression.

I have been finding the documentation available for AutoMapper difficult to follow. Can someone please point me in the right direction as to how to use this utility correctly?

Thanks in advance for any help!

Adam

4

3 回答 3

5

我终于让这个自己工作了。我最终使用的代码发布在下面。事实证明,以正确的顺序创建对象地图很重要。通过这件事我学到了很多战斗。

我已经将我的映射组织到一个配置文件中,我不会在这里介绍,我只想说,如果你可以在继承自 AutoMapper Profile 类的类之外使用我的示例,你会想要使用 Mapper.CreateMap 代替只是创建地图。

 private void CreateMaps()
    {

        CreateMap<ContactEntity, TransfereeEntityDto>();

        //ContactEntity Mapping
        CreateMap<ContactEntity, TransfereeEntityDto>();

        //CustomerEntity Mapping
        CreateMap<CustomerEntity, CustomerEntityDto>();

        //AddressEntity Mapping
        CreateMap<AddressEntity, AddressEntityDto>();

        //ServiceEntity Mapping
        CreateMap<ServiceEntity, ServiceEntityDto>()
          .ForMember(dto => dto.ServiceTypeCode, opt => opt.MapFrom(source => source.TypeCode))
          .ForMember(dto => dto.ServiceDescriptionCode, opt => opt.MapFrom(source => source.DescriptionCode))
          .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));


        //VehicleEntity Mapping
        CreateMap<VehicleEntity, VehicleEntityDto>()
            .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName))
            .ForMember(dto => dto.PortalId, option => option.Ignore());  //TODO: Should PortalID be mapped to anything? It is not in the entity.

        //ContentEntity Mapping
        CreateMap<ContentEntity, ContentEntityDto>()
            .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));

        //OrderForwardingEntity Mapping
        CreateMap<OrderForwardingEntity, OrderForwardingEntityDto>();

        //ContainerEntity Mapping
        CreateMap<ContainerEntity, ContainerEntityDto>()
            .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));

        //ShipmentForwardingEntity Mapping
        CreateMap<ShipmentForwardingEntity, ShipmentForwardingEntityDto>();


        //ShipmentRouting Mapping
        CreateMap<ShipmentRoutingEntity, ShipmentRoutingEntityDto>();

        //ShipmentEntity Mapping
        CreateMap<ShipmentEntity, ShipmentEntityDto>()
            .ForMember(dest => dest.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName))
            .ForMember(dto => dto.Services, option => option.MapFrom(source => source.ServiceEntities));

        //Forwarder mapping
        CreateMap<ContactEntity, ForwarderEntityDto>();
        //TODO: This property doesn't have any properties in the data contract

        //OrderEntity Mapping
        CreateMap<OrderEntity, OrderEntityDto>()
            .ForMember(dest => dest.SourceSystemName,
                       opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));
            //.ForMember(dto => dto.Forwarder, option => option.MapFrom(entity=>entity.Forwarder)

        //MoveEntityMapping
        CreateMap<MoveEntity, MoveEntityDto>()
            .ForMember(dto => dto.SourceSystemName, opt => opt.ResolveUsing<SourceSystemNameResolver>().FromMember(entity => entity.SourceSystemName));

    }
于 2010-01-13T19:18:52.370 回答
5

我知道你已经完成了这项工作,但我要把它扔掉,以防其他人降落在这里。

在 AutoMapper 中,当您有需要映射的嵌套对象时,即使它们完全相同(即匹配的契约类和模型类),您必须为子类定义映射,然后在定义映射时对于父级,在“.ForMember”选项中,您可以使用这些子映射来映射父级。我知道这听起来可能令人困惑,但举个例子就清楚了。

假设您有以下内容:

namespace Contracts.Entities
{
    public class Person
    {
        public string FirstName {get; set;}

        public string LastName {get; set;}

        public Address Address {get; set;}        
    }

    public class Address
    {
        public string Street {get; set;}

        public string City {get; set;}

        public string State {get; set;}        
    }
}

namespace Model.Entities
{
    public class Person
    {
        public string FirstName {get; set;}

        public string LastName {get; set;}

        public Address Address {get; set;}        
    }

    public class Address
    {
        public string Street {get; set;}

        public string City {get; set;}

        public string State {get; set;}        
    }
}

然后你去定义以下地图:

  Mapper.CreateMap<Contracts.Entities.Person, Model.Entities.Person>();
  Mapper.CreateMap<Contracts.Entities.Address, Model.Entities.Address>();

您可能认为 AutoMapper 在将合同人员映射到模型人员时会知道使用地址映射,但事实并非如此。相反,这是你必须做的:

      Mapper.CreateMap<Contracts.Entities.Person, Model.Entities.Person>()
                    .ForMember(dest => dest.Address, opt => opt.MapFrom(src => Mapper.Map<Contracts.Entities.Address, Model.Entities.Address>(src.Address)));

 Mapper.CreateMap<Contracts.Entities.Address, Model.Entities.Address>();

因此,在您的情况下,您可以定义一个Mapper.CreateMap<ContactEntity,TransfereeEntity>()地图,然后在为 PrimaryOriginTransferee 定义地图时以与上述地址相同的方式调用该地图。IE

 Mapper.CreateMap<MoveEntity, MoveEntityDto>()
 .ForMember(dest => dest.PrimaryOriginTransferee , opt => opt.MapFrom(src => Mapper.Map<ContactEntity,TransfereeEntity>(src.PrimaryOriginTransferee )));

希望这对某人有帮助!

于 2013-04-19T13:42:46.927 回答
1

您需要为目标属性类型与目标属性类型不同的属性类型添加映射配置。

    Mapper.CreateMap<ContactEntity, TransfereeEntity>();
于 2010-01-13T04:15:45.400 回答